diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..76d27bbf --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,121 @@ +name: Release + +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+" + +permissions: + contents: "write" + +jobs: + tests: + name: Release tests + uses: ./.github/workflows/tests.yml + + build: + name: Build client + runs-on: ubuntu-latest + needs: tests + steps: + - name: Setup node + uses: actions/setup-node@v6 + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Cache node modules + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Install dependencies + run: npm ci + + - name: Replace textdomains + run: find src -exec sed -i "s/http-bridge/forms-bridge/g" {} \; + + - name: Build + run: npm run build + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: build + path: forms-bridge/assets/plugin.bundle.js + + package: + name: Prepare package + runs-on: ubuntu-latest + container: + image: codeccoop/wp-cli + env: + ZIP: "${{ github.ref_name }}.zip" + needs: build + steps: + - name: Setup node + uses: actions/setup-node@v6 + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: build + path: forms-bridge/assets + + - name: Install dist archive + run: wp package install wp-cli/dist-archive-command:@stable + + - name: Replace textdomains + run: | + find forms-bridge/deps/plugin -name '*.php' -exec sed -i "s/wpct-plugin/forms-bridge/g" {} \; + find forms-bridge -name '*.php' -exec sed -i "s/wpct_plugin_/forms_bridge_plugin_/g" {} \; + find forms-bridge -name "*.php" -exec sed -i 's/WPCT_PLUGIN/FORMS_BRIDGE\\Plugin/g' {} \; + find forms-bridge/deps/http -name '*.php' -exec sed -i "s/http-bridge/forms-bridge/g" {} \; + find forms-bridge -name '*.php' -exec sed -i "s/http_bridge_/forms_bridge_http_/g" {} \; + find forms-bridge -name "*.php" -exec sed -i 's/HTTP_BRIDGE_/FORMS_BRIDGE_HTTP_/g' {} \; + find forms-bridge -name '*.php' -exec sed -i 's/HTTP_BRIDGE/FORMS_BRIDGE\\Http/g' {} \; + + - name: Archive + run: wp dist-archive forms-bridge ./"$ZIP" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: plugin-archive + path: ${{ env.ZIP }} + + release: + name: Release pushed tag + runs-on: ubuntu-latest + needs: package + env: + ZIP: "${{ github.ref_name }}.zip" + steps: + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: plugin-archive + + - name: Create release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.ref_name }} + run: | + mv $ZIP forms-bridge.zip + gh release create "$TAG" \ + --repo="$GITHUB_REPOSITORY" \ + --title="$TAG" \ + --generate-notes \ + "forms-bridge.zip" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..855f6ad7 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,66 @@ +name: Tests + +on: + pull_request: + branches: [main] + + workflow_call: + +jobs: + lint: + name: WordPress Coding Standards + runs-on: ubuntu-latest + container: + image: codeccoop/wp-test + steps: + - uses: actions/checkout@v5 + + - name: Composer install + run: composer -q install + + - name: Lint and format + run: vendor/bin/phpcbf + + phpunit: + name: PHP Unit Testing + runs-on: ubuntu-latest + container: + image: codeccoop/wp-test + steps: + - name: Start MariaDB + run: nohup docker-entrypoint.sh mariadbd 2>&1 >/dev/null & + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Cache composer + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('composer.lock') }} + + - name: Cache plugins + uses: actions/cache@v4 + with: + path: /tmp/*.zip + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('bin/download-test-deps.sh') }} + + - name: Composer install + run: composer -q install + + - name: Wait for MariaDB + run: sleep 10 + + - name: Executable mode + run: chmod a+x bin/* + + - name: Install tests suite + run: bin/install-wp-tests.sh + + - name: Download deps + run: bin/download-test-deps.sh + + - name: Unit testing + run: vendor/bin/phpunit -c phpunit.xml.dist diff --git a/.gitignore b/.gitignore index fcc60c69..bf2399bd 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .DS_Store phpcs.xml phpunit.xml +.phpunit.result.cache +.php-cs-fixer.cache Thumbs.db wp-cli.local.yml node_modules/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 064c8006..334a6d86 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,20 +1,6 @@ -workflow: - rules: - - if: $CI_COMMIT_BRANCH - changes: - - README.md - - .gitlab-ci.yml - when: - never - # Do no allow manually triggered pipelines to prevent duplicates! - # Instead rerun the pipeline created with the last push - - if: $CI_PIPELINE_SOURCE != "push" - when: never - # Only execute when a valid version tag like 1.0, 2.3-dev or similar is given - # Required is always one point like 1.0 - - if: $CI_COMMIT_TAG =~ /^[0-9]+[.][0-9]+([.][0-9]+)?([-a-z])*$/ - -stages: # List of stages for jobs, and their order of execution +# List of stages for jobs, and their order of execution +stages: + - test - build - package - upload @@ -32,49 +18,113 @@ variables: GIT_SUBMODULE_STRATEGY: normal GIT_SUBMODULE_FORCE_HTTPS: "true" +.release_job: + rules: + # Only execute when a valid version tag like 1.0, 2.3-dev or similar is given + - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_TAG =~ /^[0-9]+[.][0-9]+([.][0-9]+)?([-a-z])*$/ + +.test_job: + stage: test + image: codeccoop/wp-test + cache: + - key: + files: + - bin/download-test-deps.sh + paths: + - /tmp/*.zip + + - key: + files: + - composer.lock + paths: + - vendor + rules: + # Only execute when a valid version tag like 1.0, 2.3-dev or similar is given + - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_TAG =~ /^[0-9]+[.][0-9]+([.][0-9]+)?([-a-z])*$/ + # Run always on MR pointing to master + - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH + +lint: + extends: .test_job + before_script: + - composer -q install + script: + - vendor/bin/phpcbf + +phpunit: + extends: .test_job + before_script: + - nohup docker-entrypoint.sh mariadbd 2>&1 >/dev/null & + - composer -q install + - sleep 10 + - chmod a+x bin/* + - bin/install-wp-tests.sh + - bin/download-test-deps.sh + script: + - vendor/bin/phpunit -c phpunit.xml.dist + build: + extends: .release_job stage: build image: node:latest + cache: + - key: + files: + - package-lock.json + paths: + - node_modules script: - find src -exec sed -i "s/http-bridge/forms-bridge/g" {} \; - npm install && npm run build - - rm -rf node_modules artifacts: paths: - - assets/plugin.bundle.js + - forms-bridge/assets/plugin.bundle.js package: + extends: .release_job image: codeccoop/wp-cli:latest stage: package + dependencies: [build] script: - - find common -name '*.php' -exec sed -i "s/wpct-plugin/forms-bridge/g" {} \; - - find . -name '*.php' -exec sed -i "s/wpct_plugin_/forms_bridge_plugin_/g" {} \; - - find . -name "*.php" -exec sed -i 's/WPCT_PLUGIN/FORMS_BRIDGE\\Common/g' {} \; - - find deps/http -name '*.php' -exec sed -i "s/http-bridge/forms-bridge/g" {} \; - - find . -name '*.php' -exec sed -i "s/http_bridge_/forms_bridge_http_/g" {} \; - - find . -name "*.php" -exec sed -i 's/HTTP_BRIDGE_/FORMS_BRIDGE_HTTP_/g' {} \; - - find . -name '*.php' -exec sed -i 's/HTTP_BRIDGE/FORMS_BRIDGE\\Http/g' {} \; - - find deps/i18n -name '*.php' -exec sed -i "s/wpct-i18n/forms-bridge/g" {} \; - - find . -name '*.php' -exec sed -i "s/wpct_i18n_/forms_bridge_i18n_/g" {} \; - - find . -name '*.php' -exec sed -i 's/WPCT_I18N/FORMS_BRIDGE\\I18n/g' {} \; - - wp dist-archive . ./$ZIP + - find forms-bridge/deps/plugin -name '*.php' -exec sed -i "s/wpct-plugin/forms-bridge/g" {} \; + - find forms-bridge -name '*.php' -exec sed -i "s/wpct_plugin_/forms_bridge_plugin_/g" {} \; + - find forms-bridge -name "*.php" -exec sed -i 's/WPCT_PLUGIN/FORMS_BRIDGE\\Plugin/g' {} \; + - find forms-bridge/deps/http -name '*.php' -exec sed -i "s/http-bridge/forms-bridge/g" {} \; + - find forms-bridge -name '*.php' -exec sed -i "s/http_bridge_/forms_bridge_http_/g" {} \; + - find forms-bridge -name "*.php" -exec sed -i 's/HTTP_BRIDGE_/FORMS_BRIDGE_HTTP_/g' {} \; + - find forms-bridge -name '*.php' -exec sed -i 's/HTTP_BRIDGE/FORMS_BRIDGE\\Http/g' {} \; + - wp dist-archive forms-bridge ./$ZIP artifacts: paths: - $ZIP upload: + extends: .release_job stage: upload image: curlimages/curl:latest + dependencies: [package] script: - | - curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file ${ZIP} ${PACKAGE_REGISTRY_URL}/${ZIP} + curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \ + --upload-file ${ZIP} \ + ${PACKAGE_REGISTRY_URL}/${ZIP} release: # Caution, as of 2021-02-02 these assets links require a login, see: # https://gitlab.com/gitlab-org/gitlab/-/issues/299384 + extends: .release_job stage: release - image: registry.gitlab.com/gitlab-org/release-cli:latest + image: gitlab/glab:latest + dependencies: [package] + variables: + GITLAB_HOST: "$CI_SERVER_URL" script: - | - release-cli create --name "Release $CI_COMMIT_TAG" --tag-name $CI_COMMIT_TAG \ - --assets-link "{\"name\":\"${ZIP}\",\"url\":\"${PACKAGE_REGISTRY_URL}\/${ZIP}\",\"link_type\":\"package\",\"filepath\":\"\/plugins\/bridges\/${ZIP}\"}" + glab auth login \ + --job-token $CI_JOB_TOKEN \ + --hostname $CI_SERVER_HOST \ + --api-protocol $CI_SERVER_PROTOCOL + - | + glab release create $CI_COMMIT_TAG\ + --name "Release $CI_COMMIT_TAG" \ + --assets-links "[{\"name\":\"${ZIP}\",\"url\":\"${PACKAGE_REGISTRY_URL}\/${ZIP}\",\"link_type\":\"package\",\"direct_asset_path\":\"\/plugins\/bridges\/${ZIP}\"}]" diff --git a/.gitmodules b/.gitmodules index b27df078..910eabce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ -[submodule "common"] - path = common - url = https://git.coopdevs.org/codeccoop/wp/plugins/wpct-plugin-common.git -[submodule "deps/http"] - path = deps/http - url = https://git.coopdevs.org/codeccoop/wp/plugins/bridges/http-bridge.git -[submodule "deps/i18n"] - path = deps/i18n - url = https://git.coopdevs.org/codeccoop/wp/plugins/wpct-i18n.git +[submodule "forms-bridge/plugin"] + path = forms-bridge/deps/plugin + url = https://gitlab.com/codeccoop/wp/plugins/wpct-plugin.git +[submodule "forms-bridge/deps/http"] + path = forms-bridge/deps/http + url = https://gitlab.com/codeccoop/wp/plugins/http-bridge.git diff --git a/.lintstagedrc.js b/.lintstagedrc.js index f44e52aa..50ae20c1 100644 --- a/.lintstagedrc.js +++ b/.lintstagedrc.js @@ -8,13 +8,13 @@ const buildPrettierCommand = (filenames) => .map((f) => path.relative(process.cwd(), f)) .join(" ")}`; -const buildPrettierPhpCommand = (filenames) => - `prettier --write --ignore-unknown --config .prettierrc-php ${filenames +const buildPhpCbfCommand = (filenames) => + `vendor/bin/phpcbf -n ${filenames .map((f) => path.relative(process.cwd(), f)) .join(" ")}`; module.exports = { - // "src/**/*.{js,jsx,ts,tsx}": [buildEslintCommand], - "*.{json,js,ts,jsx,tsx,html,css}": [buildPrettierCommand], - "*.php": [buildPrettierPhpCommand], + "src/**/*.{js,jsx}": [buildEslintCommand], + "*.{json,js,jsx,html,css}": [buildPrettierCommand], + "*.php": [buildPhpCbfCommand], }; diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index df58068d..49283633 100755 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -3,40 +3,46 @@ Generally-applicable sniffs for WordPress plugins. - . - /vendor/ - /node_modules/ + ./forms-bridge + ./tests - + - + - - + + + + - + + + + - + + + diff --git a/.prettierignore b/.prettierignore index 2c60343b..8d371cf5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1,12 @@ +# js node_modules package.json package-lock.json +# php +composer.json +composer.lock + # config *.config.js .lintstagedrc.js diff --git a/.prettierrc-php b/.prettierrc-php deleted file mode 100644 index 70f4fa35..00000000 --- a/.prettierrc-php +++ /dev/null @@ -1,10 +0,0 @@ -{ - "parser": "php", - "plugins": ["@prettier/plugin-php"], - "tabWidth": 4, - "endOfLine": "lf", - "useTabs": false, - "singleQuote": true, - "printWidth": 80, - "htmlWhitespaceSensitivity": "strict" -} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5f605e62..2bc75549 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,39 +2,74 @@ ## Purpose -Our open-source software project is committed to fostering a cooperative, solidary, welcoming, and collaborative environment for everyone, irrespective of their background. This Code of Conduct applies to everyone who engages with our project on any level, providing guidance to all participants to maintain a safe, positive, and mutually supportive community. +Our open-source software project is committed to fostering a cooperative, +solidary, welcoming, and collaborative environment for everyone, irrespective of +their background. This Code of Conduct applies to everyone who engages with our +project on any level, providing guidance to all participants to maintain a safe, +positive, and mutually supportive community. ## Values -1. **Cooperation:** We believe in the power of working together. We encourage team efforts, collaborations, and the pooling of resources and knowledge. We understand that our collective intelligence surpasses individual capacities. +1. **Cooperation:** We believe in the power of working together. We encourage + team efforts, collaborations, and the pooling of resources and knowledge. We + understand that our collective intelligence surpasses individual capacities. -2. **Solidarity:** We stand together and support each other. We believe that the success of one is the success of all, and we're committed to helping each other in times of need. +2. **Solidarity:** We stand together and support each other. We believe that the + success of one is the success of all, and we're committed to helping each other + in times of need. ## Expected Behavior -1. **Respect:** Treat all community members with kindness and respect. Remember that everyone is contributing their time and expertise to improve the project. -2. **Patience:** Demonstrate patience and understanding towards others, especially when discussing complex or controversial issues. -3. **Constructive Criticism:** Provide feedback that is constructive and helpful. This includes being open to receiving such feedback. -4. **Openness:** All trascendental technical debates should take place in the project's repository issues, ensuring everyone has the chance to contribute to the discussion. -5. **Inclusivity:** Promote an inclusive and supportive environment. Every contribution is important and should be recognized. -6. **Transparency:** *If it is not in the repository, it has not happened*, or, in other words, the single source of truth is the repository and its issues. Document everything, including, e.g, writting down discussions that have been held elsewhere. +1. **Respect:** Treat all community members with kindness and respect. Remember + that everyone is contributing their time and expertise to improve the project. +2. **Patience:** Demonstrate patience and understanding towards others, + especially when discussing complex or controversial issues. +3. **Constructive Criticism:** Provide feedback that is constructive and helpful. + This includes being open to receiving such feedback. +4. **Openness:** All trascendental technical debates should take place in the + project's repository issues, ensuring everyone has the chance to contribute to + the discussion. +5. **Inclusivity:** Promote an inclusive and supportive environment. Every + contribution is important and should be recognized. +6. **Transparency:** _If it is not in the repository, it has not happened_, or, + in other words, the single source of truth is the repository and its issues. + Document everything, including, e.g, writting down discussions that have been + held elsewhere. ## Unacceptable Behavior -1. **Personal Attacks:** Any form of personal attacks, trolling, or insulting/derogatory comments are not tolerated. -2. **Harassment:** This includes, but is not limited to, harassment based on race, gender, sexual orientation, disability, age, or religion. -3. **Disruptive Behavior:** Any disruptive behavior that derails technical discussions or demeans others' contributions is unacceptable. -4. **Public or Private Harassment:** Harassment in any form, either online or offline, direct or indirect, is not allowed. -5. **Other Unethical or Unprofessional Conduct:** Any other conduct which could reasonably be considered inappropriate in a cooperative professional setting. +1. **Personal Attacks:** Any form of personal attacks, trolling, or + insulting/derogatory comments are not tolerated. +2. **Harassment:** This includes, but is not limited to, harassment based on race, + gender, sexual orientation, disability, age, or religion. +3. **Disruptive Behavior:** Any disruptive behavior that derails technical + discussions or demeans others' contributions is unacceptable. +4. **Public or Private Harassment:** Harassment in any form, either online or + offline, direct or indirect, is not allowed. +5. **Other Unethical or Unprofessional Conduct:** Any other conduct which could + reasonably be considered inappropriate in a cooperative professional setting. ## Reporting and Enforcement -Violations of this Code of Conduct will result in actions aimed at facilitating conflict resolution and repairing any harm caused. These measures will be adapted according to the evolution of the situation and the process. Actions could range from mediation between involved parties to temporary or permanent restrictions from contributing. If you witness or experience any violations, please report them by sending an email to cures@coopdevs.org. +Violations of this Code of Conduct will result in actions aimed at facilitating +conflict resolution and repairing any harm caused. These measures will be adapted +according to the evolution of the situation and the process. Actions could range +from mediation between involved parties to temporary or permanent restrictions from +contributing. If you witness or experience any violations, please report them by +sending an email to . -Our project maintainers will review and investigate all reports, and then take action that is deemed necessary and appropriate based on the progress and nature of the process. We are committed to ensuring that all our community members feel safe and respected, and we appreciate your help in maintaining this environment. +Our project maintainers will review and investigate all reports, and then take +action that is deemed necessary and appropriate based on the progress and nature +of the process. We are committed to ensuring that all our community members feel +safe and respected, and we appreciate your help in maintaining this environment. --- -This Code of Conduct is a living document, and we are committed to it for the project's health and community. We will continuously review and update it as our community grows and learns. +This Code of Conduct is a living document, and we are committed to it for the +project's health and community. We will continuously review and update it as our +community grows and learns. -We believe in the quality and potential of everyone who contributes to our project. Adhering to this Code of Conduct helps ensure that our community is welcoming, inclusive, cooperative, solidary, and respectful to all. We expect everyone to help make this a place where everyone feels safe and welcomed. +We believe in the quality and potential of everyone who contributes to our +project. Adhering to this Code of Conduct helps ensure that our community is +welcoming, inclusive, cooperative, solidary, and respectful to all. We expect +everyone to help make this a place where everyone feels safe and welcomed. diff --git a/license.txt b/LICENSE similarity index 100% rename from license.txt rename to LICENSE diff --git a/README.md b/README.md index 1f5c40ed..77d9997c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,25 @@ # Forms Bridge -Bridge your WordPress forms without code, add custom fields, use field mappers, set up a workflow and make your data flow seamlessly to your backend. +[![Plugin version](https://img.shields.io/wordpress/plugin/v/forms-bridge)](https://wordpress.org/plugins/forms-bridge/) +![GitHub Actions Tests Workflow Status](https://img.shields.io/github/actions/workflow/status/codeccoop/forms-bridge/tests.yml?label=tests) + +Bridge your WordPress forms without code, add custom fields, use field mappers, +set up a workflow and make your data flow seamlessly to your backend. ## Bridges -Think of a bridge as a pipeline through which your form submissions data flows to your backend or service. In the middle, you can add custom fields to the form submissions, use field mappers to rename and mutate your form responses, or use workflow jobs to process the data before it is sent over the wire. With bridges you can connect your WordPress forms to any kind of backend, it doesn't matter if it is a CRM, an ERP, a booking system or an email marketing platform, the only requirement is an HTTP API. If it has an API it can be bridged! +Think of a bridge as a pipeline through which your form submissions data flows +to your backend or service. In the middle, you can add custom fields to the form +submissions, use field mappers to rename and mutate your form responses, or use +workflow jobs to process the data before it is sent over the wire. With bridges you +can connect your WordPress forms to any kind of backend, it doesn't matter if it +is a CRM, an ERP, a booking system or an email marketing platform, the only requirement +is an HTTP API. If it has an API it can be bridged! ## Form builders -Form builders are well known plugins that add forms to WordPress. We do bridges, let them do the forms and then work together to make your business work with ease. +Form builders are well known plugins that add forms to WordPress. We do bridges, +let them do the forms and then work together to make your business work with ease. Forms Bridge supports the following form builders: @@ -20,7 +31,8 @@ Forms Bridge supports the following form builders: ## Addons -Forms Bridge comes with free addons. Each addon adds to the plugin new bridges to work with specific APIs, new workflow jobs and bridge templates. +Forms Bridge comes with free addons. Each addon adds to the plugin new bridges +to work with specific APIs, new workflow jobs and bridge templates. Forms Bridge has the following addons: @@ -39,7 +51,9 @@ Forms Bridge has the following addons: ## Backends -In Forms Bridge, a backend is a set of configurations that handles the information required to get your form submissions bridged over HTTP requests to remote systems. +In Forms Bridge, a backend is a set of configurations that handles the +information required to get your form submissions bridged over HTTP requests +to remote systems. To register a new backend you only have to set 3 fields: @@ -52,15 +66,21 @@ Once registered, you can reuse your backend connection on your form bridges. ## Custom fields -Custom fields are data that will be added the bridge payload. Use them to store private data you don’t want to place on your public forms, like user emails, or config values, like product IDs or lead tags. +Custom fields are data that will be added the bridge payload. Use them to store +private data you don’t want to place on your public forms, like user emails, or +config values, like product IDs or lead tags. ## Field mappers -Field mappers are mutations with which you can rename your form submission fields and transform its values. Use them to make your form submissions to fit your backend API endpoint interface. +Field mappers are mutations with which you can rename your form submission +fields and transform its values. Use them to make your form submissions to +fit your backend API endpoint interface. ## Workflows -Make your form submissions flow through a chain of jobs that pre-process the data before it was sent over the wire. Think of workflow as a system to set up automations to run on each form submission. +Make your form submissions flow through a chain of jobs that pre-process the +data before it was sent over the wire. Think of workflow as a system to set up +automations to run on each form submission. ## Templates @@ -73,15 +93,54 @@ Browse the plugin's documentation on [formsbridge.codeccoop.org](https://formsbr ## Links - [Official website](https://formsbridge.codeccoop.org/) -- [Gitlab](https://git.coopdevs.org/codeccoop/wp/plugins/bridges/forms-bridge/) +- [GitHub](https://github.com/codeccoop/forms-bridge/) - [Còdec](https://www.codeccoop.org) - [Other plugins](https://profiles.wordpress.org/codeccoop/#content-plugins) -## Dependencies +## Development + +### API + +The plugin offers some hooks to expose its internal API. Go to +[documentation](https://formsbridge.codeccoop.org/documentation/#api) to see +more details about the hooks. + +### Dependencies + +The repository handles dependencies as [git submodules](https://www.atlassian.com/git/tutorials/git-submodule). +In order to work local, you have to clone this repository and initialize its submodules +with this command: + +``` +git submodule sync +git submodule update --init +``` + +Once done, install JS dependenices with `npm install` and PHP dependencies with +`composer install`. + +### Build + +Frontend builds are made with [esbuild](https://esbuild.github.io/). Once you +have your JS dependencies installed you can run `npm run dev` to perform +a live build, or `npm run build` to get a production build. + +### Lint and format + +For JavaScript the project uses [prettier](https://prettier.io/) as a formatter +[eslint](https://eslint.org/) as the linter. + +For PHP the project uses [phpcs](https://github.com/squizlabs/PHP_CodeSniffer) +as the linter and formatter. + +Lint and format will be applied to staged files before each commit. In addition, +merge requests performs a lint test in order to be accepted. + +### Tests + +To run the projects test you have to execute the script `bin/install-wp-tests.sh` +in order to get the WordPress test suit installed in your local machine. Once done, +run `composer run test` to run project's unit tests. -This plugin relays on [Http Bridge](https://gitlab.com/codeccoop/wp/plugins/http-bridge/) -and [Wpct i18n](https://gitlab.coom/codeccoop/wp/plugins/wpct-i18n/) as depenendencies, -as well as the [Wpct Plugin Common](https://gitlab.com/codeccoop/wp/plugins/wpct-plugin-common) -snippets. The plugin comes with its dependencies bundled in its releases, so you should -not worry about its managment. You can see this plugins documentation to know more about -its APIs. +If you have docker on your local machine, you can run tests in an ephemeral environment +with the script `bin/test-on-docker.sh`. diff --git a/addons/bigin/api.php b/addons/bigin/api.php deleted file mode 100644 index 306bc319..00000000 --- a/addons/bigin/api.php +++ /dev/null @@ -1,112 +0,0 @@ - $payload['Last_Name'], - ]; - - $contact_fields = [ - 'Owner', - 'Full_Name', - 'First_Name', - 'Email', - 'Phone', - 'Mobile', - 'Title', - 'Account_Name', - 'Description', - 'Mailing_Street', - 'Mailing_City', - 'Mailing_Zip', - 'Mailing_State', - 'Mailing_Country', - ]; - - foreach ($contact_fields as $field) { - if (isset($payload[$field])) { - $contact[$field] = $payload[$field]; - } - } - - if ( - isset($payload['Account_Name']) && - is_string($payload['Account_Name']) - ) { - $account = forms_bridge_bigin_create_account($payload, $bridge); - - if (is_wp_error($account)) { - return $account; - } - - $payload['Account_Name'] = ['id' => $account['id']]; - } - - $response = $bridge - ->patch([ - 'name' => 'zoho-bigin-create-contact', - 'scope' => 'ZohoBigin.modules.contacts.CREATE', - 'endpoint' => '/bigin/v2/Contacts/upsert', - 'template' => null, - ]) - ->submit($contact); - - if (is_wp_error($response)) { - return $response; - } - - $code = $response['data']['data'][0]['code'] ?? null; - if ($code === 'DUPLICATE_DATA') { - return $response['data']['data'][0]['details']['duplicate_record']; - } else { - return $response['data']['data'][0]['details']; - } -} - -function forms_bridge_bigin_create_account($payload, $bridge) -{ - $company = [ - 'Account_Name' => $payload['Account_Name'], - ]; - - $company_fields = [ - 'Owner', - 'Phone', - 'Website', - 'Billing_Street', - 'Billing_Code', - 'Billing_City', - 'Billing_State', - 'Billing_Country', - 'Description', - ]; - - foreach ($company_fields as $field) { - if (isset($payload[$field])) { - $company[$field] = $payload[$field]; - } - } - - $response = $bridge - ->patch([ - 'name' => 'zoho-bigin-create-account', - 'scope' => 'ZohoBigin.modules.accounts.CREATE', - 'endpoint' => '/bigin/v2/Accounts/upsert', - ]) - ->submit($company); - - if (is_wp_error($response)) { - return $response; - } - - $code = $response['data']['data'][0]['code'] ?? null; - if ($code === 'DUPLICATE_DATA') { - return $response['data']['data'][0]['details']['duplicate_record']; - } else { - return $response['data']['data'][0]['details']; - } -} diff --git a/addons/bigin/class-bigin-addon.php b/addons/bigin/class-bigin-addon.php deleted file mode 100644 index f2b762a1..00000000 --- a/addons/bigin/class-bigin-addon.php +++ /dev/null @@ -1,158 +0,0 @@ - '__bigin-' . time(), - 'backend' => $backend, - 'endpoint' => '/bigin/v2/users', - 'method' => 'GET', - ]); - - $backend = $bridge->backend; - if (!$backend) { - return false; - } - - $credential = $backend->credential; - if (!$credential) { - return false; - } - - $parsed = wp_parse_url($backend->base_url); - $host = $parsed['host'] ?? ''; - - if ( - !preg_match( - '/www\.zohoapis\.(\w{2,3}(\.\w{2})?)$/', - $host, - $matches - ) - ) { - return false; - } - - $region = $matches[1]; - if (!preg_match('/' . $region . '$/', $credential->region)) { - return false; - } - - $response = $bridge->submit(['type' => 'CurrentUser']); - return !is_wp_error($response); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array List of fields and content type of the endpoint. - */ - public function get_endpoint_schema($endpoint, $backend) - { - if ( - !preg_match( - '/\/(([A-Z][a-z]+(_[A-Z][a-z])?)(?:\/upsert)?$)/', - $endpoint, - $matches - ) - ) { - return []; - } - - $module = $matches[2]; - - $bridge_class = self::bridge_class; - $bridge = new $bridge_class([ - 'name' => '__bigin-' . time(), - 'backend' => $backend, - 'endpoint' => '/bigin/v2/settings/layouts', - 'method' => 'GET', - ]); - - $response = $bridge->submit(['module' => $module]); - - if (is_wp_error($response)) { - return []; - } - - $fields = []; - foreach ($response['data']['layouts'] as $layout) { - foreach ($layout['sections'] as $section) { - foreach ($section['fields'] as $field) { - $type = $field['json_type']; - if ($type === 'jsonobject') { - $type = 'object'; - } elseif ($type === 'jsonarray') { - $type = 'array'; - } elseif ($type === 'double') { - $type = 'number'; - } - - $fields[] = [ - 'name' => $field['api_name'], - 'schema' => ['type' => $type], - ]; - } - } - } - - return $fields; - } -} - -Bigin_Addon::setup(); diff --git a/addons/bigin/class-bigin-form-bridge.php b/addons/bigin/class-bigin-form-bridge.php deleted file mode 100644 index 41f9d219..00000000 --- a/addons/bigin/class-bigin-form-bridge.php +++ /dev/null @@ -1,25 +0,0 @@ - [ - [ - 'ref' => '#credential', - 'name' => 'scope', - 'value' => - 'ZohoBigin.modules.ALL,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ', - ], - ], - 'credential' => [ - 'scope' => - 'ZohoBigin.modules.ALL,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ', - ], - ], - $defaults, - $schema - ); - }, - 20, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'bigin-') !== 0) { - return $data; - } - - return apply_filters( - 'forms_bridge_template_data', - $data, - 'zoho-' . $template_id - ); - }, - 10, - 2 -); diff --git a/addons/bigin/jobs/account-name.php b/addons/bigin/jobs/account-name.php deleted file mode 100644 index 657046ed..00000000 --- a/addons/bigin/jobs/account-name.php +++ /dev/null @@ -1,91 +0,0 @@ - $account['id'], - ]; - - return $payload; -} - -return [ - 'title' => __('Account name', 'forms-bridge'), - 'description' => __( - 'Search for an account by name or creates a new if it does\'t exists and replace the name by the ID on the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_zoho_bigin_account_name', - 'input' => [ - [ - 'name' => 'Account_Name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'Owner', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'string'], - ], - 'additionalProperties' => false, - 'required' => ['id'], - ], - ], - [ - 'name' => 'Phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Website', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_Street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_Code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_City', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_State', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_Country', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Description', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'Account_Name', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - ], -]; diff --git a/addons/bigin/jobs/appointment-participant.php b/addons/bigin/jobs/appointment-participant.php deleted file mode 100644 index 8f204c1d..00000000 --- a/addons/bigin/jobs/appointment-participant.php +++ /dev/null @@ -1,105 +0,0 @@ - 'contact', - 'participant' => $contact['id'], - ]; - - return $payload; -} - -return [ - 'title' => __('Appointment participant', 'forms-bridge'), - 'description' => __( - 'Search for a contact or creates a new one and sets its ID as appointment participant', - 'forms-bridge' - ), - 'method' => 'forms_bridge_bigin_appointment_participant', - 'input' => [ - [ - 'name' => 'Last_Name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'First_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Full_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_Street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_City', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_Zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_State', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_Country', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Description', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Account_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Title', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'Participants', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'type' => ['type' => 'string'], - 'participant' => ['type' => 'string'], - ], - ], - 'additionalItems' => true, - ], - ], - ], -]; diff --git a/addons/bigin/jobs/contact-name.php b/addons/bigin/jobs/contact-name.php deleted file mode 100644 index 9de435fc..00000000 --- a/addons/bigin/jobs/contact-name.php +++ /dev/null @@ -1,90 +0,0 @@ - $contact['id'], - ]; - - return $payload; -} - -return [ - 'title' => __('Contact name', 'forms-bridge'), - 'description' => __( - 'Search for a contact by email or creates a new if it does\'t exists and replace the name by the ID on the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_bigin_contact_name', - 'input' => [ - [ - 'name' => 'Email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'First_Name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'Phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Title', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Account_Name', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - [ - 'name' => 'Description', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'Contact_Name', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - [ - 'name' => 'Account_Name', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - ], -]; diff --git a/addons/bigin/jobs/event-dates.php b/addons/bigin/jobs/event-dates.php deleted file mode 100644 index ea755fb6..00000000 --- a/addons/bigin/jobs/event-dates.php +++ /dev/null @@ -1,55 +0,0 @@ -getTimestamp(); - - $payload['Start_DateTime'] = date('c', $timestamp); - $payload['End_DateTime'] = date('c', $timestamp + 3600 * $duration); - - return $payload; -} - -return [ - 'title' => __('Appointment dates', 'forms-bridge'), - 'description' => __( - 'Sets appointment start and end time from "date" and "duration" fields', - 'forms-bridge' - ), - 'method' => 'forms_bridge_bigin_appointment_dates', - 'input' => [ - [ - 'name' => 'date', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'duration', - 'schema' => ['type' => 'number'], - ], - ], - 'output' => [ - [ - 'name' => 'Start_DateTime', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'End_DateTime', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/bigin/templates/companies.php b/addons/bigin/templates/companies.php deleted file mode 100644 index 18261aa9..00000000 --- a/addons/bigin/templates/companies.php +++ /dev/null @@ -1,115 +0,0 @@ - __('Company Contact', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into contacts linked to company accounts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/bigin/v2/Contacts/upsert', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the account', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/bigin/v2/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Companies', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'Account_Name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Billing_Street', - 'label' => __('Street', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_Code', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_City', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_State', - 'label' => __('State', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_Country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Title', - 'label' => __('Title', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/bigin/v2/Contacts/upsert', - 'workflow' => ['account-name'], - ], -]; diff --git a/addons/bigin/templates/contacts.php b/addons/bigin/templates/contacts.php deleted file mode 100644 index 1bca3a46..00000000 --- a/addons/bigin/templates/contacts.php +++ /dev/null @@ -1,77 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/bigin/v2/Contacts/upsert', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner ID', 'forms-bridge'), - 'description' => __( - 'ID of the owner user of the contact', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/bigin/v2/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Contacts', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/bigin/v2/Contacts/upsert', - ], -]; diff --git a/addons/bigin/templates/deals.php b/addons/bigin/templates/deals.php deleted file mode 100644 index 17f8ba38..00000000 --- a/addons/bigin/templates/deals.php +++ /dev/null @@ -1,188 +0,0 @@ - __('Deals', 'forms-bridge'), - 'description' => __( - 'Leads form templates. The resulting bridge will convert form submissions into deals on the sales pipeline linked new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/bigin/v2/Pipelines', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'descritpion' => __( - 'Email of the owner user of the deal', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/bigin/v2/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Deal_Name', - 'label' => __('Deal name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Stage', - 'label' => __('Deal stage', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'value' => 'Qualification', - 'label' => __('Qualification', 'forms-bridge'), - ], - [ - 'value' => 'Needs Analysis', - 'label' => __('Needs Analysis', 'forms-bridge'), - ], - [ - 'value' => 'Proposal/Price Quote', - 'label' => __('Proposal/Price Quote', 'forms-bridge'), - ], - [ - 'value' => 'Negotation/Review', - 'label' => __('Negotiation/Review', 'forms-bridge'), - ], - [ - 'value' => 'Closed Won', - 'label' => __('Closed Won', 'forms-bridge'), - ], - [ - 'value' => 'Closed Lost', - 'label' => __('Closed Lost', 'forms-bridge'), - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Sub_Pipeline', - 'label' => __('Pipeline name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Amount', - 'label' => __('Deal amount', 'forms-bridge'), - 'type' => 'number', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Tag', - 'label' => __('Deal tags', 'forms-bridge'), - 'description' => __( - 'Tag names separated by commas', - 'forms-bridge' - ), - 'type' => 'text', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Deals', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'Account_Name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Billing_Street', - 'label' => __('Street', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_Code', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_City', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_State', - 'label' => __('State', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Billing_Country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Title', - 'label' => __('Title', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/bigin/v2/Pipelines', - 'workflow' => ['account-name', 'contact-name'], - 'mutations' => [ - [ - [ - 'from' => 'Amount', - 'to' => 'Amount', - 'cast' => 'number', - ], - ], - ], - ], -]; diff --git a/addons/bigin/templates/meetings.php b/addons/bigin/templates/meetings.php deleted file mode 100644 index 3f57f46c..00000000 --- a/addons/bigin/templates/meetings.php +++ /dev/null @@ -1,254 +0,0 @@ - __('Meetings', 'forms-bridge'), - 'description' => __( - 'Meetings form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/bigin/v2/Events', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'descritpion' => __( - 'Email of the owner user of the event', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/bigin/v2/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Event_Title', - 'label' => __('Event title', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web Meeting', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'All_day', - 'label' => __('Is all day event?', 'forms-bridge'), - 'type' => 'boolean', - 'default' => false, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'duration', - 'label' => __('Duration', 'forms-bridge'), - 'description' => __('Duration in hours', 'forms-bridge'), - 'type' => 'number', - 'default' => '1', - 'min' => 0, - 'max' => 24, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Meetings', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'date', - 'label' => __('Date', 'forms-bridge'), - 'type' => 'date', - 'required' => true, - ], - [ - 'name' => 'hour', - 'label' => __('Hour', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('1 AM', 'forms-bridge'), - 'value' => '01', - ], - [ - 'label' => __('2 AM', 'forms-bridge'), - 'value' => '02', - ], - [ - 'label' => __('3 AM', 'forms-bridge'), - 'value' => '03', - ], - [ - 'label' => __('4 AM', 'forms-bridge'), - 'value' => '04', - ], - [ - 'label' => __('5 AM', 'forms-bridge'), - 'value' => '05', - ], - [ - 'label' => __('6 AM', 'forms-bridge'), - 'value' => '06', - ], - [ - 'label' => __('7 AM', 'forms-bridge'), - 'value' => '07', - ], - [ - 'label' => __('8 AM', 'forms-bridge'), - 'value' => '08', - ], - [ - 'label' => __('9 AM', 'forms-bridge'), - 'value' => '09', - ], - [ - 'label' => __('10 AM', 'forms-bridge'), - 'value' => '10', - ], - [ - 'label' => __('11 AM', 'forms-bridge'), - 'value' => '11', - ], - [ - 'label' => __('12 AM', 'forms-bridge'), - 'value' => '12', - ], - [ - 'label' => __('1 PM', 'forms-bridge'), - 'value' => '13', - ], - [ - 'label' => __('2 PM', 'forms-bridge'), - 'value' => '14', - ], - [ - 'label' => __('3 PM', 'forms-bridge'), - 'value' => '15', - ], - [ - 'label' => __('4 PM', 'forms-bridge'), - 'value' => '16', - ], - [ - 'label' => __('5 PM', 'forms-bridge'), - 'value' => '17', - ], - [ - 'label' => __('6 PM', 'forms-bridge'), - 'value' => '18', - ], - [ - 'label' => __('7 PM', 'forms-bridge'), - 'value' => '19', - ], - [ - 'label' => __('8 PM', 'forms-bridge'), - 'value' => '20', - ], - [ - 'label' => __('9 PM', 'forms-bridge'), - 'value' => '21', - ], - [ - 'label' => __('10 PM', 'forms-bridge'), - 'value' => '22', - ], - [ - 'label' => __('11 PM', 'forms-bridge'), - 'value' => '23', - ], - [ - 'label' => __('12 PM', 'forms-bridge'), - 'value' => '24', - ], - ], - 'required' => 'true', - ], - [ - 'name' => 'minute', - 'label' => __('Minute', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - ['label' => '00', 'value' => '00.0'], - ['label' => '05', 'value' => '05'], - ['label' => '10', 'value' => '10'], - ['label' => '15', 'value' => '15'], - ['label' => '20', 'value' => '20'], - ['label' => '25', 'value' => '25'], - ['label' => '30', 'value' => '30'], - ['label' => '35', 'value' => '35'], - ['label' => '40', 'value' => '40'], - ['label' => '45', 'value' => '45'], - ['label' => '50', 'value' => '50'], - ['label' => '55', 'value' => '55'], - ], - 'required' => true, - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/bigin/v2/Events', - 'mutations' => [ - [ - [ - 'from' => '?All_day', - 'to' => 'All_day', - 'cast' => 'boolean', - ], - ], - [ - [ - 'from' => 'datetime', - 'to' => 'date', - 'cast' => 'string', - ], - ], - ], - 'workflow' => [ - 'date-fields-to-date', - 'event-dates', - 'appointment-participant', - ], - ], -]; diff --git a/addons/bigin/templates/woo-contacts.php b/addons/bigin/templates/woo-contacts.php deleted file mode 100644 index 38f602fe..00000000 --- a/addons/bigin/templates/woo-contacts.php +++ /dev/null @@ -1,349 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert woocommerce customers into contacts.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/bigin/v2/Contacts/upsert', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner ID', 'forms-bridge'), - 'description' => __( - 'ID of the owner user of the contact', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/bigin/v2/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/bigin/v2/Contacts/upsert', - 'workflow' => ['account-name'], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => '?Owner', - 'to' => 'Contact_Owner', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.company', - 'to' => 'Account_Name', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'Billing_Street', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.city', - 'to' => 'Billing_City', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.state', - 'to' => 'Billing_State', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.country', - 'to' => 'Billing_Country', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'Billing_Code', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.first_name', - 'to' => 'First_Name', - 'cast' => 'string', - ], - [ - 'from' => 'billing.last_name', - 'to' => 'Last_Name', - 'cast' => 'string', - ], - [ - 'from' => 'billing.email', - 'to' => 'Email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'Phone', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'Mailing_Street', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'Mailing_City', - 'cast' => 'string', - ], - [ - 'from' => '?billing.state', - 'to' => 'Mailing_State', - 'cast' => 'string', - ], - [ - 'from' => '?billing.country', - 'to' => 'Mailing_Country', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'Mailing_Zip', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'Contact_Owner', - 'to' => 'Owner', - 'cast' => 'inherit', - ], - ], - ], - ], -]; diff --git a/addons/brevo/api.php b/addons/brevo/api.php deleted file mode 100644 index 75fa093e..00000000 --- a/addons/brevo/api.php +++ /dev/null @@ -1,76 +0,0 @@ - $payload['name'], - ]; - - $company_fields = [ - 'attributes', - 'countryCode', - 'linkedContactsIds', - 'linkedDealsIds', - ]; - - foreach ($company_fields as $field) { - if (isset($payload[$field])) { - $company[$field] = $payload[$field]; - } - } - - $response = $bridge - ->patch([ - 'name' => 'brevo-create-company', - 'endpoint' => '/v3/companies', - 'method' => 'POST', - ]) - ->submit($company); - - if (is_wp_error($response)) { - return $response; - } - - return $response['data']; -} - -function forms_bridge_brevo_create_contact($payload, $bridge) -{ - $contact = [ - 'email' => $payload['email'], - ]; - - $contact_fields = [ - 'ext_id', - 'attributes', - 'emailBlacklisted', - 'smsBlacklisted', - 'listIds', - 'updateEnabled', - 'smtpBlacklistSender', - ]; - - foreach ($contact_fields as $field) { - if (isset($payload[$field])) { - $contact[$field] = $payload[$field]; - } - } - - $response = $bridge - ->patch([ - 'name' => 'brevo-create-contact', - 'endpoint' => '/v3/contacts', - 'method' => 'POST', - ]) - ->submit($contact); - - if (is_wp_error($response)) { - return $response; - } - - return $response['data']; -} diff --git a/addons/brevo/class-brevo-addon.php b/addons/brevo/class-brevo-addon.php deleted file mode 100644 index 2187f2b8..00000000 --- a/addons/brevo/class-brevo-addon.php +++ /dev/null @@ -1,313 +0,0 @@ - '__brevo-' . time(), - 'endpoint' => '/v3/contacts/lists', - 'method' => 'GET', - 'backend' => $backend, - ]); - - $response = $bridge->submit(['limit' => 1]); - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge = new Brevo_Form_Bridge([ - 'name' => '__brevo-' . time(), - 'endpoint' => $endpoint, - 'backend' => $backend, - 'method' => 'GET', - ]); - - return $bridge->submit(); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array - */ - public function get_endpoint_schema($endpoint, $backend) - { - $bridge = new Brevo_Form_Bridge([ - 'name' => '__brevo-' . time(), - 'endpoint' => $endpoint, - 'backend' => $backend, - 'method' => 'GET', - ]); - - if (strstr($bridge->endpoint, 'contacts')) { - $response = $bridge - ->patch([ - 'name' => 'brevo-contacts-attributes', - 'endpoint' => '/v3/contacts/attributes', - 'method' => 'GET', - ]) - ->submit(); - - if (is_wp_error($response)) { - return []; - } - - if ($bridge->endpoint === '/v3/contacts/doubleOptinConfirmation') { - $fields = [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'includeListIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - ], - 'required' => true, - ], - [ - 'name' => 'excludeListIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - ], - ], - [ - 'name' => 'templateId', - 'schema' => ['type' => 'integer'], - 'required' => true, - ], - [ - 'name' => 'redirectionUrl', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'attributes', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - ], - ], - ]; - } else { - $fields = [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'ext_id', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'emailBlacklisted', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'smsBlacklisted', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'listIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - ], - ], - [ - 'name' => 'updateEnabled', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'smtpBlacklistSender', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'attributes', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - ], - ], - ]; - } - - foreach ($response['data']['attributes'] as $attribute) { - $fields[] = [ - 'name' => 'attributes.' . $attribute['name'], - 'schema' => ['type' => 'string'], - ]; - } - - return $fields; - } else { - if (!preg_match('/\/([a-z]+)$/', $bridge->endpoint, $matches)) { - return []; - } - - $module = $matches[1]; - $response = $bridge - ->patch([ - 'name' => "brevo-{$module}-attributes", - 'endpoint' => "/v3/crm/attributes/{$module}", - 'method' => 'GET', - ]) - ->submit(); - - if (is_wp_error($response)) { - return []; - } - - if ($module === 'companies') { - $fields = [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'countryCode', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'linkedContactsIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - ], - ], - [ - 'name' => 'linkedDealsIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - ], - ], - [ - 'name' => 'attributes', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - ], - ], - ]; - } elseif ($module === 'deals') { - $fields = [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'linkedDealsIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - ], - ], - [ - 'name' => 'linkedCompaniesIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - ], - ], - [ - 'name' => 'attributes', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - ], - ], - ]; - } - - foreach ($response['data'] as $attribute) { - switch ($attribute['attributeTypeName']) { - case 'number': - $type = 'number'; - break; - case 'text': - $type = 'string'; - break; - case 'user': - $type = 'email'; - break; - case 'date': - $type = 'date'; - break; - default: - $type = 'string'; - } - - $fields[] = [ - 'name' => 'attributes.' . $attribute['internalName'], - 'schema' => ['type' => $type], - ]; - } - - return $fields; - } - } -} - -Brevo_Addon::setup(); diff --git a/addons/brevo/class-brevo-form-bridge.php b/addons/brevo/class-brevo-form-bridge.php deleted file mode 100644 index a4f1c4f1..00000000 --- a/addons/brevo/class-brevo-form-bridge.php +++ /dev/null @@ -1,71 +0,0 @@ -get_error_data()['response']; - if ( - $error_response['response']['code'] !== 425 && - $error_response['response']['code'] !== 400 - ) { - return $response; - } - - $data = json_decode($error_response['body'], true); - if ($data['code'] !== 'duplicate_parameter') { - return $response; - } - - if ( - !isset($payload['email']) || - strstr($this->endpoint, '/v3/contacts') === false - ) { - return $response; - } - - $update_response = $this->patch([ - 'name' => 'brevo-update-contact-by-email', - 'endpoint' => "/v3/contacts/{$payload['email']}?identifierType=email_id", - 'method' => 'PUT', - ])->submit($payload); - - if (is_wp_error($update_response)) { - return $update_response; - } - - return $this->patch([ - 'name' => 'brevo-search-contact-by-email', - 'endpoint' => "/v3/contacts/{$payload['email']}", - 'method' => 'GET', - ])->submit(['identifierType' => 'email_id']); - } - - return $response; - } -} diff --git a/addons/brevo/hooks.php b/addons/brevo/hooks.php deleted file mode 100644 index d0c41d1c..00000000 --- a/addons/brevo/hooks.php +++ /dev/null @@ -1,166 +0,0 @@ - [ - [ - 'ref' => '#backend', - 'name' => 'name', - 'description' => __( - 'Label of the Brevo API backend connection', - 'forms-bridge' - ), - 'default' => 'Brevo API', - ], - [ - 'ref' => '#backend', - 'name' => 'base_url', - 'value' => 'https://api.brevo.com', - ], - [ - 'ref' => '#backend/headers[]', - 'name' => 'api-key', - 'label' => __('API Key', 'forms-bridge'), - 'description' => __( - 'Get it from your account', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - ], - 'bridge' => [ - 'method' => 'POST', - ], - 'backend' => [ - 'base_url' => 'https://api.brevo.com', - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'brevo-') !== 0) { - return $data; - } - - $get_index = fn($name) => array_search( - $name, - array_column($data['bridge']['custom_fields'], 'name') - ); - - $index = array_search( - 'listIds', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = $data['bridge']['custom_fields'][$index]; - - for ($i = 0; $i < count($field['value']); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "listIds[{$i}]", - 'value' => $field['value'][$i], - ]; - - $data['bridge']['mutations'][0][] = [ - 'from' => "listIds[{$i}]", - 'to' => "listIds[{$i}]", - 'cast' => 'integer', - ]; - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'includeListIds', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = $data['bridge']['custom_fields'][$index]; - - for ($i = 0; $i < count($field['value']); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "includeListIds[{$i}]", - 'value' => $field['value'][$i], - ]; - - $data['bridge']['mutations'][0][] = [ - 'from' => "includeListIds[{$i}]", - 'to' => "includeListIds[{$i}]", - 'cast' => 'integer', - ]; - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'redirectionUrl', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = &$data['bridge']['custom_fields'][$index]; - - $field['value'] = (string) filter_var( - (string) $field['value'], - FILTER_SANITIZE_URL - ); - - $parsed = parse_url($field['value']); - - if (!isset($parsed['host'])) { - $site_url = get_site_url(); - - $field['value'] = - $site_url . - '/' . - preg_replace('/^\/+/', '', $field['value']); - } - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/brevo/jobs/country-phone-code.php b/addons/brevo/jobs/country-phone-code.php deleted file mode 100644 index 5acb9111..00000000 --- a/addons/brevo/jobs/country-phone-code.php +++ /dev/null @@ -1,59 +0,0 @@ - __('Country phone code', 'forms-bridge'), - 'description' => __( - 'Get a country by name and adds its phone prefix as the "countryCode" field on the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_brevo_country_phone_prefix', - 'input' => [ - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'countryCode', - 'schema' => ['type' => 'integer'], - ], - ], -]; - -function forms_bridge_brevo_country_phone_prefix($payload) -{ - global $forms_bridge_country_phone_codes; - global $forms_bridge_iso2_countries; - global $forms_bridge_iso3_countries; - - $countries = []; - foreach ($forms_bridge_country_phone_codes as $phone_code => $country) { - $countries[$country] = $phone_code; - } - - $country = $payload['country']; - - if (isset($forms_bridge_iso2_countries[$country])) { - $country = $forms_bridge_iso2_countries[$country]; - } elseif (isset($forms_bridge_iso3_countries[$country])) { - $country = $forms_bridge_iso3_countries[$country]; - } - - if (isset($countries[$country])) { - $payload['countryCode'] = (int) $countries[$country]; - } else { - $payload['countryCode'] = null; - } - - return $payload; -} diff --git a/addons/brevo/jobs/linked-company.php b/addons/brevo/jobs/linked-company.php deleted file mode 100644 index 8f196a81..00000000 --- a/addons/brevo/jobs/linked-company.php +++ /dev/null @@ -1,64 +0,0 @@ - __('Linked company', 'forms-bridge'), - 'description' => __( - 'Creates a new company and inserts its ID in the linkedCompaniesIds array field of the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_brevo_linked_company', - 'input' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'attributes', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - 'additionalProperties' => true, - ], - ], - [ - 'name' => 'countryCode', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'linkedContactsIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - 'additionalItems' => true, - ], - ], - ], - 'output' => [ - [ - 'name' => 'linkedCompaniesIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_brevo_linked_company($payload, $bridge) -{ - $company = forms_bridge_brevo_create_company($payload, $bridge); - - if (is_wp_error($company)) { - return $company; - } - - $payload['linkedCompaniesIds'][] = $company['id']; - - return $payload; -} diff --git a/addons/brevo/jobs/linked-contact.php b/addons/brevo/jobs/linked-contact.php deleted file mode 100644 index 10c64a99..00000000 --- a/addons/brevo/jobs/linked-contact.php +++ /dev/null @@ -1,80 +0,0 @@ - __('Linked contact', 'forms-bridge'), - 'description' => __( - 'Creates a new contact and inserts its ID in the linkedContactsIds array field of the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_brevo_linked_contact', - 'input' => [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'ext_id', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'attributes', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - 'additionalProperties' => true, - ], - ], - [ - 'name' => 'emailBlacklisted', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'smsBlacklisted', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'listIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - 'additionalItems' => true, - ], - ], - [ - 'name' => 'updateEnabled', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'smtpBlacklistSender', - 'schema' => ['type' => 'boolean'], - ], - ], - 'output' => [ - [ - 'name' => 'linkedContactsIds', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_brevo_linked_contact($payload, $bridge) -{ - $contact = forms_bridge_brevo_create_contact($payload, $bridge); - - if (is_wp_error($contact)) { - return $contact; - } - - $payload['linkedContactsIds'][] = $contact['id']; - - return $payload; -} diff --git a/addons/brevo/jobs/skip-subscription.php b/addons/brevo/jobs/skip-subscription.php deleted file mode 100644 index addba554..00000000 --- a/addons/brevo/jobs/skip-subscription.php +++ /dev/null @@ -1,31 +0,0 @@ - __('Skip subscription', 'forms-bridge'), - 'description' => __( - 'Skip subscription if the brevo field is not true', - 'forms-bridge' - ), - 'method' => 'forms_bridge_brevo_skip_subscription', - 'input' => [ - [ - 'name' => 'brevo', - 'schema' => ['type' => 'boolean'], - 'required' => true, - ], - ], - 'output' => [], -]; - -function forms_bridge_brevo_skip_subscription($payload) -{ - if ($payload['brevo'] != true) { - return; - } - - return $payload; -} diff --git a/addons/brevo/jobs/sync-woo-products.php b/addons/brevo/jobs/sync-woo-products.php deleted file mode 100644 index c757dfd0..00000000 --- a/addons/brevo/jobs/sync-woo-products.php +++ /dev/null @@ -1,138 +0,0 @@ - __('Sync woo products', 'forms-bridge'), - 'description' => __( - 'Synchronize WooCommerce orders products with the eCommerce module of Brevo', - 'forms-bridge' - ), - 'method' => 'forms_bridge_brevo_sync_woo_products', - 'input' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - 'required' => ['id', 'name', 'price'], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_brevo_sync_woo_products($payload, $bridge) -{ - $response = $bridge - ->patch([ - 'name' => 'brevo-search-products', - 'endpoint' => '/v3/products', - 'method' => 'GET', - ]) - ->submit([ - 'offset' => 0, - 'order' => 'desc', - ]); - - if (is_wp_error($response)) { - return $response; - } - - foreach ($payload['line_items'] as $line_item) { - $product = null; - foreach ($response['data']['products'] as $candidate) { - if ($candidate['id'] == $line_item['product_id']) { - $product = $candidate; - break; - } - } - - if (!$product) { - $product = [ - 'updateEnabled' => false, - 'id' => (string) $line_item['product_id'], - 'name' => $line_item['product']['name'], - 'price' => $line_item['product']['price'], - 'stock' => $line_item['product']['stock_quantity'], - ]; - - if (!empty($line_item['product']['parent_id'])) { - $product['parent_id'] = $line_item['product']['parent_id']; - } - - if (!empty($line_item['product']['sku'])) { - $product['sku'] = $line_item['product']['sku']; - } - - $product_response = $bridge - ->patch([ - 'name' => 'brevo-sync-woo-product', - 'method' => 'POST', - 'endpoint' => '/v3/products', - ]) - ->submit($product); - - if (is_wp_error($product_response)) { - return $product_response; - } - } - } - - return $payload; -} diff --git a/addons/brevo/templates/companies.php b/addons/brevo/templates/companies.php deleted file mode 100644 index 8743b201..00000000 --- a/addons/brevo/templates/companies.php +++ /dev/null @@ -1,163 +0,0 @@ - __('Companies', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into companies linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/companies', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'owner', - 'label' => __('Owner email', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the company contact', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/organization/invited/users', - 'finger' => 'users[].email', - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Companies', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Companies', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'company_name', - 'label' => __('Company', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country) { - return [ - 'value' => $country, - 'label' => $country, - ]; - }, array_values($forms_bridge_country_phone_codes)), - 'required' => true, - ], - [ - 'name' => 'phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'website', - 'label' => __('Website', 'forms-bridge'), - 'type' => 'url', - ], - [ - 'name' => 'industry', - 'label' => __('Industry', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'fname', - 'label' => __('Your first name', 'forms-bridge'), - 'type' => 'text', - 'required' => false, - ], - [ - 'name' => 'lname', - 'label' => __('Your last name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'backend' => [ - 'base_url' => 'https://api.brevo.com', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/companies', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'fname', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => 'lname', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'country', - 'cast' => 'null', - ], - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'phone', - 'to' => 'attributes.phone_number', - 'cast' => 'string', - ], - [ - 'from' => 'website', - 'to' => 'attributes.domain', - 'cast' => 'string', - ], - [ - 'from' => 'industry', - 'to' => 'attributes.industry', - 'cast' => 'string', - ], - [ - 'from' => '?owner', - 'to' => 'attributes.owner', - 'cast' => 'string', - ], - ], - ], - 'workflow' => ['linked-contact', 'country-phone-code'], - ], -]; diff --git a/addons/brevo/templates/company-deals.php b/addons/brevo/templates/company-deals.php deleted file mode 100644 index d7554d4d..00000000 --- a/addons/brevo/templates/company-deals.php +++ /dev/null @@ -1,217 +0,0 @@ - __('Company deals', 'forms-bridge'), - 'description' => __( - 'Quotation form templates. The resulting bridge will convert form submissions into deals on the sales pipeline linked new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/crm/deals', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'deal_name', - 'label' => __('Deal name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'deal_owner', - 'label' => __('Owner email', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the deal', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/organization/invited/users', - 'finger' => 'users[].email', - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'pipeline', - 'label' => __('Pipeline', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/crm/pipeline/details/all', - 'finger' => [ - 'value' => '[].pipeline', - 'label' => '[].pipeline_name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'amount', - 'label' => __('Deal amount', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company deals', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Company deals', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'company_name', - 'label' => __('Company', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country) { - return [ - 'value' => $country, - 'label' => $country, - ]; - }, array_values($forms_bridge_country_phone_codes)), - 'required' => true, - ], - [ - 'name' => 'phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'website', - 'label' => __('Website', 'forms-bridge'), - 'type' => 'url', - ], - [ - 'name' => 'industry', - 'label' => __('Industry', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'fname', - 'label' => __('Your first name', 'forms-bridge'), - 'type' => 'text', - 'required' => false, - ], - [ - 'name' => 'lname', - 'label' => __('Your last name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/crm/deals', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'fname', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => 'lname', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'country', - 'cast' => 'null', - ], - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'phone', - 'to' => 'attributes.phone_number', - 'cast' => 'string', - ], - [ - 'from' => 'website', - 'to' => 'attributes.domain', - 'cast' => 'string', - ], - [ - 'from' => 'industry', - 'to' => 'attributes.industry', - 'cast' => 'string', - ], - [ - 'from' => 'deal_owner', - 'to' => 'attributes.owner', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'deal_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?pipeline', - 'to' => 'attributes.pipeline', - 'cast' => 'string', - ], - [ - 'from' => 'deal_owner', - 'to' => 'attributes.deal_owner', - 'cast' => 'string', - ], - [ - 'from' => '?amount', - 'to' => 'attributes.amount', - 'cast' => 'number', - ], - ], - ], - 'workflow' => [ - 'linked-contact', - 'country-phone-code', - 'linked-company', - ], - ], - 'backend' => [ - 'base_url' => 'https://api.brevo.com', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], -]; diff --git a/addons/brevo/templates/contacts-doi.php b/addons/brevo/templates/contacts-doi.php deleted file mode 100644 index 17e0869f..00000000 --- a/addons/brevo/templates/contacts-doi.php +++ /dev/null @@ -1,125 +0,0 @@ - __('Contacts DOI', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions with a double opt-in confirmation check.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/contacts/doubleOptinConfirmation', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'includeListIds', - 'label' => __('Segments', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/contacts/lists', - 'finger' => [ - 'value' => 'lists[].id', - 'label' => 'lists[].name', - ], - ], - 'is_multi' => true, - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'templateId', - 'label' => __('Double opt-in template', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/smtp/templates', - 'finger' => [ - 'value' => 'templates[].id', - 'label' => 'templates[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'redirectionUrl', - 'label' => __('Redirection URL', 'forms-bridge'), - 'type' => 'text', - 'description' => __( - 'URL of the web page that user will be redirected to after clicking on the double opt in URL', - 'forms-bridge' - ), - 'required' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Contacts DOI', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Contacts DOI', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'fname', - 'label' => __('Your first name', 'forms-bridge'), - 'type' => 'text', - 'required' => false, - ], - [ - 'name' => 'lname', - 'label' => __('Your last name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'backend' => [ - 'base_url' => 'https://api.brevo.com', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/contacts/doubleOptinConfirmation', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'templateId', - 'to' => 'templateId', - 'cast' => 'integer', - ], - [ - 'from' => 'fname', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => 'lname', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - ], - ], - ], -]; diff --git a/addons/brevo/templates/contacts.php b/addons/brevo/templates/contacts.php deleted file mode 100644 index e1a44d5f..00000000 --- a/addons/brevo/templates/contacts.php +++ /dev/null @@ -1,96 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/contacts', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'listIds', - 'endpoint' => '/v3/contacts/lists', - 'label' => __('Segments', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/contacts/lists', - 'finger' => [ - 'value' => 'lists[].id', - 'label' => 'lists[].name', - ], - ], - 'is_multi' => true, - 'required' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Contacts', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Contacts', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'fname', - 'label' => __('Your first name', 'forms-bridge'), - 'type' => 'text', - 'required' => false, - ], - [ - 'name' => 'lname', - 'label' => __('Your last name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'backend' => [ - 'base_url' => 'https://api.brevo.com', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/contacts', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'fname', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => 'lname', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - ], - ], - ], -]; diff --git a/addons/brevo/templates/deals.php b/addons/brevo/templates/deals.php deleted file mode 100644 index 2a267e88..00000000 --- a/addons/brevo/templates/deals.php +++ /dev/null @@ -1,147 +0,0 @@ - __('Deals', 'forms-bridge'), - 'description' => __( - 'Leads form templates. The resulting bridge will convert form submissions into deals on the sales pipeline linked new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/crm/deals', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'deal_name', - 'label' => __('Deal name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'deal_owner', - 'label' => __('Owner email', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the deal', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/organization/invited/users', - 'finger' => 'users[].email', - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'pipeline', - 'label' => __('Pipeline', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/crm/pipeline/details/all', - 'finger' => [ - 'value' => '[].pipeline', - 'label' => '[].pipeline_name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'amount', - 'label' => __('Deal amount', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Deals', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Deals', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'fname', - 'label' => __('Your first name', 'forms-bridge'), - 'type' => 'text', - 'required' => false, - ], - [ - 'name' => 'lname', - 'label' => __('Your last name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/crm/deals', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'fname', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => 'lname', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'deal_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?pipeline', - 'to' => 'attributes.pipeline', - 'cast' => 'string', - ], - [ - 'from' => 'deal_owner', - 'to' => 'attributes.deal_owner', - 'cast' => 'string', - ], - [ - 'from' => '?amount', - 'to' => 'attributes.amount', - 'cast' => 'number', - ], - ], - ], - 'workflow' => ['linked-contact'], - ], - 'backend' => [ - 'base_url' => 'https://api.brevo.com', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], -]; diff --git a/addons/brevo/templates/woo-contacts-doi.php b/addons/brevo/templates/woo-contacts-doi.php deleted file mode 100644 index 7fca52f9..00000000 --- a/addons/brevo/templates/woo-contacts-doi.php +++ /dev/null @@ -1,305 +0,0 @@ - __('Subscription DOI', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list with a double opt in check.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/contacts/doubleOptinConfirmation', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'includeListIds', - 'label' => __('Segments', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/contacts/lists', - 'finger' => [ - 'value' => 'lists[].id', - 'label' => 'lists[].name', - ], - ], - 'is_multi' => true, - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'templateId', - 'label' => __('Double opt-in template', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/smtp/templates', - 'finger' => [ - 'value' => 'templates[].id', - 'label' => 'templates[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'redirectionUrl', - 'label' => __('Redirection URL', 'forms-bridge'), - 'type' => 'text', - 'description' => __( - 'URL of the web page that user will be redirected to after clicking on the double opt in URL', - 'forms-bridge' - ), - 'required' => true, - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/contacts/doubleOptinConfirmation', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'templateId', - 'to' => 'templateId', - 'cast' => 'integer', - ], - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'ext_id', - 'cast' => 'string', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - [ - 'from' => 'billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - ], - ], -]; diff --git a/addons/brevo/templates/woo-contacts.php b/addons/brevo/templates/woo-contacts.php deleted file mode 100644 index 91fa4c59..00000000 --- a/addons/brevo/templates/woo-contacts.php +++ /dev/null @@ -1,275 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/contacts', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'listIds', - 'label' => __('Segments', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/contacts/lists', - 'finger' => [ - 'value' => 'lists[].id', - 'label' => 'lists[].name', - ], - ], - 'is_multi' => true, - 'required' => true, - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/contacts', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'ext_id', - 'cast' => 'string', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - [ - 'from' => 'billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - ], - ], -]; diff --git a/addons/brevo/templates/woo-orders.php b/addons/brevo/templates/woo-orders.php deleted file mode 100644 index 9769456d..00000000 --- a/addons/brevo/templates/woo-orders.php +++ /dev/null @@ -1,303 +0,0 @@ - __('Woo Orders', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will synchronize WooCommerce with the Brevo eCommerce module.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/v3/orders/status', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'listIds', - 'label' => __('Segments', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/v3/contacts/lists', - 'finger' => [ - 'value' => 'lists[].id', - 'label' => 'lists[].name', - ], - ], - 'is_multi' => true, - 'required' => true, - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/v3/orders/status', - 'custom_fields' => [ - [ - 'name' => 'attributes.LANGUAGE', - 'value' => '$locale', - ], - [ - 'name' => 'createdAt', - 'value' => '$utc_date', - ], - [ - 'name' => 'updatedAt', - 'value' => '$utc_date', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'order_id', - 'cast' => 'string', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'amount', - 'cast' => 'number', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'ext_id', - 'cast' => 'string', - ], - [ - 'from' => 'ext_id', - 'to' => 'identifiers.ext_id', - 'cast' => 'copy', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'attributes.FNAME', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'attributes.LNAME', - 'cast' => 'string', - ], - [ - 'from' => 'billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'linkedContactsIds', - 'to' => 'linkedContactsIds', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'order_id', - 'to' => 'id', - 'cast' => 'string', - ], - [ - 'from' => 'line_items[].product_id', - 'to' => 'products[].productId', - 'cast' => 'string', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'products[].quantity', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'products[].price', - 'cast' => 'number', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - ], - 'workflow' => ['linked-contact', 'sync-woo-products'], - ], -]; diff --git a/addons/dolibarr/api.php b/addons/dolibarr/api.php deleted file mode 100644 index 5e0e3bbb..00000000 --- a/addons/dolibarr/api.php +++ /dev/null @@ -1,398 +0,0 @@ - fn($v) => "(t.email:=:'{$v}')", - 'firstname' => fn($v) => "(t.firstname:like:'{$v}')", - 'lastname' => fn($v) => "(t.lastname:like:'{$v}')", - // 'socid' => fn ($v) => "(t.fk_soc:=:{$v})", - ]; - - foreach ($search_fields as $field => $filter) { - if (isset($payload[$field])) { - $sqlfilters[] = $filter($payload[$field]); - } - } - - if (empty($sqlfilters)) { - return; - } - - $sqlfilters = implode(' and ', $sqlfilters); - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-search-contact', - 'endpoint' => '/api/index.php/contacts', - 'method' => 'GET', - ]) - ->submit([ - 'sortfield' => 't.rowid', - 'sortorder' => 'DESC', - 'limit' => '1', - 'properties' => 'id', - 'sqlfilters' => $sqlfilters, - ]); - - if (is_wp_error($response)) { - $error_data = $response->get_error_data(); - $response_code = $error_data['response']['response']['code']; - - if ($response_code !== 404) { - return $response; - } - } - - if (is_wp_error($response)) { - return; - } - - return $response['data'][0]; -} - -function forms_bridge_dolibarr_search_thirdparty($payload, $bridge) -{ - $sqlfilters = ["(t.nom:like:'{$payload['name']}')"]; - - $search_fields = [ - // 'typent_id' => fn ($v) => "(t.fk_typent:=:{$v})", - 'tva_intra' => fn($v) => "(t.tva_intra:=:'{$v}')", - 'idprof1' => fn($v) => "(t.siren:=:'{$v}')", - 'email' => fn($v) => "(t.email:=:'{$v}')", - 'code_client' => fn($v) => "(t.code_client:=:'{$v}')", - ]; - - foreach ($search_fields as $field => $filter) { - if (isset($payload[$field])) { - $sqlfilters[] = $filter($payload[$field]); - } - } - - if (empty($sqlfilters)) { - return; - } - - $sqlfilters = implode(' or ', $sqlfilters); - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-search-thirdparty', - 'endpoint' => '/api/index.php/thirdparties', - 'method' => 'GET', - ]) - ->submit([ - 'sortfield' => 't.rowid', - 'sortorder' => 'DESC', - 'limit' => '1', - 'properties' => 'id,code_client', - 'sqlfilters' => $sqlfilters, - ]); - - if (is_wp_error($response)) { - $error_data = $response->get_error_data(); - $response_code = $error_data['response']['response']['code'] ?? null; - - if ($response_code !== 404) { - return $response; - } - } - - if (is_wp_error($response)) { - return; - } - - return $response['data'][0]; -} - -function forms_bridge_dolibarr_get_next_code_client($payload, $bridge) -{ - $required = isset($payload['client']) && $payload['client'] != '0'; - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-get-next-code-client', - 'endpoint' => '/api/index.php/thirdparties', - 'method' => 'GET', - ]) - ->submit([ - 'sortfield' => 't.rowid', - 'sortorder' => 'DESC', - 'properties' => 'code_client', - 'limit' => 1, - ]); - - if (is_wp_error($response)) { - if (!$required) { - $payload['code_client'] = ''; - return $payload; - } - - return $response; - } - - $previous_code_client = $response['data'][0]['code_client']; - - try { - [$prefix, $number] = explode('-', $previous_code_client); - - if (empty($number)) { - $number = $prefix; - $prefix = ''; - } - - $next = strval($number + 1); - while (strlen($next) < strlen($number)) { - $next = '0' . $next; - } - } catch (Error) { - if (!$required) { - $payload['code_client'] = ''; - return $payload; - } - - return new WP_Error('unkown_code_format'); - } - - if (preg_match('/^CU[0-9]{4}$/', $prefix)) { - $prefix = 'CU' . date('y') . date('m'); - } elseif (preg_match('/^CU[0-9]{2}$/', $prefix)) { - $prefix = 'CU' . date('y'); - } - - if (empty($prefix)) { - return $next; - } - - return $prefix . '-' . $next; -} - -function forms_bridge_dolibarr_get_next_project_ref($payload, $bridge) -{ - $response = $bridge - ->patch([ - 'name' => 'dolibar-get-next-project-ref', - 'endpoint' => '/api/index.php/projects', - 'method' => 'GET', - ]) - ->submit([ - 'sortfield' => 't.rowid', - 'sortorder' => 'DESC', - 'properties' => 'ref', - 'limit' => 1, - ]); - - if (is_wp_error($response)) { - return $response; - } - - $previous_project_ref = $response['data'][0]['ref']; - - [$prefix, $number] = explode('-', $previous_project_ref); - - $next = strval($number + 1); - while (strlen($next) < strlen($number)) { - $next = '0' . $next; - } - - $prefix = 'PJ' . date('y') . date('m'); - return $prefix . '-' . $next; -} - -function forms_bridge_dolibarr_update_contact($payload, $bridge) -{ - return forms_bridge_dolibarr_create_contact($payload, $bridge, true); -} - -function forms_bridge_dolibarr_create_contact( - $payload, - $bridge, - $update = false -) { - if (!$update) { - $contact = forms_bridge_dolibarr_search_contact($payload, $bridge); - - if (!is_wp_error($contact) && isset($contact['id'])) { - $payload['id'] = $contact['id']; - return forms_bridge_dolibarr_update_contact($payload, $bridge); - } - } - - $contact = [ - 'lastname' => $payload['lastname'], - ]; - - $contact_fields = [ - 'email', - 'firstname', - 'civility_code', - 'socid', - 'poste', - 'status', - 'note_public', - 'note_private', - 'address', - 'zip', - 'town', - 'country_id', - 'state_id', - 'region_id', - 'url', - 'no_email', - 'phone_pro', - 'phone_perso', - 'phone_mobile', - 'fax', - 'stcomm_id', - 'default_lang', - ]; - - foreach ($contact_fields as $field) { - if (isset($payload[$field])) { - $contact[$field] = $payload[$field]; - } - } - - $method = 'POST'; - $endpoint = '/api/index.php/contacts'; - if ($update && isset($payload['id'])) { - $endpoint .= '/' . $payload['id']; - $method = 'PUT'; - } - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-create-contact', - 'endpoint' => $endpoint, - 'method' => $method, - ]) - ->submit($contact); - - if (is_wp_error($response)) { - return $response; - } - - if ($method === 'POST') { - $response = $bridge - ->patch([ - 'name' => 'dolibarr-get-new-contact-data', - 'endpoint' => '/api/index.php/contacts/' . $response['data'], - 'method' => 'GET', - ]) - ->submit([]); - } - - return $response['data']; -} - -function forms_bridge_dolibarr_update_thirdparty($payload, $bridge) -{ - return forms_bridge_dolibarr_create_thirdparty($payload, $bridge, true); -} - -function forms_bridge_dolibarr_create_thirdparty( - $payload, - $bridge, - $update = false -) { - if (!$update) { - $thirdparty = forms_bridge_dolibarr_search_thirdparty( - $payload, - $bridge - ); - - if (!is_wp_error($thirdparty) && isset($thirdparty['id'])) { - $payload['id'] = $thirdparty['id']; - - if (!empty($thirdparty['code_client'])) { - $payload['code_client'] = $thirdparty['code_client']; - } - - return forms_bridge_dolibarr_update_thirdparty($payload, $bridge); - } - } - - $thirdparty = [ - 'name' => $payload['name'], - ]; - - $thirdparty_fields = [ - 'email', - 'idprof1', - 'idprof2', - 'tva_intra', - 'phone', - 'fax', - 'url', - 'zip', - 'town', - 'address', - 'region_id', - 'state_id', - 'country_id', - 'no_email', - 'typent_id', - 'stcomm_id', - 'parent', - 'client', - 'fournisseur', - 'code_client', - ]; - - foreach ($thirdparty_fields as $field) { - if (isset($payload[$field])) { - $thirdparty[$field] = $payload[$field]; - } - } - - if (!isset($thirdparty['code_client']) && !$update) { - $code_client = forms_bridge_dolibarr_get_next_code_client( - $payload, - $bridge - ); - if (is_wp_error($code_client)) { - return $code_client; - } - - $thirdparty['code_client'] = $code_client; - } - - $endpoint = '/api/index.php/thirdparties'; - $method = 'POST'; - - if ($update && isset($payload['id'])) { - $endpoint .= '/' . $payload['id']; - $method = 'PUT'; - } - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-create-thirdparty', - 'endpoint' => $endpoint, - 'method' => $method, - ]) - ->submit($thirdparty); - - if (is_wp_error($response)) { - return $response; - } - - if ($method === 'POST') { - $response = $bridge - ->patch([ - 'name' => 'dolibarr-get-new-thirdparty-data', - 'endpoint' => - '/api/index.php/thirdparties/' . $response['data'], - 'method' => 'GET', - ]) - ->submit(); - } - - return $response['data']; -} diff --git a/addons/dolibarr/class-dolibarr-addon.php b/addons/dolibarr/class-dolibarr-addon.php deleted file mode 100644 index 6ef8a295..00000000 --- a/addons/dolibarr/class-dolibarr-addon.php +++ /dev/null @@ -1,130 +0,0 @@ - '__dolibarr-' . time(), - 'endpoint' => '/api/index.php/status', - 'method' => 'GET', - 'backend' => $backend, - ]); - - $response = $bridge->submit(); - if (is_wp_error($response)) { - return false; - } - - $code = $response['data']['success']['code'] ?? null; - return $code === 200; - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge = new Dolibarr_Form_Bridge([ - 'name' => '__dolibarr-' . time(), - 'endpoint' => $endpoint, - 'backend' => $backend, - 'method' => 'GET', - ]); - - return $bridge->submit(); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array - */ - public function get_endpoint_schema($endpoint, $backend) - { - $bridge = new Dolibarr_Form_Bridge([ - 'name' => '__dolibarr-' . time(), - 'endpoint' => $endpoint, - 'backend' => $backend, - 'method' => 'GET', - ]); - - $response = $bridge->submit(['limit' => 1]); - - if (is_wp_error($response)) { - return []; - } - - $entry = $response['data'][0] ?? null; - if (!$entry) { - return []; - } - - $fields = []; - foreach ($entry as $field => $value) { - if (wp_is_numeric_array($value)) { - $type = 'array'; - } elseif (is_array($value)) { - $type = 'object'; - } elseif (is_double($value)) { - $type = 'number'; - } elseif (is_int($value)) { - $type = 'integer'; - } else { - $type = 'string'; - } - - $fields[] = [ - 'name' => $field, - 'schema' => ['type' => $type], - ]; - } - - return $fields; - } -} - -Dolibarr_Addon::setup(); diff --git a/addons/dolibarr/class-dolibarr-form-bridge.php b/addons/dolibarr/class-dolibarr-form-bridge.php deleted file mode 100644 index 7769513f..00000000 --- a/addons/dolibarr/class-dolibarr-form-bridge.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - [ - 'ref' => '#backend', - 'name' => 'name', - 'default' => 'Dolibarr', - ], - [ - 'ref' => '#backend/headers[]', - 'name' => 'DOLAPIKEY', - 'label' => __('API key', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - ], - 'backend' => [ - 'name' => 'Dolibarr', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'dolibarr-') !== 0) { - return $data; - } - - $index = array_search( - 'no_email', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = &$data['bridge']['custom_fields'][$index]; - $field['value'] = $field['value'] ? '0' : '1'; - } - - $index = array_search( - 'fulldayevent', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $data['form']['fields'] = array_filter( - $data['form']['fields'], - function ($field) { - return !in_array( - $field['name'], - [ - 'hour', - 'minute', - __('Hour', 'forms-bridge'), - __('Minute', 'forms-bridge'), - ], - true - ); - } - ); - - $index = array_search( - 'duration', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - array_splice($data['bridge']['custom_fields'], $index, 1); - } - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/dolibarr/jobs/appointment-attendee.php b/addons/dolibarr/jobs/appointment-attendee.php deleted file mode 100644 index 70db2ad4..00000000 --- a/addons/dolibarr/jobs/appointment-attendee.php +++ /dev/null @@ -1,143 +0,0 @@ - $contact['id'], - 'mandatory' => 0, - 'answer_status' => 0, - 'transparency' => 0, - ]; - - return $payload; -} - -return [ - 'title' => __('Appointment attendee', 'forms-bridge'), - 'description' => __( - 'Create a contact and binds it to the appointment as an attendee', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_appointment_attendee', - 'input' => [ - [ - 'name' => 'lastname', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'firstname', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'civility_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'status', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'note_public', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'note_private', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'address', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'town', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'state_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'region_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'phone_pro', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone_perso', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone_mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'fax', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'url', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'socid', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'poste', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'stcomm_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'no_email', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'socpeopleassigned', - 'schema' => [ - 'type' => 'array', - 'items' => [ - [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'string'], - 'mandatory' => ['type' => 'string'], - 'answer_status' => ['type' => 'string'], - 'transparency' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - ], - ], - ], -]; diff --git a/addons/dolibarr/jobs/appointment-dates.php b/addons/dolibarr/jobs/appointment-dates.php deleted file mode 100644 index 47d65a48..00000000 --- a/addons/dolibarr/jobs/appointment-dates.php +++ /dev/null @@ -1,57 +0,0 @@ -getTimestamp(); - $payload['datep'] = $timestamp; - $payload['duration'] = floatval($payload['duration'] ?? 1); - $payload['datef'] = intval($payload['duration'] * 3600 + $timestamp); - - return $payload; -} - -return [ - 'title' => __('Appointment dates', 'forms-bridge'), - 'description' => __( - 'Sets appointment start, end time and duration from datetime and duration fields.', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_appointment_dates', - 'input' => [ - [ - 'name' => 'date', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'duration', - 'schema' => ['type' => 'number'], - ], - ], - 'output' => [ - [ - 'name' => 'datep', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'datef', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'duration', - 'schema' => ['type' => 'number'], - ], - ], -]; diff --git a/addons/dolibarr/jobs/contact-id.php b/addons/dolibarr/jobs/contact-id.php deleted file mode 100644 index 949643c8..00000000 --- a/addons/dolibarr/jobs/contact-id.php +++ /dev/null @@ -1,127 +0,0 @@ - __('Contact', 'forms-bridge'), - 'description' => __( - 'Creates a contact and adds its ID to the contact_ids field of the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_contact_ids', - 'input' => [ - [ - 'name' => 'lastname', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'firstname', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'civility_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'socid', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'poste', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'status', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'note_public', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'note_private', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'address', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'town', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'state_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'region_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'url', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'no_email', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'phone_pro', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone_perso', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone_mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'fax', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'stcomm_id', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'default_lang', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'contact_ids', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - 'additionalItems' => true, - ], - ], - ], -]; diff --git a/addons/dolibarr/jobs/contact-socid.php b/addons/dolibarr/jobs/contact-socid.php deleted file mode 100644 index 051099b5..00000000 --- a/addons/dolibarr/jobs/contact-socid.php +++ /dev/null @@ -1,127 +0,0 @@ - __('Third party', 'forms-bridge'), - 'description' => __( - 'Creates a new third party and returns its ID as the socid of the payload.', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_thirdparty_socid', - 'input' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'code_client', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'idprof1', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'idprof2', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'tva_intra', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'fax', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'url', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'address', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'town', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'region_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'state_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'typent_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'status', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'client', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'fournisseur', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'stcomm_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'note_public', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'no_email', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'parent', - 'schema' => ['type' => 'integer'], - ], - ], - 'output' => [ - [ - 'name' => 'socid', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/dolibarr/jobs/country-id.php b/addons/dolibarr/jobs/country-id.php deleted file mode 100644 index 722f511a..00000000 --- a/addons/dolibarr/jobs/country-id.php +++ /dev/null @@ -1,62 +0,0 @@ -patch([ - 'name' => 'dolibarr-get-country-id', - 'method' => 'GET', - 'endpoint' => - '/api/index.php/setup/dictionary/countries/byCode/' . - $payload['country'], - ]) - ->submit(); - - if (is_wp_error($response)) { - return $response; - } - - $payload['country_id'] = $response['data']['id']; - return $payload; -} - -return [ - 'title' => __('Country ID', 'forms-bridge'), - 'description' => __( - 'Gets country_id value from country code and replace it on the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_country_id_from_code', - 'input' => [ - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'country_id', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'country_id', - 'schema' => ['type' => 'integer'], - ], - ], -]; diff --git a/addons/dolibarr/jobs/next-client-code.php b/addons/dolibarr/jobs/next-client-code.php deleted file mode 100644 index 8dceba31..00000000 --- a/addons/dolibarr/jobs/next-client-code.php +++ /dev/null @@ -1,36 +0,0 @@ - __('Next code client', 'forms-brige'), - 'description' => __( - 'Query for the next valid thirdparty code client', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_next_code_client', - 'input' => [], - 'output' => [ - [ - 'name' => 'code_client', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/dolibarr/jobs/next-project-ref.php b/addons/dolibarr/jobs/next-project-ref.php deleted file mode 100644 index 7f5902b5..00000000 --- a/addons/dolibarr/jobs/next-project-ref.php +++ /dev/null @@ -1,33 +0,0 @@ - __('Next project ref', 'forms-brige'), - 'description' => __('Query for the next valid project ref', 'forms-bridge'), - 'method' => 'forms_bridge_dolibarr_next_project_ref', - 'input' => [], - 'output' => [ - [ - 'name' => 'ref', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/dolibarr/jobs/products-by-ref.php b/addons/dolibarr/jobs/products-by-ref.php deleted file mode 100644 index 2f6f0dc7..00000000 --- a/addons/dolibarr/jobs/products-by-ref.php +++ /dev/null @@ -1,87 +0,0 @@ - __('Products by reference', 'forms-bridge'), - 'description' => __( - 'Search for products on Dolibarr based on a list of references and returns its IDs.', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_search_products_by_ref', - 'input' => [ - [ - 'name' => 'product_refs', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - 'additionalItems' => true, - ], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'fk_products', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_dolibarr_search_products_by_ref($payload, $bridge) -{ - $sqlfilters = []; - $refs = (array) $payload['product_refs']; - foreach ($refs as $ref) { - $ref = trim($ref); - $sqlfilters[] = "(t.ref:=:'{$ref}')"; - } - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-search-products-by-ref', - 'endpoint' => '/api/index.php/products', - 'method' => 'GET', - ]) - ->submit([ - 'properties' => 'id,ref', - 'sqlfilters' => implode(' or ', $sqlfilters), - ]); - - if (is_wp_error($response)) { - return $response; - } - - $fk_products = []; - foreach ($refs as $ref) { - foreach ($response['data'] as $product) { - if ($product['ref'] === $ref) { - $fk_products[] = $product['id']; - break; - } - } - } - - if (count($fk_products) !== count($payload['product_refs'])) { - return new WP_Error( - 'product_search_error', - __( - 'Inconsistencies between amount of found products and search references', - 'forms-bridge' - ), - [ - 'response' => $response, - 'internal_refs' => $payload['product_refs'], - ] - ); - } - - $payload['fk_products'] = $fk_products; - return $payload; -} diff --git a/addons/dolibarr/jobs/skip-if-contact-exists.php b/addons/dolibarr/jobs/skip-if-contact-exists.php deleted file mode 100644 index 8474f971..00000000 --- a/addons/dolibarr/jobs/skip-if-contact-exists.php +++ /dev/null @@ -1,79 +0,0 @@ - __('Skip if contact exists', 'forms-bridge'), - 'description' => __( - 'Aborts form submission if the contact exists', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_skip_contact', - 'input' => [ - [ - 'name' => 'email', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'firstname', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'lastname', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'socid', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'requires' => ['email'], - ], - [ - 'name' => 'firstname', - 'schema' => ['type' => 'string'], - 'requires' => ['firstname'], - ], - [ - 'name' => 'lastname', - 'schema' => ['type' => 'string'], - 'requires' => ['lastname'], - ], - [ - 'name' => 'socid', - 'schema' => ['type' => 'string'], - 'requires' => ['socid'], - ], - ], -]; diff --git a/addons/dolibarr/jobs/skip-if-thirdparty-exists.php b/addons/dolibarr/jobs/skip-if-thirdparty-exists.php deleted file mode 100644 index b6170c05..00000000 --- a/addons/dolibarr/jobs/skip-if-thirdparty-exists.php +++ /dev/null @@ -1,74 +0,0 @@ - __('Skip if thirdparty exists', 'forms-bridge'), - 'description' => __( - 'Aborts form submission if a thirdparty already exists', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_skip_thirdparty', - 'input' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'idprof1', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'requires' => ['name'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'requires' => ['email'], - ], - [ - 'name' => 'idprof1', - 'schema' => ['type' => 'string'], - 'requires' => ['idprof1'], - ], - ], -]; diff --git a/addons/dolibarr/jobs/sync-products-by-ref.php b/addons/dolibarr/jobs/sync-products-by-ref.php deleted file mode 100644 index 6958360b..00000000 --- a/addons/dolibarr/jobs/sync-products-by-ref.php +++ /dev/null @@ -1,145 +0,0 @@ - __('Sync woo products', 'forms-bridge'), - 'description' => __( - 'Search for products from the WooCommerce order by sku on Dolibarr and creates new ones if someone does not exists', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_sync_products_by_ref', - 'input' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - 'required' => ['sku', 'name', 'price'], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_dolibarr_sync_products_by_ref($payload, $bridge) -{ - $product_refs = []; - foreach ($payload['line_items'] as $line_item) { - if (empty($line_item['product']['sku'])) { - return new WP_Error( - "SKU is required on product {$line_item['product']['name']}" - ); - } - - $product_refs[] = $line_item['product']['sku']; - } - - $sqlfilters = []; - foreach ($product_refs as $ref) { - $ref = trim($ref); - $sqlfilters[] = "(t.ref:=:'{$ref}')"; - } - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-search-products-by-ref', - 'endpoint' => '/api/index.php/products', - 'method' => 'GET', - ]) - ->submit([ - 'properties' => 'id,ref', - 'sqlfilters' => implode(' or ', $sqlfilters), - ]); - - if (is_wp_error($response)) { - return $response; - } - - foreach ($payload['line_items'] as $line_item) { - $product = null; - foreach ($response['data'] as $candidate) { - if ($candidate['ref'] === $line_item['product']['sku']) { - $product = $candidate; - break; - } - } - - if (!$product) { - $product_response = $bridge - ->patch([ - 'name' => 'dolibarr-sync-product-by-ref', - 'endpoint' => '/api/index.php/products', - 'method' => 'POST', - ]) - ->submit([ - 'label' => $line_item['product']['name'], - 'ref' => $line_item['product']['sku'], - 'status' => '1', - 'type' => '0', - 'price' => $line_item['product']['price'], - ]); - - if (is_wp_error($product_response)) { - return $product_response; - } - } - } - - return $payload; -} diff --git a/addons/dolibarr/jobs/validate-order.php b/addons/dolibarr/jobs/validate-order.php deleted file mode 100644 index d100fbf1..00000000 --- a/addons/dolibarr/jobs/validate-order.php +++ /dev/null @@ -1,66 +0,0 @@ - __('Validate order', 'forms-bridge'), - 'description' => __( - 'Add a callback to the bridge submission to validate the order after its creation', - 'forms-bridge' - ), - 'method' => 'forms_bridge_dolibarr_enqueue_order_validation', - 'input' => [], - 'output' => [], -]; - -function forms_bridge_dolibarr_validate_order( - $bridge, - $response, - $payload, - $attachments -) { - remove_action( - 'forms_bridge_after_submission', - 'forms_bridge_dolibarr_validate_order', - 10, - 4 - ); - - $order_id = intval($response['data'] ?? null); - - if (empty($order_id)) { - return; - } - - $response = $bridge - ->patch([ - 'name' => 'dolibarr-validate-order', - 'method' => 'POST', - 'endpoint' => "/api/index.php/orders/{$order_id}/validate", - ]) - ->submit(); - - if (is_wp_error($response)) { - do_action( - 'forms_bridge_on_failure', - $response, - $bridge, - $payload, - $attachments - ); - } -} - -function forms_bridge_dolibarr_enqueue_order_validation($payload) -{ - add_action( - 'forms_bridge_after_submission', - 'forms_bridge_dolibarr_validate_order', - 10, - 4 - ); - - return $payload; -} diff --git a/addons/dolibarr/state-codes.php b/addons/dolibarr/state-codes.php deleted file mode 100644 index 72898ff6..00000000 --- a/addons/dolibarr/state-codes.php +++ /dev/null @@ -1,1829 +0,0 @@ - '60', 'label' => 'AD-002 - Canillo'], - ['value' => '61', 'label' => 'AD-003 - Encamp'], - ['value' => '62', 'label' => 'AD-004 - La Massana'], - ['value' => '63', 'label' => 'AD-005 - Ordino'], - ['value' => '64', 'label' => 'AD-006 - Sant Julià de Lòria'], - ['value' => '65', 'label' => 'AD-007 - Andorra la Vella'], - ['value' => '66', 'label' => 'AD-008 - Escaldes-Engordany'], - ['value' => '1684', 'label' => 'AE-1 - Abu Dhabi'], - ['value' => '1685', 'label' => 'AE-2 - Dubai'], - ['value' => '1686', 'label' => 'AE-3 - Ajman'], - ['value' => '1687', 'label' => 'AE-4 - Fujairah'], - ['value' => '1688', 'label' => 'AE-5 - Ras al-Khaimah'], - ['value' => '1689', 'label' => 'AE-6 - Sharjah'], - ['value' => '1690', 'label' => 'AE-7 - Umm al-Quwain'], - ['value' => '67', 'label' => 'AO-ABO - Bengo'], - ['value' => '68', 'label' => 'AO-BGU - Benguela'], - ['value' => '69', 'label' => 'AO-BIE - Bié'], - ['value' => '70', 'label' => 'AO-CAB - Cabinda'], - ['value' => '71', 'label' => 'AO-CCU - Kuando Kubango'], - ['value' => '74', 'label' => 'AO-CNN - Cunene'], - ['value' => '72', 'label' => 'AO-CNO - Kwanza Norte'], - ['value' => '73', 'label' => 'AO-CUS - Kwanza Sul'], - ['value' => '75', 'label' => 'AO-HUA - Huambo'], - ['value' => '76', 'label' => 'AO-HUI - Huila'], - ['value' => '78', 'label' => 'AO-LNO - Lunda-Norte'], - ['value' => '79', 'label' => 'AO-LSU - Lunda-Sul'], - ['value' => '77', 'label' => 'AO-LUA - Luanda'], - ['value' => '80', 'label' => 'AO-MAL - Malange'], - ['value' => '81', 'label' => 'AO-MOX - Moxico'], - ['value' => '82', 'label' => 'AO-NAM - Namíbe'], - ['value' => '83', 'label' => 'AO-UIG - Uíge'], - ['value' => '84', 'label' => 'AO-ZAI - Zaíre'], - ['value' => '85', 'label' => '2301 - Catamarca'], - ['value' => '86', 'label' => '2302 - Jujuy'], - ['value' => '87', 'label' => '2303 - Tucamán'], - ['value' => '88', 'label' => '2304 - Santiago del Estero'], - ['value' => '89', 'label' => '2305 - Salta'], - ['value' => '90', 'label' => '2306 - Chaco'], - ['value' => '91', 'label' => '2307 - Corrientes'], - ['value' => '92', 'label' => '2308 - Entre Ríos'], - ['value' => '93', 'label' => '2309 - Formosa'], - ['value' => '94', 'label' => '2310 - Santa Fe'], - ['value' => '95', 'label' => '2311 - La Rioja'], - ['value' => '96', 'label' => '2312 - Mendoza'], - ['value' => '97', 'label' => '2313 - San Juan'], - ['value' => '98', 'label' => '2314 - San Luis'], - ['value' => '99', 'label' => '2315 - Córdoba'], - ['value' => '100', 'label' => '2316 - Buenos Aires'], - ['value' => '101', 'label' => '2317 - Caba'], - ['value' => '102', 'label' => '2318 - La Pampa'], - ['value' => '103', 'label' => '2319 - Neuquén'], - ['value' => '104', 'label' => '2320 - Río Negro'], - ['value' => '105', 'label' => '2321 - Chubut'], - ['value' => '106', 'label' => '2322 - Santa Cruz'], - ['value' => '107', 'label' => '2323 - Tierra del Fuego'], - ['value' => '108', 'label' => '2324 - Islas Malvinas'], - ['value' => '109', 'label' => '2325 - Antártida'], - ['value' => '110', 'label' => '2326 - Misiones'], - ['value' => '119', 'label' => 'B - Burgenland'], - ['value' => '120', 'label' => 'K - Kärnten'], - ['value' => '121', 'label' => 'N - Niederösterreich'], - ['value' => '122', 'label' => 'O - Oberösterreich'], - ['value' => '123', 'label' => 'S - Salzburg'], - ['value' => '124', 'label' => 'ST - Steiermark'], - ['value' => '125', 'label' => 'T - Tirol'], - ['value' => '126', 'label' => 'V - Vorarlberg'], - ['value' => '127', 'label' => 'W - Wien'], - ['value' => '115', 'label' => 'ACT - Australia Capital Territory'], - ['value' => '111', 'label' => 'NSW - New South Wales'], - ['value' => '118', 'label' => 'NT - Northern Territory'], - ['value' => '113', 'label' => 'QLD - Queensland'], - ['value' => '114', 'label' => 'SA - South Australia'], - ['value' => '116', 'label' => 'TAS - Tasmania'], - ['value' => '112', 'label' => 'VIC - Victoria'], - ['value' => '117', 'label' => 'WA - Western Australia'], - ['value' => '128', 'label' => 'CC - Christ Church'], - ['value' => '131', 'label' => 'JA - Saint James'], - ['value' => '129', 'label' => 'SA - Saint Andrew'], - ['value' => '133', 'label' => 'SB - Saint Joseph'], - ['value' => '137', 'label' => 'SC - Saint Philip'], - ['value' => '130', 'label' => 'SG - Saint George'], - ['value' => '132', 'label' => 'SJ - Saint John'], - ['value' => '134', 'label' => 'SL - Saint Lucy'], - ['value' => '135', 'label' => 'SM - Saint Michael'], - ['value' => '136', 'label' => 'SP - Saint Peter'], - ['value' => '138', 'label' => 'ST - Saint Thomas'], - ['value' => '139', 'label' => '01 - Anvers'], - ['value' => '140', 'label' => '02 - Bruxelles-Capitale'], - ['value' => '141', 'label' => '03 - Brabant-Wallon'], - ['value' => '142', 'label' => '04 - Brabant-Flamand'], - ['value' => '143', 'label' => '05 - Flandre-Occidentale'], - ['value' => '144', 'label' => '06 - Flandre-Orientale'], - ['value' => '145', 'label' => '07 - Hainaut'], - ['value' => '146', 'label' => '08 - Liège'], - ['value' => '147', 'label' => '09 - Limbourg'], - ['value' => '148', 'label' => '10 - Luxembourg'], - ['value' => '149', 'label' => '11 - Namur'], - ['value' => '1565', 'label' => 'BI0001 - Bubanza'], - ['value' => '1566', 'label' => 'BI0002 - Gihanga'], - ['value' => '1567', 'label' => 'BI0003 - Musigati'], - ['value' => '1568', 'label' => 'BI0004 - Mpanda'], - ['value' => '1569', 'label' => 'BI0005 - Rugazi'], - ['value' => '1570', 'label' => 'BI0006 - Muha'], - ['value' => '1571', 'label' => 'BI0007 - Mukaza'], - ['value' => '1572', 'label' => 'BI0008 - Ntahangwa'], - ['value' => '1573', 'label' => 'BI0009 - Isale'], - ['value' => '1574', 'label' => 'BI0010 - Kabezi'], - ['value' => '1575', 'label' => 'BI0011 - Kanyosha'], - ['value' => '1576', 'label' => 'BI0012 - Mubimbi'], - ['value' => '1577', 'label' => 'BI0013 - Mugongomanga'], - ['value' => '1578', 'label' => 'BI0014 - Mukike'], - ['value' => '1579', 'label' => 'BI0015 - Mutambu'], - ['value' => '1580', 'label' => 'BI0016 - Mutimbuzi'], - ['value' => '1581', 'label' => 'BI0017 - Nyabiraba'], - ['value' => '1582', 'label' => 'BI0018 - Bururi'], - ['value' => '1583', 'label' => 'BI0019 - Matana'], - ['value' => '1584', 'label' => 'BI0020 - Mugamba'], - ['value' => '1585', 'label' => 'BI0021 - Rutovu'], - ['value' => '1586', 'label' => 'BI0022 - Songa'], - ['value' => '1587', 'label' => 'BI0023 - Vyanda'], - ['value' => '1588', 'label' => 'BI0024 - Cankuzo'], - ['value' => '1589', 'label' => 'BI0025 - Cendajuru'], - ['value' => '1590', 'label' => 'BI0026 - Gisagara'], - ['value' => '1591', 'label' => 'BI0027 - Kigamba'], - ['value' => '1592', 'label' => 'BI0028 - Mishiha'], - ['value' => '1593', 'label' => 'BI0029 - Buganda'], - ['value' => '1594', 'label' => 'BI0030 - Bukinanyana'], - ['value' => '1595', 'label' => 'BI0031 - Mabayi'], - ['value' => '1596', 'label' => 'BI0032 - Mugina'], - ['value' => '1597', 'label' => 'BI0033 - Murwi'], - ['value' => '1598', 'label' => 'BI0034 - Rugombo'], - ['value' => '1599', 'label' => 'BI0035 - Bugendana'], - ['value' => '1600', 'label' => 'BI0036 - Bukirasazi'], - ['value' => '1601', 'label' => 'BI0037 - Buraza'], - ['value' => '1602', 'label' => 'BI0038 - Giheta'], - ['value' => '1603', 'label' => 'BI0039 - Gishubi'], - ['value' => '1604', 'label' => 'BI0040 - Gitega'], - ['value' => '1605', 'label' => 'BI0041 - Itaba'], - ['value' => '1606', 'label' => 'BI0042 - Makebuko'], - ['value' => '1607', 'label' => 'BI0043 - Mutaho'], - ['value' => '1608', 'label' => 'BI0044 - Nyanrusange'], - ['value' => '1609', 'label' => 'BI0045 - Ryansoro'], - ['value' => '1610', 'label' => 'BI0046 - Bugenyuzi'], - ['value' => '1611', 'label' => 'BI0047 - Buhiga'], - ['value' => '1612', 'label' => 'BI0048 - Gihogazi'], - ['value' => '1613', 'label' => 'BI0049 - Gitaramuka'], - ['value' => '1614', 'label' => 'BI0050 - Mutumba'], - ['value' => '1615', 'label' => 'BI0051 - Nyabikere'], - ['value' => '1616', 'label' => 'BI0052 - Shombo'], - ['value' => '1617', 'label' => 'BI0053 - Butaganzwa'], - ['value' => '1618', 'label' => 'BI0054 - Gahombo'], - ['value' => '1619', 'label' => 'BI0055 - Gatara'], - ['value' => '1620', 'label' => 'BI0056 - Kabarore'], - ['value' => '1621', 'label' => 'BI0057 - Kayanza'], - ['value' => '1622', 'label' => 'BI0058 - Matongo'], - ['value' => '1623', 'label' => 'BI0059 - Muhanga'], - ['value' => '1624', 'label' => 'BI0060 - Muruta'], - ['value' => '1625', 'label' => 'BI0061 - Rango'], - ['value' => '1626', 'label' => 'BI0062 - Bugabira'], - ['value' => '1627', 'label' => 'BI0063 - Busoni'], - ['value' => '1628', 'label' => 'BI0064 - Bwambarangwe'], - ['value' => '1629', 'label' => 'BI0065 - Gitobe'], - ['value' => '1630', 'label' => 'BI0066 - Kirundo'], - ['value' => '1631', 'label' => 'BI0067 - Ntega'], - ['value' => '1632', 'label' => 'BI0068 - Vumbi'], - ['value' => '1633', 'label' => 'BI0069 - Kayogoro'], - ['value' => '1634', 'label' => 'BI0070 - Kibago'], - ['value' => '1635', 'label' => 'BI0071 - Mabanda'], - ['value' => '1636', 'label' => 'BI0072 - Makamba'], - ['value' => '1637', 'label' => 'BI0073 - Nyanza-Lac'], - ['value' => '1638', 'label' => 'BI0074 - Vugizo'], - ['value' => '1639', 'label' => 'BI0075 - Bukeye'], - ['value' => '1640', 'label' => 'BI0076 - Kiganda'], - ['value' => '1641', 'label' => 'BI0077 - Mbuye'], - ['value' => '1642', 'label' => 'BI0078 - Muramvya'], - ['value' => '1643', 'label' => 'BI0079 - Rutegama'], - ['value' => '1644', 'label' => 'BI0080 - Buhinyuza'], - ['value' => '1645', 'label' => 'BI0081 - Butihinda'], - ['value' => '1646', 'label' => 'BI0082 - Gashoho'], - ['value' => '1647', 'label' => 'BI0083 - Gasorwe'], - ['value' => '1648', 'label' => 'BI0084 - Giteranyi'], - ['value' => '1649', 'label' => 'BI0085 - Muyinga'], - ['value' => '1650', 'label' => 'BI0086 - Mwakiro'], - ['value' => '1651', 'label' => 'BI0087 - Bisoro'], - ['value' => '1652', 'label' => 'BI0088 - Gisozi'], - ['value' => '1653', 'label' => 'BI0089 - Kayokwe'], - ['value' => '1654', 'label' => 'BI0090 - Ndava'], - ['value' => '1655', 'label' => 'BI0091 - Nyabihanga'], - ['value' => '1656', 'label' => 'BI0092 - Rusaka'], - ['value' => '1657', 'label' => 'BI0093 - Busiga'], - ['value' => '1658', 'label' => 'BI0094 - Gashikanwa'], - ['value' => '1659', 'label' => 'BI0095 - Kiremba'], - ['value' => '1660', 'label' => 'BI0096 - Marangara'], - ['value' => '1661', 'label' => 'BI0097 - Mwumba'], - ['value' => '1662', 'label' => 'BI0098 - Ngozi'], - ['value' => '1663', 'label' => 'BI0099 - Nyamurenza'], - ['value' => '1664', 'label' => 'BI0100 - Ruhororo'], - ['value' => '1665', 'label' => 'BI0101 - Tangara'], - ['value' => '1666', 'label' => 'BI0102 - Bugarama'], - ['value' => '1667', 'label' => 'BI0103 - Burambi'], - ['value' => '1668', 'label' => 'BI0104 - Buyengero'], - ['value' => '1669', 'label' => 'BI0105 - Muhuta'], - ['value' => '1670', 'label' => 'BI0106 - Rumonge'], - ['value' => '1671', 'label' => 'BI0107 - Bukemba'], - ['value' => '1672', 'label' => 'BI0108 - Giharo'], - ['value' => '1673', 'label' => 'BI0109 - Gitanga'], - ['value' => '1674', 'label' => 'BI0110 - Mpinga-Kayove'], - ['value' => '1675', 'label' => 'BI0111 - Musongati'], - ['value' => '1676', 'label' => 'BI0112 - Rutana'], - ['value' => '1677', 'label' => 'BI0113 - Butaganzwa'], - ['value' => '1678', 'label' => 'BI0114 - Butezi'], - ['value' => '1679', 'label' => 'BI0115 - Bweru'], - ['value' => '1680', 'label' => 'BI0116 - Gisuru'], - ['value' => '1681', 'label' => 'BI0117 - Kinyinya'], - ['value' => '1682', 'label' => 'BI0118 - Nyabitsinda'], - ['value' => '1683', 'label' => 'BI0119 - Ruyigi'], - ['value' => '1159', 'label' => '001 - Belisario Boeto'], - ['value' => '1160', 'label' => '002 - Hernando Siles'], - ['value' => '1161', 'label' => '003 - Jaime Zudáñez'], - ['value' => '1162', 'label' => '004 - Juana Azurduy de Padilla'], - ['value' => '1163', 'label' => '005 - Luis Calvo'], - ['value' => '1164', 'label' => '006 - Nor Cinti'], - ['value' => '1165', 'label' => '007 - Oropeza'], - ['value' => '1166', 'label' => '008 - Sud Cinti'], - ['value' => '1167', 'label' => '009 - Tomina'], - ['value' => '1168', 'label' => '010 - Yamparáez'], - ['value' => '1169', 'label' => '011 - Abel Iturralde'], - ['value' => '1170', 'label' => '012 - Aroma'], - ['value' => '1171', 'label' => '013 - Bautista Saavedra'], - ['value' => '1172', 'label' => '014 - Caranavi'], - ['value' => '1173', 'label' => '015 - Eliodoro Camacho'], - ['value' => '1174', 'label' => '016 - Franz Tamayo'], - ['value' => '1175', 'label' => '017 - Gualberto Villarroel'], - ['value' => '1176', 'label' => '018 - Ingaví'], - ['value' => '1177', 'label' => '019 - Inquisivi'], - ['value' => '1178', 'label' => '020 - José Ramón Loayza'], - ['value' => '1179', 'label' => '021 - Larecaja'], - ['value' => '1180', 'label' => '022 - Los Andes (Bolivia)'], - ['value' => '1181', 'label' => '023 - Manco Kapac'], - ['value' => '1182', 'label' => '024 - Muñecas'], - ['value' => '1183', 'label' => '025 - Nor Yungas'], - ['value' => '1184', 'label' => '026 - Omasuyos'], - ['value' => '1185', 'label' => '027 - Pacajes'], - ['value' => '1186', 'label' => '028 - Pedro Domingo Murillo'], - ['value' => '1187', 'label' => '029 - Sud Yungas'], - ['value' => '1188', 'label' => '030 - General José Manuel Pando'], - ['value' => '1189', 'label' => '031 - Arani'], - ['value' => '1190', 'label' => '032 - Arque'], - ['value' => '1191', 'label' => '033 - Ayopaya'], - ['value' => '1192', 'label' => '034 - Bolívar (Bolivia)'], - ['value' => '1193', 'label' => '035 - Campero'], - ['value' => '1194', 'label' => '036 - Capinota'], - ['value' => '1195', 'label' => '037 - Cercado (Cochabamba)'], - ['value' => '1196', 'label' => '038 - Esteban Arze'], - ['value' => '1197', 'label' => '039 - Germán Jordán'], - ['value' => '1198', 'label' => '040 - José Carrasco'], - ['value' => '1199', 'label' => '041 - Mizque'], - ['value' => '1200', 'label' => '042 - Punata'], - ['value' => '1201', 'label' => '043 - Quillacollo'], - ['value' => '1202', 'label' => '044 - Tapacarí'], - ['value' => '1203', 'label' => '045 - Tiraque'], - ['value' => '1204', 'label' => '046 - Chapare'], - ['value' => '1205', 'label' => '047 - Carangas'], - ['value' => '1206', 'label' => '048 - Cercado (Oruro)'], - ['value' => '1207', 'label' => '049 - Eduardo Avaroa'], - ['value' => '1208', 'label' => '050 - Ladislao Cabrera'], - ['value' => '1209', 'label' => '051 - Litoral de Atacama'], - ['value' => '1210', 'label' => '052 - Mejillones'], - ['value' => '1211', 'label' => '053 - Nor Carangas'], - ['value' => '1212', 'label' => '054 - Pantaleón Dalence'], - ['value' => '1213', 'label' => '055 - Poopó'], - ['value' => '1214', 'label' => '056 - Sabaya'], - ['value' => '1215', 'label' => '057 - Sajama'], - ['value' => '1216', 'label' => '058 - San Pedro de Totora'], - ['value' => '1217', 'label' => '059 - Saucarí'], - ['value' => '1218', 'label' => '060 - Sebastián Pagador'], - ['value' => '1219', 'label' => '061 - Sud Carangas'], - ['value' => '1220', 'label' => '062 - Tomás Barrón'], - ['value' => '1221', 'label' => '063 - Alonso de Ibáñez'], - ['value' => '1222', 'label' => '064 - Antonio Quijarro'], - ['value' => '1223', 'label' => '065 - Bernardino Bilbao'], - ['value' => '1224', 'label' => '066 - Charcas (Potosí)'], - ['value' => '1225', 'label' => '067 - Chayanta'], - ['value' => '1226', 'label' => '068 - Cornelio Saavedra'], - ['value' => '1227', 'label' => '069 - Daniel Campos'], - ['value' => '1228', 'label' => '070 - Enrique Baldivieso'], - ['value' => '1229', 'label' => '071 - José María Linares'], - ['value' => '1230', 'label' => '072 - Modesto Omiste'], - ['value' => '1231', 'label' => '073 - Nor Chichas'], - ['value' => '1232', 'label' => '074 - Nor Lípez'], - ['value' => '1233', 'label' => '075 - Rafael Bustillo'], - ['value' => '1234', 'label' => '076 - Sud Chichas'], - ['value' => '1235', 'label' => '077 - Sud Lípez'], - ['value' => '1236', 'label' => '078 - Tomás Frías'], - ['value' => '1237', 'label' => '079 - Aniceto Arce'], - ['value' => '1238', 'label' => '080 - Burdet O\'Connor'], - ['value' => '1239', 'label' => '081 - Cercado (Tarija)'], - ['value' => '1240', 'label' => '082 - Eustaquio Méndez'], - ['value' => '1241', 'label' => '083 - José María Avilés'], - ['value' => '1242', 'label' => '084 - Gran Chaco'], - ['value' => '1243', 'label' => '085 - Andrés Ibáñez'], - ['value' => '1244', 'label' => '086 - Caballero'], - ['value' => '1245', 'label' => '087 - Chiquitos'], - ['value' => '1246', 'label' => '088 - Cordillera (Bolivia)'], - ['value' => '1247', 'label' => '089 - Florida'], - ['value' => '1248', 'label' => '090 - Germán Busch'], - ['value' => '1249', 'label' => '091 - Guarayos'], - ['value' => '1250', 'label' => '092 - Ichilo'], - ['value' => '1251', 'label' => '093 - Obispo Santistevan'], - ['value' => '1252', 'label' => '094 - Sara'], - ['value' => '1253', 'label' => '095 - Vallegrande'], - ['value' => '1254', 'label' => '096 - Velasco'], - ['value' => '1255', 'label' => '097 - Warnes'], - ['value' => '1256', 'label' => '098 - Ángel Sandóval'], - ['value' => '1257', 'label' => '099 - Ñuflo de Chaves'], - ['value' => '1258', 'label' => '100 - Cercado (Beni)'], - ['value' => '1259', 'label' => '101 - Iténez'], - ['value' => '1260', 'label' => '102 - Mamoré'], - ['value' => '1261', 'label' => '103 - Marbán'], - ['value' => '1262', 'label' => '104 - Moxos'], - ['value' => '1263', 'label' => '105 - Vaca Díez'], - ['value' => '1264', 'label' => '106 - Yacuma'], - ['value' => '1265', 'label' => '107 - General José Ballivián Segurola'], - ['value' => '1266', 'label' => '108 - Abuná'], - ['value' => '1267', 'label' => '109 - Madre de Dios'], - ['value' => '1268', 'label' => '110 - Manuripi'], - ['value' => '1269', 'label' => '111 - Nicolás Suárez'], - ['value' => '1270', 'label' => '112 - General Federico Román'], - ['value' => '150', 'label' => 'AC - Acre'], - ['value' => '151', 'label' => 'AL - Alagoas'], - ['value' => '153', 'label' => 'AM - Amazonas'], - ['value' => '152', 'label' => 'AP - Amapá'], - ['value' => '154', 'label' => 'BA - Bahia'], - ['value' => '155', 'label' => 'CE - Ceará'], - ['value' => '176', 'label' => 'DF - Distrito Federal'], - ['value' => '156', 'label' => 'ES - Espirito Santo'], - ['value' => '157', 'label' => 'GO - Goiás'], - ['value' => '158', 'label' => 'MA - Maranhão'], - ['value' => '161', 'label' => 'MG - Minas Gerais'], - ['value' => '160', 'label' => 'MS - Mato Grosso do Sul'], - ['value' => '159', 'label' => 'MT - Mato Grosso'], - ['value' => '162', 'label' => 'PA - Pará'], - ['value' => '163', 'label' => 'PB - Paraiba'], - ['value' => '165', 'label' => 'PE - Pernambuco'], - ['value' => '166', 'label' => 'PI - Piauí'], - ['value' => '164', 'label' => 'PR - Paraná'], - ['value' => '167', 'label' => 'RJ - Rio de Janeiro'], - ['value' => '168', 'label' => 'RN - Rio Grande do Norte'], - ['value' => '170', 'label' => 'RO - Rondônia'], - ['value' => '171', 'label' => 'RR - Roraima'], - ['value' => '169', 'label' => 'RS - Rio Grande do Sul'], - ['value' => '172', 'label' => 'SC - Santa Catarina'], - ['value' => '173', 'label' => 'SE - Sergipe'], - ['value' => '174', 'label' => 'SP - Sao Paulo'], - ['value' => '175', 'label' => 'TO - Tocantins'], - ['value' => '185', 'label' => 'AB - Alberta'], - ['value' => '182', 'label' => 'BC - British Columbia'], - ['value' => '181', 'label' => 'MB - Manitoba'], - ['value' => '180', 'label' => 'NB - New Brunswick'], - ['value' => '186', 'label' => 'NL - Newfoundland and Labrador'], - ['value' => '179', 'label' => 'NS - Nova Scotia'], - ['value' => '177', 'label' => 'ON - Ontario'], - ['value' => '183', 'label' => 'PE - Prince Edward Island'], - ['value' => '178', 'label' => 'QC - Quebec'], - ['value' => '184', 'label' => 'SK - Saskatchewan'], - ['value' => '1028', 'label' => 'AG - Argovie'], - ['value' => '1029', 'label' => 'AI - Appenzell Rhodes intérieures'], - ['value' => '1030', 'label' => 'AR - Appenzell Rhodes extérieures'], - ['value' => '1031', 'label' => 'BE - Berne'], - ['value' => '1032', 'label' => 'BL - Bâle Campagne'], - ['value' => '1033', 'label' => 'BS - Bâle Ville'], - ['value' => '1034', 'label' => 'FR - Fribourg'], - ['value' => '1035', 'label' => 'GE - Genève'], - ['value' => '1036', 'label' => 'GL - Glaris'], - ['value' => '1037', 'label' => 'GR - Grisons'], - ['value' => '1038', 'label' => 'JU - Jura'], - ['value' => '1039', 'label' => 'LU - Lucerne'], - ['value' => '1040', 'label' => 'NE - Neuchâtel'], - ['value' => '1041', 'label' => 'NW - Nidwald'], - ['value' => '1042', 'label' => 'OW - Obwald'], - ['value' => '1043', 'label' => 'SG - Saint-Gall'], - ['value' => '1044', 'label' => 'SH - Schaffhouse'], - ['value' => '1045', 'label' => 'SO - Soleure'], - ['value' => '1046', 'label' => 'SZ - Schwyz'], - ['value' => '1047', 'label' => 'TG - Thurgovie'], - ['value' => '1048', 'label' => 'TI - Tessin'], - ['value' => '1049', 'label' => 'UR - Uri'], - ['value' => '1050', 'label' => 'VD - Vaud'], - ['value' => '1051', 'label' => 'VS - Valais'], - ['value' => '1052', 'label' => 'ZG - Zug'], - ['value' => '1053', 'label' => 'ZH - Zürich'], - ['value' => '187', 'label' => '011 - Iquique'], - ['value' => '188', 'label' => '014 - Tamarugal'], - ['value' => '189', 'label' => '021 - Antofagasa'], - ['value' => '190', 'label' => '022 - El Loa'], - ['value' => '191', 'label' => '023 - Tocopilla'], - ['value' => '192', 'label' => '031 - Copiapó'], - ['value' => '193', 'label' => '032 - Chañaral'], - ['value' => '194', 'label' => '033 - Huasco'], - ['value' => '195', 'label' => '041 - Elqui'], - ['value' => '196', 'label' => '042 - Choapa'], - ['value' => '197', 'label' => '043 - Limarí'], - ['value' => '198', 'label' => '051 - Valparaíso'], - ['value' => '199', 'label' => '052 - Isla de Pascua'], - ['value' => '200', 'label' => '053 - Los Andes'], - ['value' => '201', 'label' => '054 - Petorca'], - ['value' => '202', 'label' => '055 - Quillota'], - ['value' => '203', 'label' => '056 - San Antonio'], - ['value' => '204', 'label' => '057 - San Felipe de Aconcagua'], - ['value' => '205', 'label' => '058 - Marga Marga'], - ['value' => '206', 'label' => '061 - Cachapoal'], - ['value' => '207', 'label' => '062 - Cardenal Caro'], - ['value' => '208', 'label' => '063 - Colchagua'], - ['value' => '209', 'label' => '071 - Talca'], - ['value' => '210', 'label' => '072 - Cauquenes'], - ['value' => '211', 'label' => '073 - Curicó'], - ['value' => '212', 'label' => '074 - Linares'], - ['value' => '213', 'label' => '081 - Concepción'], - ['value' => '214', 'label' => '082 - Arauco'], - ['value' => '215', 'label' => '083 - Biobío'], - ['value' => '216', 'label' => '084 - Ñuble'], - ['value' => '217', 'label' => '091 - Cautín'], - ['value' => '218', 'label' => '092 - Malleco'], - ['value' => '219', 'label' => '101 - Llanquihue'], - ['value' => '220', 'label' => '102 - Chiloé'], - ['value' => '221', 'label' => '103 - Osorno'], - ['value' => '222', 'label' => '104 - Palena'], - ['value' => '223', 'label' => '111 - Coihaique'], - ['value' => '224', 'label' => '112 - Aisén'], - ['value' => '225', 'label' => '113 - Capitán Prat'], - ['value' => '226', 'label' => '114 - General Carrera'], - ['value' => '227', 'label' => '121 - Magallanes'], - ['value' => '228', 'label' => '122 - Antártica Chilena'], - ['value' => '229', 'label' => '123 - Tierra del Fuego'], - ['value' => '230', 'label' => '124 - Última Esperanza'], - ['value' => '231', 'label' => '131 - Santiago'], - ['value' => '232', 'label' => '132 - Cordillera'], - ['value' => '233', 'label' => '133 - Chacabuco'], - ['value' => '234', 'label' => '134 - Maipo'], - ['value' => '235', 'label' => '135 - Melipilla'], - ['value' => '236', 'label' => '136 - Talagante'], - ['value' => '237', 'label' => '141 - Valdivia'], - ['value' => '238', 'label' => '142 - Ranco'], - ['value' => '239', 'label' => '151 - Arica'], - ['value' => '240', 'label' => '152 - Parinacota'], - ['value' => '262', 'label' => 'AMA - Amazonas'], - ['value' => '241', 'label' => 'ANT - Antioquia'], - ['value' => '260', 'label' => 'ARA - Arauca'], - ['value' => '257', 'label' => 'ATL - Atlántico'], - ['value' => '270', 'label' => 'BOG - Bogotá'], - ['value' => '242', 'label' => 'BOL - Bolívar'], - ['value' => '243', 'label' => 'BOY - Boyacá'], - ['value' => '244', 'label' => 'CAL - Caldas'], - ['value' => '263', 'label' => 'CAQ - Caquetá'], - ['value' => '261', 'label' => 'CAS - Casanare'], - ['value' => '245', 'label' => 'CAU - Cauca'], - ['value' => '272', 'label' => 'CES - Cesar'], - ['value' => '264', 'label' => 'CHO - Chocó'], - ['value' => '258', 'label' => 'COR - Córdoba'], - ['value' => '246', 'label' => 'CUN - Cundinamarca'], - ['value' => '265', 'label' => 'GUA - Guainía'], - ['value' => '266', 'label' => 'GUV - Guaviare'], - ['value' => '247', 'label' => 'HUI - Huila'], - ['value' => '248', 'label' => 'LAG - La Guajira'], - ['value' => '273', 'label' => 'MAG - Magdalena'], - ['value' => '249', 'label' => 'MET - Meta'], - ['value' => '250', 'label' => 'NAR - Nariño'], - ['value' => '251', 'label' => 'NDS - Norte de Santander'], - ['value' => '267', 'label' => 'PUT - Putumayo'], - ['value' => '268', 'label' => 'QUI - Quindío'], - ['value' => '256', 'label' => 'RIS - Risalda'], - ['value' => '252', 'label' => 'SAN - Santander'], - [ - 'value' => '259', - 'label' => 'SAP - San Andrés, Providencia y Santa Catalina', - ], - ['value' => '253', 'label' => 'SUC - Sucre'], - ['value' => '254', 'label' => 'TOL - Tolima'], - ['value' => '255', 'label' => 'VAC - Valle del Cauca'], - ['value' => '269', 'label' => 'VAU - Vaupés'], - ['value' => '271', 'label' => 'VID - Vichada'], - ['value' => '399', 'label' => 'BB - Brandenburg'], - ['value' => '398', 'label' => 'BE - Berlin'], - ['value' => '396', 'label' => 'BW - Baden-Württemberg'], - ['value' => '397', 'label' => 'BY - Bayern'], - ['value' => '400', 'label' => 'HB - Bremen'], - ['value' => '402', 'label' => 'HE - Hessen'], - ['value' => '401', 'label' => 'HH - Hamburg'], - ['value' => '403', 'label' => 'MV - Mecklenburg-Vorpommern'], - ['value' => '404', 'label' => 'NI - Niedersachsen'], - ['value' => '405', 'label' => 'NW - Nordrhein-Westfalen'], - ['value' => '406', 'label' => 'RP - Rheinland-Pfalz'], - ['value' => '410', 'label' => 'SH - Schleswig-Holstein'], - ['value' => '407', 'label' => 'SL - Saarland'], - ['value' => '408', 'label' => 'SN - Sachsen'], - ['value' => '409', 'label' => 'ST - Sachsen-Anhalt'], - ['value' => '411', 'label' => 'TH - Thüringen'], - ['value' => '2', 'label' => '01 - Adrar'], - ['value' => '3', 'label' => '02 - Chlef'], - ['value' => '4', 'label' => '03 - Laghouat'], - ['value' => '5', 'label' => '04 - Oum El Bouaghi'], - ['value' => '6', 'label' => '05 - Batna'], - ['value' => '7', 'label' => '06 - Béjaïa'], - ['value' => '8', 'label' => '07 - Biskra'], - ['value' => '9', 'label' => '08 - Béchar'], - ['value' => '10', 'label' => '09 - Blida'], - ['value' => '11', 'label' => '10 - Bouira'], - ['value' => '12', 'label' => '11 - Tamanrasset'], - ['value' => '13', 'label' => '12 - Tébessa'], - ['value' => '14', 'label' => '13 - Tlemcen'], - ['value' => '15', 'label' => '14 - Tiaret'], - ['value' => '16', 'label' => '15 - Tizi Ouzou'], - ['value' => '17', 'label' => '16 - Alger'], - ['value' => '18', 'label' => '17 - Djelfa'], - ['value' => '19', 'label' => '18 - Jijel'], - ['value' => '20', 'label' => '19 - Sétif'], - ['value' => '21', 'label' => '20 - Saïda'], - ['value' => '22', 'label' => '21 - Skikda'], - ['value' => '23', 'label' => '22 - Sidi Bel Abbès'], - ['value' => '24', 'label' => '23 - Annaba'], - ['value' => '25', 'label' => '24 - Guelma'], - ['value' => '26', 'label' => '25 - Constantine'], - ['value' => '27', 'label' => '26 - Médéa'], - ['value' => '28', 'label' => '27 - Mostaganem'], - ['value' => '29', 'label' => '28 - M\'Sila'], - ['value' => '30', 'label' => '29 - Mascara'], - ['value' => '31', 'label' => '30 - Ouargla'], - ['value' => '32', 'label' => '31 - Oran'], - ['value' => '33', 'label' => '32 - El Bayadh'], - ['value' => '34', 'label' => '33 - Illizi'], - ['value' => '35', 'label' => '34 - Bordj Bou Arreridj'], - ['value' => '36', 'label' => '35 - Boumerdès'], - ['value' => '37', 'label' => '36 - El Tarf'], - ['value' => '38', 'label' => '37 - Tindouf'], - ['value' => '39', 'label' => '38 - Tissemsilt'], - ['value' => '40', 'label' => '39 - El Oued'], - ['value' => '41', 'label' => '40 - Khenchela'], - ['value' => '42', 'label' => '41 - Souk Ahras'], - ['value' => '43', 'label' => '42 - Tipaza'], - ['value' => '44', 'label' => '43 - Mila'], - ['value' => '45', 'label' => '44 - Aïn Defla'], - ['value' => '46', 'label' => '45 - Naâma'], - ['value' => '47', 'label' => '46 - Aïn Témouchent'], - ['value' => '48', 'label' => '47 - Ghardaïa'], - ['value' => '49', 'label' => '48 - Relizane'], - ['value' => '50', 'label' => '49 - Timimoun'], - ['value' => '51', 'label' => '50 - Bordj Badji Mokhtar'], - ['value' => '52', 'label' => '51 - Ouled Djellal'], - ['value' => '53', 'label' => '52 - Béni Abbès'], - ['value' => '54', 'label' => '53 - In Salah'], - ['value' => '55', 'label' => '54 - In Guezzam'], - ['value' => '56', 'label' => '55 - Touggourt'], - ['value' => '57', 'label' => '56 - Djanet'], - ['value' => '58', 'label' => '57 - El M\'Ghair'], - ['value' => '59', 'label' => '58 - El Ménéa'], - ['value' => '1273', 'label' => 'A - Alacant'], - ['value' => '1272', 'label' => 'AB - Albacete'], - ['value' => '1274', 'label' => 'AL - Almería'], - ['value' => '1276', 'label' => 'AV - Ávila'], - ['value' => '1278', 'label' => 'B - Barcelona'], - ['value' => '1277', 'label' => 'BA - Badajoz'], - ['value' => '1320', 'label' => 'BI - Bizkaia'], - ['value' => '1279', 'label' => 'BU - Burgos'], - ['value' => '1296', 'label' => 'C - La Coruña'], - ['value' => '1281', 'label' => 'CA - Cádiz'], - ['value' => '1280', 'label' => 'CC - Cáceres'], - ['value' => '1284', 'label' => 'CE - Ceuta'], - ['value' => '1286', 'label' => 'CO - Córdoba'], - ['value' => '1285', 'label' => 'CR - Ciudad Real'], - ['value' => '1283', 'label' => 'CS - Castelló'], - ['value' => '1287', 'label' => 'CU - Cuenca'], - ['value' => '1298', 'label' => 'GC - Las Palmas'], - ['value' => '1288', 'label' => 'GI - Girona'], - ['value' => '1289', 'label' => 'GR - Granada'], - ['value' => '1290', 'label' => 'GU - Guadalajara'], - ['value' => '1292', 'label' => 'H - Huelva'], - ['value' => '1293', 'label' => 'HU - Huesca'], - ['value' => '1295', 'label' => 'J - Jaén'], - ['value' => '1300', 'label' => 'L - Lleida'], - ['value' => '1299', 'label' => 'LE - León'], - ['value' => '1297', 'label' => 'LO - La Rioja'], - ['value' => '1301', 'label' => 'LU - Lugo'], - ['value' => '1302', 'label' => 'M - Madrid'], - ['value' => '1303', 'label' => 'MA - Málaga'], - ['value' => '1304', 'label' => 'ML - Melilla'], - ['value' => '1305', 'label' => 'MU - Murcia'], - ['value' => '1306', 'label' => 'NA - Navarra'], - ['value' => '1275', 'label' => 'O - Asturias'], - ['value' => '1307', 'label' => 'OR - Orense'], - ['value' => '1308', 'label' => 'P - Palencia'], - ['value' => '1294', 'label' => 'PM - Illes Balears'], - ['value' => '1309', 'label' => 'PO - Pontevedra'], - ['value' => '1282', 'label' => 'S - Cantabria'], - ['value' => '1310', 'label' => 'SA - Salamanca'], - ['value' => '1313', 'label' => 'SE - Sevilla'], - ['value' => '1312', 'label' => 'SG - Segovia'], - ['value' => '1314', 'label' => 'SO - Soria'], - ['value' => '1291', 'label' => 'SS - Gipuzkoa'], - ['value' => '1315', 'label' => 'T - Tarragona'], - ['value' => '1316', 'label' => 'TE - Teruel'], - ['value' => '1311', 'label' => 'TF - Santa Cruz de Tenerife'], - ['value' => '1317', 'label' => 'TO - Toledo'], - ['value' => '1318', 'label' => 'V - València'], - ['value' => '1319', 'label' => 'VA - Valladolid'], - ['value' => '1271', 'label' => 'VI - Araba'], - ['value' => '1322', 'label' => 'Z - Zaragoza'], - ['value' => '1321', 'label' => 'ZA - Zamora'], - ['value' => '300', 'label' => '01 - Ain'], - ['value' => '301', 'label' => '02 - Aisne'], - ['value' => '302', 'label' => '03 - Allier'], - ['value' => '303', 'label' => '04 - Alpes-de-Haute-Provence'], - ['value' => '304', 'label' => '05 - Hautes-Alpes'], - ['value' => '305', 'label' => '06 - Alpes-Maritimes'], - ['value' => '306', 'label' => '07 - Ardèche'], - ['value' => '307', 'label' => '08 - Ardennes'], - ['value' => '308', 'label' => '09 - Ariège'], - ['value' => '309', 'label' => '10 - Aube'], - ['value' => '310', 'label' => '11 - Aude'], - ['value' => '311', 'label' => '12 - Aveyron'], - ['value' => '312', 'label' => '13 - Bouches-du-Rhône'], - ['value' => '313', 'label' => '14 - Calvados'], - ['value' => '314', 'label' => '15 - Cantal'], - ['value' => '315', 'label' => '16 - Charente'], - ['value' => '316', 'label' => '17 - Charente-Maritime'], - ['value' => '317', 'label' => '18 - Cher'], - ['value' => '318', 'label' => '19 - Corrèze'], - ['value' => '321', 'label' => '21 - Côte-d Or'], - ['value' => '322', 'label' => '22 - Côtes-d Armor'], - ['value' => '323', 'label' => '23 - Creuse'], - ['value' => '324', 'label' => '24 - Dordogne'], - ['value' => '325', 'label' => '25 - Doubs'], - ['value' => '326', 'label' => '26 - Drôme'], - ['value' => '327', 'label' => '27 - Eure'], - ['value' => '328', 'label' => '28 - Eure-et-Loir'], - ['value' => '329', 'label' => '29 - Finistère'], - ['value' => '319', 'label' => '2A - Corse-du-Sud'], - ['value' => '320', 'label' => '2B - Haute-Corse'], - ['value' => '330', 'label' => '30 - Gard'], - ['value' => '331', 'label' => '31 - Haute-Garonne'], - ['value' => '332', 'label' => '32 - Gers'], - ['value' => '333', 'label' => '33 - Gironde'], - ['value' => '334', 'label' => '34 - Hérault'], - ['value' => '335', 'label' => '35 - Ille-et-Vilaine'], - ['value' => '336', 'label' => '36 - Indre'], - ['value' => '337', 'label' => '37 - Indre-et-Loire'], - ['value' => '338', 'label' => '38 - Isère'], - ['value' => '339', 'label' => '39 - Jura'], - ['value' => '340', 'label' => '40 - Landes'], - ['value' => '341', 'label' => '41 - Loir-et-Cher'], - ['value' => '342', 'label' => '42 - Loire'], - ['value' => '343', 'label' => '43 - Haute-Loire'], - ['value' => '344', 'label' => '44 - Loire-Atlantique'], - ['value' => '345', 'label' => '45 - Loiret'], - ['value' => '346', 'label' => '46 - Lot'], - ['value' => '347', 'label' => '47 - Lot-et-Garonne'], - ['value' => '348', 'label' => '48 - Lozère'], - ['value' => '349', 'label' => '49 - Maine-et-Loire'], - ['value' => '350', 'label' => '50 - Manche'], - ['value' => '351', 'label' => '51 - Marne'], - ['value' => '352', 'label' => '52 - Haute-Marne'], - ['value' => '353', 'label' => '53 - Mayenne'], - ['value' => '354', 'label' => '54 - Meurthe-et-Moselle'], - ['value' => '355', 'label' => '55 - Meuse'], - ['value' => '356', 'label' => '56 - Morbihan'], - ['value' => '357', 'label' => '57 - Moselle'], - ['value' => '358', 'label' => '58 - Nièvre'], - ['value' => '359', 'label' => '59 - Nord'], - ['value' => '360', 'label' => '60 - Oise'], - ['value' => '361', 'label' => '61 - Orne'], - ['value' => '362', 'label' => '62 - Pas-de-Calais'], - ['value' => '363', 'label' => '63 - Puy-de-Dôme'], - ['value' => '364', 'label' => '64 - Pyrénées-Atlantiques'], - ['value' => '365', 'label' => '65 - Hautes-Pyrénées'], - ['value' => '366', 'label' => '66 - Pyrénées-Orientales'], - ['value' => '367', 'label' => '67 - Bas-Rhin'], - ['value' => '368', 'label' => '68 - Haut-Rhin'], - ['value' => '369', 'label' => '69 - Rhône'], - ['value' => '370', 'label' => '70 - Haute-Saône'], - ['value' => '371', 'label' => '71 - Saône-et-Loire'], - ['value' => '372', 'label' => '72 - Sarthe'], - ['value' => '373', 'label' => '73 - Savoie'], - ['value' => '374', 'label' => '74 - Haute-Savoie'], - ['value' => '375', 'label' => '75 - Paris'], - ['value' => '376', 'label' => '76 - Seine-Maritime'], - ['value' => '377', 'label' => '77 - Seine-et-Marne'], - ['value' => '378', 'label' => '78 - Yvelines'], - ['value' => '379', 'label' => '79 - Deux-Sèvres'], - ['value' => '380', 'label' => '80 - Somme'], - ['value' => '381', 'label' => '81 - Tarn'], - ['value' => '382', 'label' => '82 - Tarn-et-Garonne'], - ['value' => '383', 'label' => '83 - Var'], - ['value' => '384', 'label' => '84 - Vaucluse'], - ['value' => '385', 'label' => '85 - Vendée'], - ['value' => '386', 'label' => '86 - Vienne'], - ['value' => '387', 'label' => '87 - Haute-Vienne'], - ['value' => '388', 'label' => '88 - Vosges'], - ['value' => '389', 'label' => '89 - Yonne'], - ['value' => '390', 'label' => '90 - Territoire de Belfort'], - ['value' => '391', 'label' => '91 - Essonne'], - ['value' => '392', 'label' => '92 - Hauts-de-Seine'], - ['value' => '393', 'label' => '93 - Seine-Saint-Denis'], - ['value' => '394', 'label' => '94 - Val-de-Marne'], - ['value' => '395', 'label' => '95 - Val-d Oise'], - ['value' => '295', 'label' => '971 - Guadeloupe'], - ['value' => '296', 'label' => '972 - Martinique'], - ['value' => '297', 'label' => '973 - Guyane'], - ['value' => '298', 'label' => '974 - Réunion'], - ['value' => '299', 'label' => '976 - Mayotte'], - ['value' => '1323', 'label' => '701 - Bedfordshire'], - ['value' => '1324', 'label' => '702 - Berkshire'], - ['value' => '1325', 'label' => '703 - Bristol, City of'], - ['value' => '1326', 'label' => '704 - Buckinghamshire'], - ['value' => '1327', 'label' => '705 - Cambridgeshire'], - ['value' => '1328', 'label' => '706 - Cheshire'], - ['value' => '1329', 'label' => '707 - Cleveland'], - ['value' => '1330', 'label' => '708 - Cornwall'], - ['value' => '1331', 'label' => '709 - Cumberland'], - ['value' => '1332', 'label' => '710 - Cumbria'], - ['value' => '1333', 'label' => '711 - Derbyshire'], - ['value' => '1334', 'label' => '712 - Devon'], - ['value' => '1335', 'label' => '713 - Dorset'], - ['value' => '1336', 'label' => '714 - Co. Durham'], - ['value' => '1337', 'label' => '715 - East Riding of Yorkshire'], - ['value' => '1338', 'label' => '716 - East Sussex'], - ['value' => '1339', 'label' => '717 - Essex'], - ['value' => '1340', 'label' => '718 - Gloucestershire'], - ['value' => '1341', 'label' => '719 - Greater Manchester'], - ['value' => '1342', 'label' => '720 - Hampshire'], - ['value' => '1343', 'label' => '721 - Hertfordshire'], - ['value' => '1344', 'label' => '722 - Hereford and Worcester'], - ['value' => '1345', 'label' => '723 - Herefordshire'], - ['value' => '1346', 'label' => '724 - Huntingdonshire'], - ['value' => '1347', 'label' => '725 - Isle of Man'], - ['value' => '1348', 'label' => '726 - Isle of Wight'], - ['value' => '1349', 'label' => '727 - Jersey'], - ['value' => '1350', 'label' => '728 - Kent'], - ['value' => '1351', 'label' => '729 - Lancashire'], - ['value' => '1352', 'label' => '730 - Leicestershire'], - ['value' => '1353', 'label' => '731 - Lincolnshire'], - ['value' => '1354', 'label' => '732 - London - City of London'], - ['value' => '1355', 'label' => '733 - Merseyside'], - ['value' => '1356', 'label' => '734 - Middlesex'], - ['value' => '1357', 'label' => '735 - Norfolk'], - ['value' => '1358', 'label' => '736 - North Yorkshire'], - ['value' => '1359', 'label' => '737 - North Riding of Yorkshire'], - ['value' => '1360', 'label' => '738 - Northamptonshire'], - ['value' => '1361', 'label' => '739 - Northumberland'], - ['value' => '1362', 'label' => '740 - Nottinghamshire'], - ['value' => '1363', 'label' => '741 - Oxfordshire'], - ['value' => '1364', 'label' => '742 - Rutland'], - ['value' => '1365', 'label' => '743 - Shropshire'], - ['value' => '1366', 'label' => '744 - Somerset'], - ['value' => '1367', 'label' => '745 - Staffordshire'], - ['value' => '1368', 'label' => '746 - Suffolk'], - ['value' => '1369', 'label' => '747 - Surrey'], - ['value' => '1370', 'label' => '748 - Sussex'], - ['value' => '1371', 'label' => '749 - Tyne and Wear'], - ['value' => '1372', 'label' => '750 - Warwickshire'], - ['value' => '1373', 'label' => '751 - West Midlands'], - ['value' => '1374', 'label' => '752 - West Sussex'], - ['value' => '1375', 'label' => '753 - West Yorkshire'], - ['value' => '1376', 'label' => '754 - West Riding of Yorkshire'], - ['value' => '1377', 'label' => '755 - Wiltshire'], - ['value' => '1378', 'label' => '756 - Worcestershire'], - ['value' => '1379', 'label' => '757 - Yorkshire'], - ['value' => '1380', 'label' => '758 - Anglesey'], - ['value' => '1381', 'label' => '759 - Breconshire'], - ['value' => '1382', 'label' => '760 - Caernarvonshire'], - ['value' => '1383', 'label' => '761 - Cardiganshire'], - ['value' => '1384', 'label' => '762 - Carmarthenshire'], - ['value' => '1385', 'label' => '763 - Ceredigion'], - ['value' => '1386', 'label' => '764 - Denbighshire'], - ['value' => '1387', 'label' => '765 - Flintshire'], - ['value' => '1388', 'label' => '766 - Glamorgan'], - ['value' => '1389', 'label' => '767 - Gwent'], - ['value' => '1390', 'label' => '768 - Gwynedd'], - ['value' => '1391', 'label' => '769 - Merionethshire'], - ['value' => '1392', 'label' => '770 - Monmouthshire'], - ['value' => '1393', 'label' => '771 - Mid Glamorgan'], - ['value' => '1394', 'label' => '772 - Montgomeryshire'], - ['value' => '1395', 'label' => '773 - Pembrokeshire'], - ['value' => '1396', 'label' => '774 - Powys'], - ['value' => '1397', 'label' => '775 - Radnorshire'], - ['value' => '1398', 'label' => '776 - South Glamorgan'], - ['value' => '1399', 'label' => '777 - Aberdeen, City of'], - ['value' => '1400', 'label' => '778 - Angus'], - ['value' => '1401', 'label' => '779 - Argyll'], - ['value' => '1402', 'label' => '780 - Ayrshire'], - ['value' => '1403', 'label' => '781 - Banffshire'], - ['value' => '1404', 'label' => '782 - Berwickshire'], - ['value' => '1405', 'label' => '783 - Bute'], - ['value' => '1406', 'label' => '784 - Caithness'], - ['value' => '1407', 'label' => '785 - Clackmannanshire'], - ['value' => '1408', 'label' => '786 - Dumfriesshire'], - ['value' => '1409', 'label' => '787 - Dumbartonshire'], - ['value' => '1410', 'label' => '788 - Dundee, City of'], - ['value' => '1411', 'label' => '789 - East Lothian'], - ['value' => '1412', 'label' => '790 - Fife'], - ['value' => '1413', 'label' => '791 - Inverness'], - ['value' => '1414', 'label' => '792 - Kincardineshire'], - ['value' => '1415', 'label' => '793 - Kinross-shire'], - ['value' => '1416', 'label' => '794 - Kirkcudbrightshire'], - ['value' => '1417', 'label' => '795 - Lanarkshire'], - ['value' => '1418', 'label' => '796 - Midlothian'], - ['value' => '1419', 'label' => '797 - Morayshire'], - ['value' => '1420', 'label' => '798 - Nairnshire'], - ['value' => '1421', 'label' => '799 - Orkney'], - ['value' => '1422', 'label' => '800 - Peebleshire'], - ['value' => '1423', 'label' => '801 - Perthshire'], - ['value' => '1424', 'label' => '802 - Renfrewshire'], - ['value' => '1425', 'label' => '803 - Ross & Cromarty'], - ['value' => '1426', 'label' => '804 - Roxburghshire'], - ['value' => '1427', 'label' => '805 - Selkirkshire'], - ['value' => '1428', 'label' => '806 - Shetland'], - ['value' => '1429', 'label' => '807 - Stirlingshire'], - ['value' => '1430', 'label' => '808 - Sutherland'], - ['value' => '1431', 'label' => '809 - West Lothian'], - ['value' => '1432', 'label' => '810 - Wigtownshire'], - ['value' => '1433', 'label' => '811 - Antrim'], - ['value' => '1434', 'label' => '812 - Armagh'], - ['value' => '1435', 'label' => '813 - Co. Down'], - ['value' => '1436', 'label' => '814 - Co. Fermanagh'], - ['value' => '1437', 'label' => '815 - Co. Londonderry'], - ['value' => '414', 'label' => '01 - Έβρος'], - ['value' => '415', 'label' => '02 - Θάσος'], - ['value' => '416', 'label' => '03 - Καβάλα'], - ['value' => '417', 'label' => '04 - Ξάνθη'], - ['value' => '418', 'label' => '05 - Ροδόπη'], - ['value' => '419', 'label' => '06 - Ημαθία'], - ['value' => '420', 'label' => '07 - Θεσσαλονίκη'], - ['value' => '421', 'label' => '08 - Κιλκίς'], - ['value' => '422', 'label' => '09 - Πέλλα'], - ['value' => '423', 'label' => '10 - Πιερία'], - ['value' => '424', 'label' => '11 - Σέρρες'], - ['value' => '425', 'label' => '12 - Χαλκιδική'], - ['value' => '426', 'label' => '13 - Άρτα'], - ['value' => '427', 'label' => '14 - Θεσπρωτία'], - ['value' => '428', 'label' => '15 - Ιωάννινα'], - ['value' => '429', 'label' => '16 - Πρέβεζα'], - ['value' => '430', 'label' => '17 - Γρεβενά'], - ['value' => '431', 'label' => '18 - Καστοριά'], - ['value' => '432', 'label' => '19 - Κοζάνη'], - ['value' => '433', 'label' => '20 - Φλώρινα'], - ['value' => '434', 'label' => '21 - Καρδίτσα'], - ['value' => '435', 'label' => '22 - Λάρισα'], - ['value' => '436', 'label' => '23 - Μαγνησία'], - ['value' => '437', 'label' => '24 - Τρίκαλα'], - ['value' => '438', 'label' => '25 - Σποράδες'], - ['value' => '439', 'label' => '26 - Βοιωτία'], - ['value' => '440', 'label' => '27 - Εύβοια'], - ['value' => '441', 'label' => '28 - Ευρυτανία'], - ['value' => '442', 'label' => '29 - Φθιώτιδα'], - ['value' => '443', 'label' => '30 - Φωκίδα'], - ['value' => '444', 'label' => '31 - Αργολίδα'], - ['value' => '445', 'label' => '32 - Αρκαδία'], - ['value' => '446', 'label' => '33 - Κορινθία'], - ['value' => '447', 'label' => '34 - Λακωνία'], - ['value' => '448', 'label' => '35 - Μεσσηνία'], - ['value' => '449', 'label' => '36 - Αιτωλοακαρνανία'], - ['value' => '450', 'label' => '37 - Αχαΐα'], - ['value' => '451', 'label' => '38 - Ηλεία'], - ['value' => '452', 'label' => '39 - Ζάκυνθος'], - ['value' => '453', 'label' => '40 - Κέρκυρα'], - ['value' => '454', 'label' => '41 - Κεφαλληνία'], - ['value' => '455', 'label' => '42 - Ιθάκη'], - ['value' => '456', 'label' => '43 - Λευκάδα'], - ['value' => '457', 'label' => '44 - Ικαρία'], - ['value' => '458', 'label' => '45 - Λέσβος'], - ['value' => '459', 'label' => '46 - Λήμνος'], - ['value' => '460', 'label' => '47 - Σάμος'], - ['value' => '461', 'label' => '48 - Χίος'], - ['value' => '462', 'label' => '49 - Άνδρος'], - ['value' => '463', 'label' => '50 - Θήρα'], - ['value' => '464', 'label' => '51 - Κάλυμνος'], - ['value' => '465', 'label' => '52 - Κάρπαθος'], - ['value' => '466', 'label' => '53 - Κέα-Κύθνος'], - ['value' => '467', 'label' => '54 - Κω'], - ['value' => '468', 'label' => '55 - Μήλος'], - ['value' => '469', 'label' => '56 - Μύκονος'], - ['value' => '470', 'label' => '57 - Νάξος'], - ['value' => '471', 'label' => '58 - Πάρος'], - ['value' => '472', 'label' => '59 - Ρόδος'], - ['value' => '473', 'label' => '60 - Σύρος'], - ['value' => '474', 'label' => '61 - Τήνος'], - ['value' => '475', 'label' => '62 - Ηράκλειο'], - ['value' => '476', 'label' => '63 - Λασίθι'], - ['value' => '477', 'label' => '64 - Ρέθυμνο'], - ['value' => '478', 'label' => '65 - Χανιά'], - ['value' => '412', 'label' => '66 - Αθήνα'], - ['value' => '413', 'label' => '67 - Δράμα'], - ['value' => '479', 'label' => 'AT - Atlántida'], - ['value' => '480', 'label' => 'CH - Choluteca'], - ['value' => '481', 'label' => 'CL - Colón'], - ['value' => '482', 'label' => 'CM - Comayagua'], - ['value' => '483', 'label' => 'CO - Copán'], - ['value' => '484', 'label' => 'CR - Cortés'], - ['value' => '497', 'label' => 'DC - Distrito Central'], - ['value' => '485', 'label' => 'EP - El Paraíso'], - ['value' => '486', 'label' => 'FM - Francisco Morazán'], - ['value' => '487', 'label' => 'GD - Gracias a Dios'], - ['value' => '489', 'label' => 'IB - Islas de la Bahía'], - ['value' => '488', 'label' => 'IN - Intibucá'], - ['value' => '491', 'label' => 'LM - Lempira'], - ['value' => '490', 'label' => 'LP - La Paz'], - ['value' => '492', 'label' => 'OC - Ocotepeque'], - ['value' => '493', 'label' => 'OL - Olancho'], - ['value' => '494', 'label' => 'SB - Santa Bárbara'], - ['value' => '495', 'label' => 'VL - Valle'], - ['value' => '496', 'label' => 'YO - Yoro'], - ['value' => '274', 'label' => 'HR-01 - Bjelovarsko-bilogorska županija'], - ['value' => '275', 'label' => 'HR-02 - Karlovačka županija'], - ['value' => '276', 'label' => 'HR-03 - Koprivničko-križevačka županija'], - ['value' => '277', 'label' => 'HR-04 - Krapinsko-zagorska županija'], - ['value' => '278', 'label' => 'HR-05 - Ličko-senjska županija'], - ['value' => '279', 'label' => 'HR-06 - Međimurska županija'], - ['value' => '280', 'label' => 'HR-07 - Primorsko-goranska županija'], - ['value' => '281', 'label' => 'HR-08 - Sisačko-moslavačka županija'], - ['value' => '282', 'label' => 'HR-09 - Varaždinska županija'], - ['value' => '283', 'label' => 'HR-10 - Zagrebačka županija'], - ['value' => '284', 'label' => 'HR-11 - Grad Zagreb'], - ['value' => '285', 'label' => 'HR-12 - Zadarska županija'], - ['value' => '286', 'label' => 'HR-13 - Šibensko-kninska županija'], - ['value' => '287', 'label' => 'HR-14 - Splitsko-dalmatinska županija'], - ['value' => '288', 'label' => 'HR-15 - Dubrovačko-neretvanska županija'], - ['value' => '289', 'label' => 'HR-16 - Brodsko-posavska županija'], - ['value' => '290', 'label' => 'HR-17 - Osječko-baranjska županija'], - ['value' => '291', 'label' => 'HR-18 - Požeško-slavonska županija'], - ['value' => '292', 'label' => 'HR-19 - Virovitičko-podravska županija'], - ['value' => '293', 'label' => 'HR-20 - Vukovarsko-srijemska županija'], - ['value' => '294', 'label' => 'HR-21 - Istarska županija'], - ['value' => '506', 'label' => 'HU-BA - Baranya'], - ['value' => '516', 'label' => 'HU-BE - Békés'], - ['value' => '515', 'label' => 'HU-BK - Bács-Kiskun'], - ['value' => '498', 'label' => 'HU-BU - Budapest'], - ['value' => '509', 'label' => 'HU-BZ - Borsod-Abaúj-Zemplén'], - ['value' => '517', 'label' => 'HU-CS - Csongrád'], - ['value' => '500', 'label' => 'HU-FE - Fejér'], - ['value' => '503', 'label' => 'HU-GS - Győr-Moson-Sopron'], - ['value' => '512', 'label' => 'HU-HB - Hajdú-Bihar'], - ['value' => '510', 'label' => 'HU-HE - Heves'], - ['value' => '513', 'label' => 'HU-JN - Jász-Nagykun-Szolnok'], - ['value' => '501', 'label' => 'HU-KE - Komárom-Esztergom'], - ['value' => '511', 'label' => 'HU-NO - Nógrád'], - ['value' => '499', 'label' => 'HU-PE - Pest'], - ['value' => '507', 'label' => 'HU-SO - Somogy'], - ['value' => '514', 'label' => 'HU-SZ - Szabolcs-Szatmár-Bereg'], - ['value' => '508', 'label' => 'HU-TO - Tolna'], - ['value' => '504', 'label' => 'HU-VA - Vas'], - ['value' => '502', 'label' => 'HU-VE - Veszprém'], - ['value' => '505', 'label' => 'HU-ZA - Zala'], - ['value' => '1494', 'label' => 'AC - Nanggroe Aceh Darussalam'], - ['value' => '1474', 'label' => 'BA - Bali'], - ['value' => '1475', 'label' => 'BB - Bangka Belitung'], - ['value' => '1477', 'label' => 'BE - Bengkulu'], - ['value' => '1476', 'label' => 'BT - Banten'], - ['value' => '1480', 'label' => 'GO - Gorontalo'], - ['value' => '1481', 'label' => 'JA - Jambi'], - ['value' => '1482', 'label' => 'JB - Jawa Barat'], - ['value' => '1484', 'label' => 'JI - Jawa Timur'], - ['value' => '1479', 'label' => 'JK - DKI Jakarta'], - ['value' => '1483', 'label' => 'JT - Jawa Tengah'], - ['value' => '1485', 'label' => 'KB - Kalimantan Barat'], - ['value' => '1488', 'label' => 'KI - Kalimantan Timur'], - ['value' => '1490', 'label' => 'KR - Kepulauan Riau'], - ['value' => '1486', 'label' => 'KS - Kalimantan Selatan'], - ['value' => '1487', 'label' => 'KT - Kalimantan Tengah'], - ['value' => '1489', 'label' => 'KU - Kalimantan Utara'], - ['value' => '1491', 'label' => 'LA - Lampung'], - ['value' => '1492', 'label' => 'MA - Maluku'], - ['value' => '1493', 'label' => 'MU - Maluku Utara'], - ['value' => '1495', 'label' => 'NB - Nusa Tenggara Barat'], - ['value' => '1496', 'label' => 'NT - Nusa Tenggara Timur'], - ['value' => '1497', 'label' => 'PA - Papua'], - ['value' => '1498', 'label' => 'PB - Papua Barat'], - ['value' => '1499', 'label' => 'RI - Riau'], - ['value' => '1504', 'label' => 'SA - Sulawesi Utara'], - ['value' => '1505', 'label' => 'SB - Sumatera Barat'], - ['value' => '1503', 'label' => 'SG - Sulawesi Tenggara'], - ['value' => '1501', 'label' => 'SN - Sulawesi Selatan'], - ['value' => '1500', 'label' => 'SR - Sulawesi Barat'], - ['value' => '1506', 'label' => 'SS - Sumatera Selatan'], - ['value' => '1502', 'label' => 'ST - Sulawesi Tengah'], - ['value' => '1507', 'label' => 'SU - Sumatera Utara '], - ['value' => '1478', 'label' => 'YO - DI Yogyakarta'], - ['value' => '1438', 'label' => 'AN - Andaman & Nicobar'], - ['value' => '1439', 'label' => 'AP - Andhra Pradesh'], - ['value' => '1440', 'label' => 'AR - Arunachal Pradesh'], - ['value' => '1441', 'label' => 'AS - Assam'], - ['value' => '1442', 'label' => 'BR - Bihar'], - ['value' => '1443', 'label' => 'CG - Chattisgarh'], - ['value' => '1444', 'label' => 'CH - Chandigarh'], - ['value' => '1445', 'label' => 'DD - Daman & Diu'], - ['value' => '1446', 'label' => 'DL - Delhi'], - ['value' => '1447', 'label' => 'DN - Dadra and Nagar Haveli'], - ['value' => '1448', 'label' => 'GA - Goa'], - ['value' => '1449', 'label' => 'GJ - Gujarat'], - ['value' => '1450', 'label' => 'HP - Himachal Pradesh'], - ['value' => '1451', 'label' => 'HR - Haryana'], - ['value' => '1452', 'label' => 'JH - Jharkhand'], - ['value' => '1453', 'label' => 'JK - Jammu & Kashmir'], - ['value' => '1454', 'label' => 'KA - Karnataka'], - ['value' => '1455', 'label' => 'KL - Kerala'], - ['value' => '1456', 'label' => 'LD - Lakshadweep'], - ['value' => '1457', 'label' => 'MH - Maharashtra'], - ['value' => '1458', 'label' => 'ML - Meghalaya'], - ['value' => '1459', 'label' => 'MN - Manipur'], - ['value' => '1460', 'label' => 'MP - Madhya Pradesh'], - ['value' => '1461', 'label' => 'MZ - Mizoram'], - ['value' => '1462', 'label' => 'NL - Nagaland'], - ['value' => '1463', 'label' => 'OR - Orissa'], - ['value' => '1464', 'label' => 'PB - Punjab'], - ['value' => '1465', 'label' => 'PY - Puducherry'], - ['value' => '1466', 'label' => 'RJ - Rajasthan'], - ['value' => '1467', 'label' => 'SK - Sikkim'], - ['value' => '1468', 'label' => 'TE - Telangana'], - ['value' => '1469', 'label' => 'TN - Tamil Nadu'], - ['value' => '1470', 'label' => 'TR - Tripura'], - ['value' => '1471', 'label' => 'UL - Uttarakhand'], - ['value' => '1472', 'label' => 'UP - Uttar Pradesh'], - ['value' => '1473', 'label' => 'WB - West Bengal'], - ['value' => '518', 'label' => 'AG - AGRIGENTO'], - ['value' => '519', 'label' => 'AL - ALESSANDRIA'], - ['value' => '520', 'label' => 'AN - ANCONA'], - ['value' => '521', 'label' => 'AO - AOSTA'], - ['value' => '523', 'label' => 'AP - ASCOLI PICENO'], - ['value' => '562', 'label' => 'AQ - L AQUILA'], - ['value' => '522', 'label' => 'AR - AREZZO'], - ['value' => '524', 'label' => 'AT - ASTI'], - ['value' => '525', 'label' => 'AV - AVELLINO'], - ['value' => '526', 'label' => 'BA - BARI'], - ['value' => '530', 'label' => 'BG - BERGAMO'], - ['value' => '531', 'label' => 'BI - BIELLA'], - ['value' => '528', 'label' => 'BL - BELLUNO'], - ['value' => '529', 'label' => 'BN - BENEVENTO'], - ['value' => '532', 'label' => 'BO - BOLOGNA'], - ['value' => '535', 'label' => 'BR - BRINDISI'], - ['value' => '534', 'label' => 'BS - BRESCIA'], - ['value' => '527', 'label' => 'BT - BARLETTA-ANDRIA-TRANI'], - ['value' => '533', 'label' => 'BZ - BOLZANO'], - ['value' => '536', 'label' => 'CA - CAGLIARI'], - ['value' => '538', 'label' => 'CB - CAMPOBASSO'], - ['value' => '540', 'label' => 'CE - CASERTA'], - ['value' => '543', 'label' => 'CH - CHIETI'], - ['value' => '539', 'label' => 'CI - CARBONIA-IGLESIAS'], - ['value' => '537', 'label' => 'CL - CALTANISSETTA'], - ['value' => '548', 'label' => 'CN - CUNEO'], - ['value' => '544', 'label' => 'CO - COMO'], - ['value' => '546', 'label' => 'CR - CREMONA'], - ['value' => '545', 'label' => 'CS - COSENZA'], - ['value' => '541', 'label' => 'CT - CATANIA'], - ['value' => '542', 'label' => 'CZ - CATANZARO'], - ['value' => '549', 'label' => 'EN - ENNA'], - ['value' => '554', 'label' => 'FC - FORLI-CESENA'], - ['value' => '551', 'label' => 'FE - FERRARA'], - ['value' => '553', 'label' => 'FG - FOGGIA'], - ['value' => '552', 'label' => 'FI - FIRENZE'], - ['value' => '550', 'label' => 'FM - FERMO'], - ['value' => '555', 'label' => 'FR - FROSINONE'], - ['value' => '556', 'label' => 'GE - GENOVA'], - ['value' => '557', 'label' => 'GO - GORIZIA'], - ['value' => '558', 'label' => 'GR - GROSSETO'], - ['value' => '559', 'label' => 'IM - Instant messaging'], - ['value' => '560', 'label' => 'IS - ISERNIA'], - ['value' => '547', 'label' => 'KR - CROTONE'], - ['value' => '565', 'label' => 'LC - LECCO'], - ['value' => '564', 'label' => 'LE - LECCE'], - ['value' => '566', 'label' => 'LI - LIVORNO'], - ['value' => '567', 'label' => 'LO - LODI'], - ['value' => '563', 'label' => 'LT - LATINA'], - ['value' => '568', 'label' => 'LU - LUCCA'], - ['value' => '576', 'label' => 'MB - MONZA e BRIANZA'], - ['value' => '569', 'label' => 'MC - MACERATA'], - ['value' => '574', 'label' => 'ME - MESSINA'], - ['value' => '575', 'label' => 'MI - MILANO'], - ['value' => '570', 'label' => 'MN - MANTOVA'], - ['value' => '577', 'label' => 'MO - MODENA'], - ['value' => '571', 'label' => 'MS - MASSA-CARRARA'], - ['value' => '572', 'label' => 'MT - MATERA'], - ['value' => '578', 'label' => 'NA - NAPOLI'], - ['value' => '579', 'label' => 'NO - NOVARA'], - ['value' => '580', 'label' => 'NU - NUORO'], - ['value' => '581', 'label' => 'OG - OGLIASTRA'], - ['value' => '583', 'label' => 'OR - ORISTANO'], - ['value' => '582', 'label' => 'OT - OLBIA-TEMPIO'], - ['value' => '585', 'label' => 'PA - PALERMO'], - ['value' => '591', 'label' => 'PC - PIACENZA'], - ['value' => '584', 'label' => 'PD - PADOVA'], - ['value' => '590', 'label' => 'PE - PESCARA'], - ['value' => '588', 'label' => 'PG - PERUGIA'], - ['value' => '592', 'label' => 'PI - PISA'], - ['value' => '594', 'label' => 'PN - PORDENONE'], - ['value' => '596', 'label' => 'PO - PRATO'], - ['value' => '586', 'label' => 'PR - PARMA'], - ['value' => '593', 'label' => 'PT - PISTOIA'], - ['value' => '589', 'label' => 'PU - PESARO e URBINO'], - ['value' => '587', 'label' => 'PV - PAVIA'], - ['value' => '595', 'label' => 'PZ - POTENZA'], - ['value' => '598', 'label' => 'RA - RAVENNA'], - ['value' => '599', 'label' => 'RC - REGGIO CALABRIA'], - ['value' => '600', 'label' => 'RE - REGGIO NELL EMILIA'], - ['value' => '597', 'label' => 'RG - RAGUSA'], - ['value' => '601', 'label' => 'RI - RIETI'], - ['value' => '603', 'label' => 'RM - ROMA'], - ['value' => '602', 'label' => 'RN - RIMINI'], - ['value' => '604', 'label' => 'RO - ROVIGO'], - ['value' => '605', 'label' => 'SA - SALERNO'], - ['value' => '608', 'label' => 'SI - SIENA'], - ['value' => '610', 'label' => 'SO - SONDRIO'], - ['value' => '561', 'label' => 'SP - LA SPEZIA'], - ['value' => '609', 'label' => 'SR - SIRACUSA'], - ['value' => '606', 'label' => 'SS - SASSARI'], - ['value' => '607', 'label' => 'SV - SAVONA'], - ['value' => '611', 'label' => 'TA - TARANTO'], - ['value' => '612', 'label' => 'TE - TERAMO'], - ['value' => '616', 'label' => 'TN - TRENTO'], - ['value' => '614', 'label' => 'TO - TORINO'], - ['value' => '615', 'label' => 'TP - TRAPANI'], - ['value' => '613', 'label' => 'TR - TERNI'], - ['value' => '618', 'label' => 'TS - TRIESTE'], - ['value' => '617', 'label' => 'TV - TREVISO'], - ['value' => '619', 'label' => 'UD - UDINE'], - ['value' => '620', 'label' => 'VA - VARESE'], - ['value' => '622', 'label' => 'VB - VERBANO-CUSIO-OSSOLA'], - ['value' => '623', 'label' => 'VC - VERCELLI'], - ['value' => '621', 'label' => 'VE - VENEZIA'], - ['value' => '626', 'label' => 'VI - VICENZA'], - ['value' => '624', 'label' => 'VR - VERONA'], - ['value' => '573', 'label' => 'VS - MEDIO CAMPIDANO'], - ['value' => '627', 'label' => 'VT - VITERBO'], - ['value' => '625', 'label' => 'VV - VIBO VALENTIA'], - ['value' => '1691', 'label' => '01 - 北海道'], - ['value' => '1692', 'label' => '02 - 青森県'], - ['value' => '1693', 'label' => '03 - 岩手県'], - ['value' => '1694', 'label' => '04 - 宮城県'], - ['value' => '1695', 'label' => '05 - 秋田県'], - ['value' => '1696', 'label' => '06 - 山形県'], - ['value' => '1697', 'label' => '07 - 福島県'], - ['value' => '1698', 'label' => '08 - 茨城県'], - ['value' => '1699', 'label' => '09 - 栃木県'], - ['value' => '1700', 'label' => '10 - 群馬県'], - ['value' => '1701', 'label' => '11 - 埼玉県'], - ['value' => '1702', 'label' => '12 - 千葉県'], - ['value' => '1703', 'label' => '13 - 東京都'], - ['value' => '1704', 'label' => '14 - 神奈川県'], - ['value' => '1705', 'label' => '15 - 新潟県'], - ['value' => '1706', 'label' => '16 - 富山県'], - ['value' => '1707', 'label' => '17 - 石川県'], - ['value' => '1708', 'label' => '18 - 福井県'], - ['value' => '1709', 'label' => '19 - 山梨県'], - ['value' => '1710', 'label' => '20 - 長野県'], - ['value' => '1711', 'label' => '21 - 岐阜県'], - ['value' => '1712', 'label' => '22 - 静岡県'], - ['value' => '1713', 'label' => '23 - 愛知県'], - ['value' => '1714', 'label' => '24 - 三重県'], - ['value' => '1715', 'label' => '25 - 滋賀県'], - ['value' => '1716', 'label' => '26 - 京都府'], - ['value' => '1717', 'label' => '27 - 大阪府'], - ['value' => '1718', 'label' => '28 - 兵庫県'], - ['value' => '1719', 'label' => '29 - 奈良県'], - ['value' => '1720', 'label' => '30 - 和歌山県'], - ['value' => '1721', 'label' => '31 - 鳥取県'], - ['value' => '1722', 'label' => '32 - 島根県'], - ['value' => '1723', 'label' => '33 - 岡山県'], - ['value' => '1724', 'label' => '34 - 広島県'], - ['value' => '1725', 'label' => '35 - 山口県'], - ['value' => '1726', 'label' => '36 - 徳島県'], - ['value' => '1727', 'label' => '37 - 香川県'], - ['value' => '1728', 'label' => '38 - 愛媛県'], - ['value' => '1729', 'label' => '39 - 高知県'], - ['value' => '1730', 'label' => '40 - 福岡県'], - ['value' => '1731', 'label' => '41 - 佐賀県'], - ['value' => '1732', 'label' => '42 - 長崎県'], - ['value' => '1733', 'label' => '43 - 熊本県'], - ['value' => '1734', 'label' => '44 - 大分県'], - ['value' => '1735', 'label' => '45 - 宮崎県'], - ['value' => '1736', 'label' => '46 - 鹿児島県'], - ['value' => '1737', 'label' => '47 - 沖縄県'], - ['value' => '628', 'label' => 'LU0001 - Clervaux'], - ['value' => '629', 'label' => 'LU0002 - Diekirch'], - ['value' => '630', 'label' => 'LU0003 - Redange'], - ['value' => '631', 'label' => 'LU0004 - Vianden'], - ['value' => '632', 'label' => 'LU0005 - Wiltz'], - ['value' => '633', 'label' => 'LU0006 - Echternach'], - ['value' => '634', 'label' => 'LU0007 - Grevenmacher'], - ['value' => '635', 'label' => 'LU0008 - Remich'], - ['value' => '636', 'label' => 'LU0009 - Capellen'], - ['value' => '637', 'label' => 'LU0010 - Esch-sur-Alzette'], - ['value' => '638', 'label' => 'LU0011 - Luxembourg'], - ['value' => '639', 'label' => 'LU0012 - Mersch'], - ['value' => '640', 'label' => 'MA - Province de Benslimane'], - ['value' => '641', 'label' => 'MA1 - Province de Berrechid'], - ['value' => '654', 'label' => 'MA10 - Province de Sidi Slimane'], - ['value' => '655', 'label' => 'MA11 - Préfecture de Casablanca'], - ['value' => '656', 'label' => 'MA12 - Préfecture de Mohammédia'], - ['value' => '657', 'label' => 'MA13 - Province de Médiouna'], - ['value' => '658', 'label' => 'MA14 - Province de Nouaceur'], - ['value' => '664', 'label' => 'MA15 - Province de Boujdour'], - ['value' => '659', 'label' => 'MA15 - Province d\'Assa-Zag'], - ['value' => '717', 'label' => 'MA15A - Province d\'Assa-Zag'], - ['value' => '665', 'label' => 'MA16 - Province de Lâayoune'], - ['value' => '660', 'label' => 'MA16 - Province d\'Es-Semara'], - ['value' => '718', 'label' => 'MA16A - Province d\'Es-Semara'], - ['value' => '666', 'label' => 'MA17 - Province de Tarfaya'], - ['value' => '661', 'label' => 'MA17A - Province de Guelmim'], - ['value' => '667', 'label' => 'MA18 - Préfecture de Marrakech'], - ['value' => '662', 'label' => 'MA18 - Province de Tata'], - ['value' => '719', 'label' => 'MA18A - Préfecture de Marrakech'], - ['value' => '663', 'label' => 'MA19 - Province de Tan-Tan'], - ['value' => '668', 'label' => 'MA19 - Province d\'Al Haouz'], - ['value' => '720', 'label' => 'MA19A - Province de Tan-Tan'], - ['value' => '721', 'label' => 'MA19B - Province de Tan-Tan'], - ['value' => '642', 'label' => 'MA2 - Province de Khouribga'], - ['value' => '669', 'label' => 'MA20 - Province de Chichaoua'], - ['value' => '670', 'label' => 'MA21 - Province d\'El Kelâa des Sraghna'], - ['value' => '671', 'label' => 'MA22 - Province d\'Essaouira'], - ['value' => '672', 'label' => 'MA23 - Province de Rehamna'], - ['value' => '673', 'label' => 'MA24 - Préfecture de Meknès'], - ['value' => '674', 'label' => 'MA25 - Province d’El Hajeb'], - ['value' => '675', 'label' => 'MA26 - Province d\'Errachidia'], - ['value' => '676', 'label' => 'MA27 - Province d’Ifrane'], - ['value' => '677', 'label' => 'MA28 - Province de Khénifra'], - ['value' => '678', 'label' => 'MA29 - Province de Midelt'], - ['value' => '643', 'label' => 'MA3 - Province de Settat'], - ['value' => '679', 'label' => 'MA30 - Préfecture d\'Oujda-Angad'], - ['value' => '680', 'label' => 'MA31 - Province de Berkane'], - ['value' => '681', 'label' => 'MA32 - Province de Driouch'], - ['value' => '682', 'label' => 'MA33 - Province de Figuig'], - ['value' => '683', 'label' => 'MA34 - Province de Jerada'], - ['value' => '684', 'label' => 'MA35 - Province de Nador'], - ['value' => '685', 'label' => 'MA36 - Province de Taourirt'], - ['value' => '686', 'label' => 'MA37 - Province d\'Aousserd'], - ['value' => '687', 'label' => 'MA38 - Province d\'Oued Ed-Dahab'], - ['value' => '688', 'label' => 'MA39 - Préfecture de Rabat'], - ['value' => '644', 'label' => 'MA4 - Province d\'El Jadida'], - ['value' => '689', 'label' => 'MA40 - Préfecture de Skhirat-Témara'], - ['value' => '690', 'label' => 'MA41 - Préfecture de Salé'], - ['value' => '691', 'label' => 'MA42 - Province de Khémisset'], - ['value' => '692', 'label' => 'MA43 - Préfecture d\'Agadir Ida-Outanane'], - ['value' => '693', 'label' => 'MA44 - Préfecture d\'Inezgane-Aït Melloul'], - ['value' => '694', 'label' => 'MA45 - Province de Chtouka-Aït Baha'], - ['value' => '695', 'label' => 'MA46 - Province d\'Ouarzazate'], - ['value' => '696', 'label' => 'MA47 - Province de Sidi Ifni'], - ['value' => '697', 'label' => 'MA48 - Province de Taroudant'], - ['value' => '698', 'label' => 'MA49 - Province de Tinghir'], - ['value' => '645', 'label' => 'MA5 - Province de Safi'], - ['value' => '699', 'label' => 'MA50 - Province de Tiznit'], - ['value' => '700', 'label' => 'MA51 - Province de Zagora'], - ['value' => '701', 'label' => 'MA52 - Province d\'Azilal'], - ['value' => '702', 'label' => 'MA53 - Province de Beni Mellal'], - ['value' => '703', 'label' => 'MA54 - Province de Fquih Ben Salah'], - ['value' => '704', 'label' => 'MA55 - Préfecture de M\'diq-Fnideq'], - ['value' => '705', 'label' => 'MA56 - Préfecture de Tanger-Asilah'], - ['value' => '706', 'label' => 'MA57 - Province de Chefchaouen'], - ['value' => '707', 'label' => 'MA58 - Province de Fahs-Anjra'], - ['value' => '708', 'label' => 'MA59 - Province de Larache'], - ['value' => '646', 'label' => 'MA6 - Province de Sidi Bennour'], - ['value' => '709', 'label' => 'MA60 - Province d\'Ouezzane'], - ['value' => '710', 'label' => 'MA61 - Province de Tétouan'], - ['value' => '711', 'label' => 'MA62 - Province de Guercif'], - ['value' => '712', 'label' => 'MA63 - Province d\'Al Hoceïma'], - ['value' => '713', 'label' => 'MA64 - Province de Taounate'], - ['value' => '714', 'label' => 'MA65 - Province de Taza'], - ['value' => '715', 'label' => 'MA6A - Préfecture de Fès'], - ['value' => '648', 'label' => 'MA6B - Préfecture de Fès'], - ['value' => '647', 'label' => 'MA7 - Province de Youssoufia'], - ['value' => '716', 'label' => 'MA7A - Province de Boulemane'], - ['value' => '649', 'label' => 'MA7B - Province de Boulemane'], - ['value' => '650', 'label' => 'MA8 - Province de Moulay Yacoub'], - ['value' => '652', 'label' => 'MA8A - Province de Kénitra'], - ['value' => '651', 'label' => 'MA9 - Province de Sefrou'], - ['value' => '653', 'label' => 'MA9A - Province de Sidi Kacem'], - ['value' => '1509', 'label' => 'AGS - Aguascalientes'], - ['value' => '1510', 'label' => 'BCN - Baja California Norte'], - ['value' => '1511', 'label' => 'BCS - Baja California Sur'], - ['value' => '1512', 'label' => 'CAM - Campeche'], - ['value' => '1514', 'label' => 'CHI - Chihuahua'], - ['value' => '1513', 'label' => 'CHP - Chiapas'], - ['value' => '1508', 'label' => 'CMX - Ciudad de México'], - ['value' => '1515', 'label' => 'COA - Coahuila'], - ['value' => '1516', 'label' => 'COL - Colima'], - ['value' => '1517', 'label' => 'DUR - Durango'], - ['value' => '1519', 'label' => 'GRO - Guerrero'], - ['value' => '1518', 'label' => 'GTO - Guanajuato'], - ['value' => '1520', 'label' => 'HGO - Hidalgo'], - ['value' => '1521', 'label' => 'JAL - Jalisco'], - ['value' => '1522', 'label' => 'MEX - México'], - ['value' => '1523', 'label' => 'MIC - Michoacán de Ocampo'], - ['value' => '1524', 'label' => 'MOR - Morelos'], - ['value' => '1525', 'label' => 'NAY - Nayarit'], - ['value' => '1526', 'label' => 'NLE - Nuevo León'], - ['value' => '1527', 'label' => 'OAX - Oaxaca'], - ['value' => '1528', 'label' => 'PUE - Puebla'], - ['value' => '1529', 'label' => 'QRO - Querétaro'], - ['value' => '1530', 'label' => 'ROO - Quintana Roo'], - ['value' => '1532', 'label' => 'SIN - Sinaloa'], - ['value' => '1531', 'label' => 'SLP - San Luis Potosí'], - ['value' => '1533', 'label' => 'SON - Sonora'], - ['value' => '1534', 'label' => 'TAB - Tabasco'], - ['value' => '1535', 'label' => 'TAM - Tamaulipas'], - ['value' => '1536', 'label' => 'TLX - Tlaxcala'], - ['value' => '1537', 'label' => 'VER - Veracruz'], - ['value' => '1538', 'label' => 'YUC - Yucatán'], - ['value' => '1539', 'label' => 'ZAC - Zacatecas'], - ['value' => '724', 'label' => 'DR - Drenthe'], - ['value' => '727', 'label' => 'FL - Flevoland'], - ['value' => '723', 'label' => 'FR - Friesland'], - ['value' => '726', 'label' => 'GD - Gelderland'], - ['value' => '722', 'label' => 'GR - Groningen'], - ['value' => '733', 'label' => 'LB - Limburg'], - ['value' => '732', 'label' => 'NB - Noord-Brabant'], - ['value' => '729', 'label' => 'NH - Noord-Holland'], - ['value' => '725', 'label' => 'OV - Overijssel'], - ['value' => '728', 'label' => 'UT - Utrecht'], - ['value' => '730', 'label' => 'ZH - Zuid-Holland'], - ['value' => '731', 'label' => 'ZL - Zeeland'], - ['value' => '734', 'label' => 'PA-1 - Bocas del Toro'], - ['value' => '743', 'label' => 'PA-13 - Panamá Oeste'], - ['value' => '735', 'label' => 'PA-2 - Coclé'], - ['value' => '736', 'label' => 'PA-3 - Colón'], - ['value' => '737', 'label' => 'PA-4 - Chiriquí'], - ['value' => '738', 'label' => 'PA-5 - Darién'], - ['value' => '739', 'label' => 'PA-6 - Herrera'], - ['value' => '740', 'label' => 'PA-7 - Los Santos'], - ['value' => '741', 'label' => 'PA-8 - Panamá'], - ['value' => '742', 'label' => 'PA-9 - Veraguas'], - ['value' => '744', 'label' => '0101 - Chachapoyas'], - ['value' => '745', 'label' => '0102 - Bagua'], - ['value' => '746', 'label' => '0103 - Bongará'], - ['value' => '747', 'label' => '0104 - Condorcanqui'], - ['value' => '748', 'label' => '0105 - Luya'], - ['value' => '749', 'label' => '0106 - Rodríguez de Mendoza'], - ['value' => '750', 'label' => '0107 - Utcubamba'], - ['value' => '751', 'label' => '0201 - Huaraz'], - ['value' => '752', 'label' => '0202 - Aija'], - ['value' => '753', 'label' => '0203 - Antonio Raymondi'], - ['value' => '754', 'label' => '0204 - Asunción'], - ['value' => '755', 'label' => '0205 - Bolognesi'], - ['value' => '756', 'label' => '0206 - Carhuaz'], - ['value' => '757', 'label' => '0207 - Carlos Fermín Fitzcarrald'], - ['value' => '758', 'label' => '0208 - Casma'], - ['value' => '759', 'label' => '0209 - Corongo'], - ['value' => '760', 'label' => '0210 - Huari'], - ['value' => '761', 'label' => '0211 - Huarmey'], - ['value' => '762', 'label' => '0212 - Huaylas'], - ['value' => '763', 'label' => '0213 - Mariscal Luzuriaga'], - ['value' => '764', 'label' => '0214 - Ocros'], - ['value' => '765', 'label' => '0215 - Pallasca'], - ['value' => '766', 'label' => '0216 - Pomabamba'], - ['value' => '767', 'label' => '0217 - Recuay'], - ['value' => '768', 'label' => '0218 - Papá'], - ['value' => '769', 'label' => '0219 - Sihuas'], - ['value' => '770', 'label' => '0220 - Yungay'], - ['value' => '771', 'label' => '0301 - Abancay'], - ['value' => '772', 'label' => '0302 - Andahuaylas'], - ['value' => '773', 'label' => '0303 - Antabamba'], - ['value' => '774', 'label' => '0304 - Aymaraes'], - ['value' => '775', 'label' => '0305 - Cotabambas'], - ['value' => '776', 'label' => '0306 - Chincheros'], - ['value' => '777', 'label' => '0307 - Grau'], - ['value' => '778', 'label' => '0401 - Arequipa'], - ['value' => '779', 'label' => '0402 - Camaná'], - ['value' => '780', 'label' => '0403 - Caravelí'], - ['value' => '781', 'label' => '0404 - Castilla'], - ['value' => '782', 'label' => '0405 - Caylloma'], - ['value' => '783', 'label' => '0406 - Condesuyos'], - ['value' => '784', 'label' => '0407 - Islay'], - ['value' => '785', 'label' => '0408 - La Unión'], - ['value' => '786', 'label' => '0501 - Huamanga'], - ['value' => '787', 'label' => '0502 - Cangallo'], - ['value' => '788', 'label' => '0503 - Huanca Sancos'], - ['value' => '789', 'label' => '0504 - Huanta'], - ['value' => '790', 'label' => '0505 - La Mar'], - ['value' => '791', 'label' => '0506 - Lucanas'], - ['value' => '792', 'label' => '0507 - Parinacochas'], - ['value' => '793', 'label' => '0508 - Páucar del Sara Sara'], - ['value' => '794', 'label' => '0509 - Sucre'], - ['value' => '795', 'label' => '0510 - Víctor Fajardo'], - ['value' => '796', 'label' => '0511 - Vilcas Huamán'], - ['value' => '797', 'label' => '0601 - Cajamarca'], - ['value' => '798', 'label' => '0602 - Cajabamba'], - ['value' => '799', 'label' => '0603 - Celendín'], - ['value' => '800', 'label' => '0604 - Chota'], - ['value' => '801', 'label' => '0605 - Contumazá'], - ['value' => '802', 'label' => '0606 - Cutervo'], - ['value' => '803', 'label' => '0607 - Hualgayoc'], - ['value' => '804', 'label' => '0608 - Jaén'], - ['value' => '805', 'label' => '0609 - San Ignacio'], - ['value' => '806', 'label' => '0610 - San Marcos'], - ['value' => '807', 'label' => '0611 - San Miguel'], - ['value' => '808', 'label' => '0612 - San Pablo'], - ['value' => '809', 'label' => '0613 - Santa Cruz'], - ['value' => '810', 'label' => '0701 - Callao'], - ['value' => '811', 'label' => '0801 - Cusco'], - ['value' => '812', 'label' => '0802 - Acomayo'], - ['value' => '813', 'label' => '0803 - Anta'], - ['value' => '814', 'label' => '0804 - Calca'], - ['value' => '815', 'label' => '0805 - Canas'], - ['value' => '816', 'label' => '0806 - Canchis'], - ['value' => '817', 'label' => '0807 - Chumbivilcas'], - ['value' => '818', 'label' => '0808 - Espinar'], - ['value' => '819', 'label' => '0809 - La Convención'], - ['value' => '820', 'label' => '0810 - Paruro'], - ['value' => '821', 'label' => '0811 - Paucartambo'], - ['value' => '822', 'label' => '0812 - Quispicanchi'], - ['value' => '823', 'label' => '0813 - Urubamba'], - ['value' => '824', 'label' => '0901 - Huancavelica'], - ['value' => '825', 'label' => '0902 - Acobamba'], - ['value' => '826', 'label' => '0903 - Angaraes'], - ['value' => '827', 'label' => '0904 - Castrovirreyna'], - ['value' => '828', 'label' => '0905 - Churcampa'], - ['value' => '829', 'label' => '0906 - Huaytará'], - ['value' => '830', 'label' => '0907 - Tayacaja'], - ['value' => '831', 'label' => '1001 - Huánuco'], - ['value' => '832', 'label' => '1002 - Ambón'], - ['value' => '833', 'label' => '1003 - Dos de Mayo'], - ['value' => '834', 'label' => '1004 - Huacaybamba'], - ['value' => '835', 'label' => '1005 - Huamalíes'], - ['value' => '836', 'label' => '1006 - Leoncio Prado'], - ['value' => '837', 'label' => '1007 - Marañón'], - ['value' => '838', 'label' => '1008 - Pachitea'], - ['value' => '839', 'label' => '1009 - Puerto Inca'], - ['value' => '840', 'label' => '1010 - Lauricocha'], - ['value' => '841', 'label' => '1011 - Yarowilca'], - ['value' => '842', 'label' => '1101 - Ica'], - ['value' => '843', 'label' => '1102 - Chincha'], - ['value' => '844', 'label' => '1103 - Nazca'], - ['value' => '845', 'label' => '1104 - Palpa'], - ['value' => '846', 'label' => '1105 - Pisco'], - ['value' => '847', 'label' => '1201 - Huancayo'], - ['value' => '848', 'label' => '1202 - Concepción'], - ['value' => '849', 'label' => '1203 - Chanchamayo'], - ['value' => '850', 'label' => '1204 - Jauja'], - ['value' => '851', 'label' => '1205 - Junín'], - ['value' => '852', 'label' => '1206 - Satipo'], - ['value' => '853', 'label' => '1207 - Tarma'], - ['value' => '854', 'label' => '1208 - Yauli'], - ['value' => '855', 'label' => '1209 - Chupaca'], - ['value' => '856', 'label' => '1301 - Trujillo'], - ['value' => '857', 'label' => '1302 - Ascope'], - ['value' => '858', 'label' => '1303 - Bolívar'], - ['value' => '859', 'label' => '1304 - Chepén'], - ['value' => '860', 'label' => '1305 - Julcán'], - ['value' => '861', 'label' => '1306 - Otuzco'], - ['value' => '862', 'label' => '1307 - Pacasmayo'], - ['value' => '863', 'label' => '1308 - Pataz'], - ['value' => '864', 'label' => '1309 - Sánchez Carrión'], - ['value' => '865', 'label' => '1310 - Santiago de Chuco'], - ['value' => '866', 'label' => '1311 - Gran Chimú'], - ['value' => '867', 'label' => '1312 - Virú'], - ['value' => '868', 'label' => '1401 - Chiclayo'], - ['value' => '869', 'label' => '1402 - Ferreñafe'], - ['value' => '870', 'label' => '1403 - Lambayeque'], - ['value' => '871', 'label' => '1501 - Lima'], - ['value' => '872', 'label' => '1502 - Huaura'], - ['value' => '873', 'label' => '1503 - Barranca'], - ['value' => '874', 'label' => '1504 - Cajatambo'], - ['value' => '875', 'label' => '1505 - Canta'], - ['value' => '876', 'label' => '1506 - Cañete'], - ['value' => '877', 'label' => '1507 - Huaral'], - ['value' => '878', 'label' => '1508 - Huarochirí'], - ['value' => '879', 'label' => '1509 - Oyón'], - ['value' => '880', 'label' => '1510 - Yauyos'], - ['value' => '881', 'label' => '1601 - Maynas'], - ['value' => '882', 'label' => '1602 - Alto Amazonas'], - ['value' => '883', 'label' => '1603 - Loreto'], - ['value' => '884', 'label' => '1604 - Mariscal Ramón Castilla'], - ['value' => '885', 'label' => '1605 - Requena'], - ['value' => '886', 'label' => '1606 - Ucayali'], - ['value' => '887', 'label' => '1607 - Datem del Marañón'], - ['value' => '888', 'label' => '1701 - Tambopata'], - ['value' => '889', 'label' => '1702 - Manú'], - ['value' => '890', 'label' => '1703 - Tahuamanu'], - ['value' => '891', 'label' => '1801 - Mariscal Nieto'], - ['value' => '892', 'label' => '1802 - General Sánchez Cerro'], - ['value' => '893', 'label' => '1803 - Ilo'], - ['value' => '894', 'label' => '1901 - Pasco'], - ['value' => '895', 'label' => '1902 - Daniel Alcides Carrión'], - ['value' => '896', 'label' => '1903 - Oxapampa'], - ['value' => '897', 'label' => '2001 - Piura'], - ['value' => '898', 'label' => '2002 - Ayabaca'], - ['value' => '899', 'label' => '2003 - Huancabamba'], - ['value' => '900', 'label' => '2004 - Morropón'], - ['value' => '901', 'label' => '2005 - Paita'], - ['value' => '902', 'label' => '2006 - Sullana'], - ['value' => '903', 'label' => '2007 - Talara'], - ['value' => '904', 'label' => '2008 - Sechura'], - ['value' => '905', 'label' => '2101 - Puno'], - ['value' => '906', 'label' => '2102 - Azángaro'], - ['value' => '907', 'label' => '2103 - Carabaya'], - ['value' => '908', 'label' => '2104 - Chucuito'], - ['value' => '909', 'label' => '2105 - El Collao'], - ['value' => '910', 'label' => '2106 - Huancané'], - ['value' => '911', 'label' => '2107 - Lampa'], - ['value' => '912', 'label' => '2108 - Melgar'], - ['value' => '913', 'label' => '2109 - Moho'], - ['value' => '914', 'label' => '2110 - San Antonio de Putina'], - ['value' => '915', 'label' => '2111 - San Román'], - ['value' => '916', 'label' => '2112 - Sandia'], - ['value' => '917', 'label' => '2113 - Yunguyo'], - ['value' => '918', 'label' => '2201 - Moyobamba'], - ['value' => '919', 'label' => '2202 - Bellavista'], - ['value' => '920', 'label' => '2203 - El Dorado'], - ['value' => '921', 'label' => '2204 - Huallaga'], - ['value' => '922', 'label' => '2205 - Lamas'], - ['value' => '923', 'label' => '2206 - Mariscal Cáceres'], - ['value' => '924', 'label' => '2207 - Picota'], - ['value' => '925', 'label' => '2208 - La Rioja'], - ['value' => '926', 'label' => '2209 - San Martín'], - ['value' => '927', 'label' => '2210 - Tocache'], - ['value' => '928', 'label' => '2301 - Tacna'], - ['value' => '929', 'label' => '2302 - Candarave'], - ['value' => '930', 'label' => '2303 - Jorge Basadre'], - ['value' => '931', 'label' => '2304 - Tarata'], - ['value' => '932', 'label' => '2401 - Tumbes'], - ['value' => '933', 'label' => '2402 - Contralmirante Villar'], - ['value' => '934', 'label' => '2403 - Zarumilla'], - ['value' => '935', 'label' => '2501 - Coronel Portillo'], - ['value' => '936', 'label' => '2502 - Atalaya'], - ['value' => '937', 'label' => '2503 - Padre Abad'], - ['value' => '938', 'label' => '2504 - Purús'], - ['value' => '940', 'label' => 'PT-AC - Azores'], - ['value' => '951', 'label' => 'PT-AML - Área Metropolitana de Lisboa'], - ['value' => '939', 'label' => 'PT-AV - Aveiro'], - ['value' => '943', 'label' => 'PT-BA - Bragança'], - ['value' => '941', 'label' => 'PT-BE - Beja'], - ['value' => '942', 'label' => 'PT-BR - Braga'], - ['value' => '944', 'label' => 'PT-CB - Castelo Branco'], - ['value' => '945', 'label' => 'PT-CO - Coimbra'], - ['value' => '946', 'label' => 'PT-EV - Évora'], - ['value' => '947', 'label' => 'PT-FA - Faro'], - ['value' => '948', 'label' => 'PT-GU - Guarda'], - ['value' => '949', 'label' => 'PT-LE - Leiria'], - ['value' => '950', 'label' => 'PT-LI - Lisboa'], - ['value' => '952', 'label' => 'PT-MA - Madeira'], - ['value' => '953', 'label' => 'PT-PA - Portalegre'], - ['value' => '954', 'label' => 'PT-PO - Porto'], - ['value' => '955', 'label' => 'PT-SA - Santarém'], - ['value' => '956', 'label' => 'PT-SE - Setúbal'], - ['value' => '957', 'label' => 'PT-VC - Viana Do Castelo'], - ['value' => '959', 'label' => 'PT-VI - Viseu'], - ['value' => '958', 'label' => 'PT-VR - Vila Real'], - ['value' => '960', 'label' => 'AB - Alba'], - ['value' => '962', 'label' => 'AG - Argeș'], - ['value' => '961', 'label' => 'AR - Arad'], - ['value' => '963', 'label' => 'BC - Bacău'], - ['value' => '964', 'label' => 'BH - Bihor'], - ['value' => '965', 'label' => 'BN - Bistrița-Năsăud'], - ['value' => '968', 'label' => 'BR - Brăila'], - ['value' => '966', 'label' => 'BT - Botoșani'], - ['value' => '969', 'label' => 'BU - Bucuresti'], - ['value' => '967', 'label' => 'BV - Brașov'], - ['value' => '970', 'label' => 'BZ - Buzău'], - ['value' => '973', 'label' => 'CJ - Cluj'], - ['value' => '971', 'label' => 'CL - Călărași'], - ['value' => '972', 'label' => 'CS - Caraș-Severin'], - ['value' => '974', 'label' => 'CT - Constanța'], - ['value' => '975', 'label' => 'CV - Covasna'], - ['value' => '976', 'label' => 'DB - Dâmbovița'], - ['value' => '977', 'label' => 'DJ - Dolj'], - ['value' => '980', 'label' => 'GJ - Gorj'], - ['value' => '978', 'label' => 'GL - Galați'], - ['value' => '979', 'label' => 'GR - Giurgiu'], - ['value' => '982', 'label' => 'HD - Hunedoara'], - ['value' => '981', 'label' => 'HR - Harghita'], - ['value' => '985', 'label' => 'IF - Ilfov'], - ['value' => '983', 'label' => 'IL - Ialomița'], - ['value' => '984', 'label' => 'IS - Iași'], - ['value' => '987', 'label' => 'MH - Mehedinți'], - ['value' => '986', 'label' => 'MM - Maramureș'], - ['value' => '988', 'label' => 'MS - Mureș'], - ['value' => '989', 'label' => 'NT - Neamț'], - ['value' => '990', 'label' => 'OT - Olt'], - ['value' => '991', 'label' => 'PH - Prahova'], - ['value' => '994', 'label' => 'SB - Sibiu'], - ['value' => '993', 'label' => 'SJ - Sălaj'], - ['value' => '992', 'label' => 'SM - Satu Mare'], - ['value' => '995', 'label' => 'SV - Suceava'], - ['value' => '998', 'label' => 'TL - Tulcea'], - ['value' => '997', 'label' => 'TM - Timiș'], - ['value' => '996', 'label' => 'TR - Teleorman'], - ['value' => '1000', 'label' => 'VL - Vâlcea'], - ['value' => '1001', 'label' => 'VN - Vrancea'], - ['value' => '999', 'label' => 'VS - Vaslui'], - ['value' => '1016', 'label' => 'SI031 - Mura'], - ['value' => '1017', 'label' => 'SI032 - Drava'], - ['value' => '1018', 'label' => 'SI033 - Carinthia'], - ['value' => '1019', 'label' => 'SI034 - Savinja'], - ['value' => '1020', 'label' => 'SI035 - Central Sava'], - ['value' => '1021', 'label' => 'SI036 - Lower Sava'], - ['value' => '1022', 'label' => 'SI037 - Southeast Slovenia'], - ['value' => '1023', 'label' => 'SI038 - Littoral–Inner Carniola'], - ['value' => '1025', 'label' => 'SI038 - Upper Carniola'], - ['value' => '1024', 'label' => 'SI041 - Central Slovenia'], - ['value' => '1026', 'label' => 'SI043 - Gorizia'], - ['value' => '1027', 'label' => 'SI044 - Coastal–Karst'], - ['value' => '1013', 'label' => 'AH - Ahuachapan'], - ['value' => '1005', 'label' => 'CA - Cabañas'], - ['value' => '1004', 'label' => 'CH - Chalatenango'], - ['value' => '1008', 'label' => 'CU - Cuscatlan'], - ['value' => '1003', 'label' => 'LL - La Libertad'], - ['value' => '1006', 'label' => 'LP - La Paz'], - ['value' => '1012', 'label' => 'LU - La Union'], - ['value' => '1011', 'label' => 'MO - Morazan'], - ['value' => '1014', 'label' => 'SA - Santa Ana'], - ['value' => '1010', 'label' => 'SM - San Miguel'], - ['value' => '1015', 'label' => 'SO - Sonsonate'], - ['value' => '1002', 'label' => 'SS - San Salvador'], - ['value' => '1007', 'label' => 'SV - San Vicente'], - ['value' => '1009', 'label' => 'US - Usulutan'], - ['value' => '1085', 'label' => 'TN01 - Ariana'], - ['value' => '1086', 'label' => 'TN02 - Béja'], - ['value' => '1087', 'label' => 'TN03 - Ben Arous'], - ['value' => '1088', 'label' => 'TN04 - Bizerte'], - ['value' => '1089', 'label' => 'TN05 - Gabès'], - ['value' => '1090', 'label' => 'TN06 - Gafsa'], - ['value' => '1091', 'label' => 'TN07 - Jendouba'], - ['value' => '1092', 'label' => 'TN08 - Kairouan'], - ['value' => '1093', 'label' => 'TN09 - Kasserine'], - ['value' => '1094', 'label' => 'TN10 - Kébili'], - ['value' => '1095', 'label' => 'TN11 - La Manouba'], - ['value' => '1096', 'label' => 'TN12 - Le Kef'], - ['value' => '1097', 'label' => 'TN13 - Mahdia'], - ['value' => '1098', 'label' => 'TN14 - Médenine'], - ['value' => '1099', 'label' => 'TN15 - Monastir'], - ['value' => '1100', 'label' => 'TN16 - Nabeul'], - ['value' => '1101', 'label' => 'TN17 - Sfax'], - ['value' => '1102', 'label' => 'TN18 - Sidi Bouzid'], - ['value' => '1103', 'label' => 'TN19 - Siliana'], - ['value' => '1104', 'label' => 'TN20 - Sousse'], - ['value' => '1105', 'label' => 'TN21 - Tataouine'], - ['value' => '1106', 'label' => 'TN22 - Tozeur'], - ['value' => '1107', 'label' => 'TN23 - Tunis'], - ['value' => '1108', 'label' => 'TN24 - Zaghouan'], - ['value' => '1738', 'label' => 'TR-01 - Adana'], - ['value' => '1739', 'label' => 'TR-02 - Adıyaman'], - ['value' => '1740', 'label' => 'TR-03 - Afyon'], - ['value' => '1741', 'label' => 'TR-04 - Ağrı'], - ['value' => '1742', 'label' => 'TR-05 - Amasya'], - ['value' => '1743', 'label' => 'TR-06 - Ankara'], - ['value' => '1744', 'label' => 'TR-07 - Antalya'], - ['value' => '1745', 'label' => 'TR-08 - Artvin'], - ['value' => '1746', 'label' => 'TR-09 - Aydın'], - ['value' => '1747', 'label' => 'TR-10 - Balıkesir'], - ['value' => '1748', 'label' => 'TR-11 - Bilecik'], - ['value' => '1749', 'label' => 'TR-12 - Bingöl'], - ['value' => '1750', 'label' => 'TR-13 - Bitlis'], - ['value' => '1751', 'label' => 'TR-14 - Bolu'], - ['value' => '1752', 'label' => 'TR-15 - Burdur'], - ['value' => '1753', 'label' => 'TR-16 - Bursa'], - ['value' => '1754', 'label' => 'TR-17 - Çanakkale'], - ['value' => '1755', 'label' => 'TR-18 - Çankırı'], - ['value' => '1756', 'label' => 'TR-19 - Çorum'], - ['value' => '1757', 'label' => 'TR-20 - Denizli'], - ['value' => '1758', 'label' => 'TR-21 - Diyarbakır'], - ['value' => '1759', 'label' => 'TR-22 - Edirne'], - ['value' => '1760', 'label' => 'TR-23 - Elazığ'], - ['value' => '1761', 'label' => 'TR-24 - Erzincan'], - ['value' => '1762', 'label' => 'TR-25 - Erzurum'], - ['value' => '1763', 'label' => 'TR-26 - Eskişehir'], - ['value' => '1764', 'label' => 'TR-27 - Gaziantep'], - ['value' => '1765', 'label' => 'TR-28 - Giresun'], - ['value' => '1766', 'label' => 'TR-29 - Gümüşhane'], - ['value' => '1767', 'label' => 'TR-30 - Hakkari'], - ['value' => '1768', 'label' => 'TR-31 - Hatay'], - ['value' => '1769', 'label' => 'TR-32 - Isparta'], - ['value' => '1770', 'label' => 'TR-33 - İçel'], - ['value' => '1771', 'label' => 'TR-34 - İstanbul'], - ['value' => '1772', 'label' => 'TR-35 - İzmir'], - ['value' => '1773', 'label' => 'TR-36 - Kars'], - ['value' => '1774', 'label' => 'TR-37 - Kastamonu'], - ['value' => '1775', 'label' => 'TR-38 - Kayseri'], - ['value' => '1776', 'label' => 'TR-39 - Kırklareli'], - ['value' => '1777', 'label' => 'TR-40 - Kırşehir'], - ['value' => '1778', 'label' => 'TR-41 - Kocaeli'], - ['value' => '1779', 'label' => 'TR-42 - Konya'], - ['value' => '1780', 'label' => 'TR-43 - Kütahya'], - ['value' => '1781', 'label' => 'TR-44 - Malatya'], - ['value' => '1782', 'label' => 'TR-45 - Manisa'], - ['value' => '1783', 'label' => 'TR-46 - Kahramanmaraş'], - ['value' => '1784', 'label' => 'TR-47 - Mardin'], - ['value' => '1785', 'label' => 'TR-48 - Muğla'], - ['value' => '1786', 'label' => 'TR-49 - Muş'], - ['value' => '1787', 'label' => 'TR-50 - Nevşehir'], - ['value' => '1788', 'label' => 'TR-51 - Niğde'], - ['value' => '1789', 'label' => 'TR-52 - Ordu'], - ['value' => '1790', 'label' => 'TR-53 - Rize'], - ['value' => '1791', 'label' => 'TR-54 - Sakarya'], - ['value' => '1792', 'label' => 'TR-55 - Samsun'], - ['value' => '1793', 'label' => 'TR-56 - Siirt'], - ['value' => '1794', 'label' => 'TR-57 - Sinop'], - ['value' => '1795', 'label' => 'TR-58 - Sivas'], - ['value' => '1796', 'label' => 'TR-59 - Tekirdağ'], - ['value' => '1797', 'label' => 'TR-60 - Tokat'], - ['value' => '1798', 'label' => 'TR-61 - Trabzon'], - ['value' => '1799', 'label' => 'TR-62 - Tunceli'], - ['value' => '1801', 'label' => 'TR-63 - Uşak'], - ['value' => '1800', 'label' => 'TR-63 - Şanlıurfa'], - ['value' => '1802', 'label' => 'TR-65 - Van'], - ['value' => '1803', 'label' => 'TR-66 - Yozgat'], - ['value' => '1804', 'label' => 'TR-67 - Zonguldak'], - ['value' => '1805', 'label' => 'TR-68 - Aksaray'], - ['value' => '1806', 'label' => 'TR-69 - Bayburt'], - ['value' => '1807', 'label' => 'TR-70 - Karaman'], - ['value' => '1808', 'label' => 'TR-71 - Kırıkkale'], - ['value' => '1809', 'label' => 'TR-72 - Batman'], - ['value' => '1810', 'label' => 'TR-73 - Şırnak'], - ['value' => '1811', 'label' => 'TR-74 - Bartın'], - ['value' => '1812', 'label' => 'TR-75 - Ardahan'], - ['value' => '1813', 'label' => 'TR-76 - Iğdır'], - ['value' => '1814', 'label' => 'TR-77 - Yalova'], - ['value' => '1815', 'label' => 'TR-78 - Karabük'], - ['value' => '1816', 'label' => 'TR-79 - Kilis'], - ['value' => '1817', 'label' => 'TR-80 - Osmaniye'], - ['value' => '1818', 'label' => 'TR-81 - Düzce'], - ['value' => '1068', 'label' => 'TW-CHY - 嘉義縣'], - ['value' => '1063', 'label' => 'TW-CWH - 彰化縣'], - ['value' => '1064', 'label' => 'TW-CWS - 彰化市'], - ['value' => '1069', 'label' => 'TW-CYI - 嘉義市'], - ['value' => '1081', 'label' => 'TW-GNI - 綠島'], - ['value' => '1059', 'label' => 'TW-HSC - 新竹市'], - ['value' => '1058', 'label' => 'TW-HSH - 新竹縣'], - ['value' => '1076', 'label' => 'TW-HWA - 花蓮縣'], - ['value' => '1077', 'label' => 'TW-HWC - 花蓮市'], - ['value' => '1075', 'label' => 'TW-ILC - 宜蘭市'], - ['value' => '1074', 'label' => 'TW-ILN - 宜蘭縣'], - ['value' => '1072', 'label' => 'TW-IUH - 屏東縣'], - ['value' => '1071', 'label' => 'TW-KHH - 高雄市'], - ['value' => '1054', 'label' => 'TW-KLU - 基隆市'], - ['value' => '1083', 'label' => 'TW-KMN - 金門縣'], - ['value' => '1082', 'label' => 'TW-KYD - 蘭嶼'], - ['value' => '1084', 'label' => 'TW-LNN - 連江縣'], - ['value' => '1061', 'label' => 'TW-MAC - 苗栗市'], - ['value' => '1060', 'label' => 'TW-MAL - 苗栗縣'], - ['value' => '1065', 'label' => 'TW-NTC - 南投市'], - ['value' => '1066', 'label' => 'TW-NTO - 南投縣'], - ['value' => '1080', 'label' => 'TW-PEH - 澎湖縣'], - ['value' => '1073', 'label' => 'TW-PTS - 屏東市'], - ['value' => '1070', 'label' => 'TW-TNN - 臺南市'], - ['value' => '1055', 'label' => 'TW-TPE - 臺北市'], - ['value' => '1056', 'label' => 'TW-TPH - 新北市'], - ['value' => '1078', 'label' => 'TW-TTC - 臺東市'], - ['value' => '1079', 'label' => 'TW-TTT - 臺東縣'], - ['value' => '1062', 'label' => 'TW-TXG - 臺中市'], - ['value' => '1057', 'label' => 'TW-TYC - 桃園市'], - ['value' => '1067', 'label' => 'TW-YLH - 雲林縣'], - ['value' => '1110', 'label' => 'AK - Alaska'], - ['value' => '1109', 'label' => 'AL - Alabama'], - ['value' => '1112', 'label' => 'AR - Arkansas'], - ['value' => '1111', 'label' => 'AZ - Arizona'], - ['value' => '1113', 'label' => 'CA - California'], - ['value' => '1114', 'label' => 'CO - Colorado'], - ['value' => '1115', 'label' => 'CT - Connecticut'], - ['value' => '1116', 'label' => 'DE - Delaware'], - ['value' => '1117', 'label' => 'FL - Florida'], - ['value' => '1118', 'label' => 'GA - Georgia'], - ['value' => '1119', 'label' => 'HI - Hawaii'], - ['value' => '1123', 'label' => 'IA - Iowa'], - ['value' => '1120', 'label' => 'ID - Idaho'], - ['value' => '1121', 'label' => 'IL - Illinois'], - ['value' => '1122', 'label' => 'IN - Indiana'], - ['value' => '1124', 'label' => 'KS - Kansas'], - ['value' => '1125', 'label' => 'KY - Kentucky'], - ['value' => '1126', 'label' => 'LA - Louisiana'], - ['value' => '1129', 'label' => 'MA - Massachusetts'], - ['value' => '1128', 'label' => 'MD - Maryland'], - ['value' => '1127', 'label' => 'ME - Maine'], - ['value' => '1130', 'label' => 'MI - Michigan'], - ['value' => '1131', 'label' => 'MN - Minnesota'], - ['value' => '1133', 'label' => 'MO - Missouri'], - ['value' => '1132', 'label' => 'MS - Mississippi'], - ['value' => '1134', 'label' => 'MT - Montana'], - ['value' => '1141', 'label' => 'NC - North Carolina'], - ['value' => '1142', 'label' => 'ND - North Dakota'], - ['value' => '1135', 'label' => 'NE - Nebraska'], - ['value' => '1137', 'label' => 'NH - New Hampshire'], - ['value' => '1138', 'label' => 'NJ - New Jersey'], - ['value' => '1139', 'label' => 'NM - New Mexico'], - ['value' => '1136', 'label' => 'NV - Nevada'], - ['value' => '1140', 'label' => 'NY - New York'], - ['value' => '1143', 'label' => 'OH - Ohio'], - ['value' => '1144', 'label' => 'OK - Oklahoma'], - ['value' => '1145', 'label' => 'OR - Oregon'], - ['value' => '1146', 'label' => 'PA - Pennsylvania'], - ['value' => '1147', 'label' => 'RI - Rhode Island'], - ['value' => '1148', 'label' => 'SC - South Carolina'], - ['value' => '1149', 'label' => 'SD - South Dakota'], - ['value' => '1150', 'label' => 'TN - Tennessee'], - ['value' => '1151', 'label' => 'TX - Texas'], - ['value' => '1152', 'label' => 'UT - Utah'], - ['value' => '1154', 'label' => 'VA - Virginia'], - ['value' => '1153', 'label' => 'VT - Vermont'], - ['value' => '1155', 'label' => 'WA - Washington'], - ['value' => '1157', 'label' => 'WI - Wisconsin'], - ['value' => '1156', 'label' => 'WV - West Virginia'], - ['value' => '1158', 'label' => 'WY - Wyoming'], - ['value' => '1545', 'label' => 'VE-A - Distrito Capital'], - ['value' => '1560', 'label' => 'VE-B - Anzoátegui'], - ['value' => '1556', 'label' => 'VE-C - Apure'], - ['value' => '1546', 'label' => 'VE-D - Aragua'], - ['value' => '1542', 'label' => 'VE-E - Barinas'], - ['value' => '1551', 'label' => 'VE-F - Bolívar'], - ['value' => '1547', 'label' => 'VE-G - Carabobo'], - ['value' => '1558', 'label' => 'VE-H - Cojedes'], - ['value' => '1548', 'label' => 'VE-I - Falcón'], - ['value' => '1557', 'label' => 'VE-J - Guárico'], - ['value' => '1549', 'label' => 'VE-K - Lara'], - ['value' => '1540', 'label' => 'VE-L - Mérida'], - ['value' => '1543', 'label' => 'VE-M - Miranda'], - ['value' => '1561', 'label' => 'VE-N - Monagas'], - ['value' => '1554', 'label' => 'VE-O - Nueva Esparta'], - ['value' => '1559', 'label' => 'VE-P - Portuguesa'], - ['value' => '1562', 'label' => 'VE-R - Sucre'], - ['value' => '1564', 'label' => 'VE-S - Táchira'], - ['value' => '1541', 'label' => 'VE-T - Trujillo'], - ['value' => '1550', 'label' => 'VE-U - Yaracuy'], - ['value' => '1563', 'label' => 'VE-V - Zulia'], - ['value' => '1544', 'label' => 'VE-W - Vargas'], - ['value' => '1552', 'label' => 'VE-X - Amazonas'], - ['value' => '1553', 'label' => 'VE-Y - Delta Amacuro'], - ['value' => '1555', 'label' => 'VE-Z - Dependencias Federales'], -]; diff --git a/addons/dolibarr/templates/appointments.php b/addons/dolibarr/templates/appointments.php deleted file mode 100644 index cb820bab..00000000 --- a/addons/dolibarr/templates/appointments.php +++ /dev/null @@ -1,258 +0,0 @@ - __('Appointments', 'forms-bridge'), - 'description' => __( - 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/agendaevents', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'userownerid', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __('Host user of the event', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/users', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].email', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'type_code', - 'label' => __('Event type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/setup/dictionary/event_types', - 'finger' => [ - 'value' => '[].code', - 'label' => '[].label', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'label', - 'label' => __('Event label', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web appointment', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'fulldayevent', - 'label' => __('Is all day event?', 'forms-bridge'), - 'type' => 'boolean', - 'default' => false, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'duration', - 'label' => __('Duration (Hours)', 'forms-bridge'), - 'type' => 'number', - 'default' => 1, - 'required' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Appointments', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'date', - 'label' => __('Date', 'forms-bridge'), - 'type' => 'date', - 'required' => true, - ], - [ - 'name' => 'hour', - 'label' => __('Hour', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - [ - 'label' => __('1 AM', 'forms-bridge'), - 'value' => '01', - ], - [ - 'label' => __('2 AM', 'forms-bridge'), - 'value' => '02', - ], - [ - 'label' => __('3 AM', 'forms-bridge'), - 'value' => '03', - ], - [ - 'label' => __('4 AM', 'forms-bridge'), - 'value' => '04', - ], - [ - 'label' => __('5 AM', 'forms-bridge'), - 'value' => '05', - ], - [ - 'label' => __('6 AM', 'forms-bridge'), - 'value' => '06', - ], - [ - 'label' => __('7 AM', 'forms-bridge'), - 'value' => '07', - ], - [ - 'label' => __('8 AM', 'forms-bridge'), - 'value' => '08', - ], - [ - 'label' => __('9 AM', 'forms-bridge'), - 'value' => '09', - ], - [ - 'label' => __('10 AM', 'forms-bridge'), - 'value' => '10', - ], - [ - 'label' => __('11 AM', 'forms-bridge'), - 'value' => '11', - ], - [ - 'label' => __('12 AM', 'forms-bridge'), - 'value' => '12', - ], - [ - 'label' => __('1 PM', 'forms-bridge'), - 'value' => '13', - ], - [ - 'label' => __('2 PM', 'forms-bridge'), - 'value' => '14', - ], - [ - 'label' => __('3 PM', 'forms-bridge'), - 'value' => '15', - ], - [ - 'label' => __('4 PM', 'forms-bridge'), - 'value' => '16', - ], - [ - 'label' => __('5 PM', 'forms-bridge'), - 'value' => '17', - ], - [ - 'label' => __('6 PM', 'forms-bridge'), - 'value' => '18', - ], - [ - 'label' => __('7 PM', 'forms-bridge'), - 'value' => '19', - ], - [ - 'label' => __('8 PM', 'forms-bridge'), - 'value' => '20', - ], - [ - 'label' => __('9 PM', 'forms-bridge'), - 'value' => '21', - ], - [ - 'label' => __('10 PM', 'forms-bridge'), - 'value' => '22', - ], - [ - 'label' => __('11 PM', 'forms-bridge'), - 'value' => '23', - ], - [ - 'label' => __('12 PM', 'forms-bridge'), - 'value' => '24', - ], - ], - ], - [ - 'name' => 'minute', - 'label' => __('Minute', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - ['label' => '00', 'value' => '00.0'], - ['label' => '05', 'value' => '05'], - ['label' => '10', 'value' => '10'], - ['label' => '15', 'value' => '15'], - ['label' => '20', 'value' => '20'], - ['label' => '25', 'value' => '25'], - ['label' => '30', 'value' => '30'], - ['label' => '35', 'value' => '35'], - ['label' => '40', 'value' => '40'], - ['label' => '45', 'value' => '45'], - ['label' => '50', 'value' => '50'], - ['label' => '55', 'value' => '55'], - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/agendaevents', - 'mutations' => [ - [ - [ - 'from' => '?duration', - 'to' => 'duration', - 'cast' => 'number', - ], - [ - 'from' => 'userownerid', - 'to' => 'userownerid', - 'cast' => 'integer', - ], - ], - [ - [ - 'from' => 'datetime', - 'to' => 'date', - 'cast' => 'string', - ], - ], - ], - 'workflow' => [ - 'date-fields-to-date', - 'appointment-dates', - 'appointment-attendee', - ], - ], -]; diff --git a/addons/dolibarr/templates/company-appointments.php b/addons/dolibarr/templates/company-appointments.php deleted file mode 100644 index 88b61fe5..00000000 --- a/addons/dolibarr/templates/company-appointments.php +++ /dev/null @@ -1,338 +0,0 @@ - __('Company Appointments', 'forms-bridge'), - 'description' => __( - 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/agendaevents', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'userownerid', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __('Host user of the event', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/users', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].email', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'type_code', - 'label' => __('Event type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/setup/dictionary/event_types', - 'finger' => [ - 'value' => '[].code', - 'label' => '[].label', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'label', - 'label' => __('Event label', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web appointment', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'fulldayevent', - 'label' => __('Is all day event?', 'forms-bridge'), - 'type' => 'boolean', - 'default' => false, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'duration', - 'label' => __('Duration (Hours)', 'forms-bridge'), - 'type' => 'number', - 'default' => 1, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'client', - 'label' => __('Thirdparty status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'value' => '1', - 'label' => __('Client', 'forms-bridge'), - ], - [ - 'value' => '2', - 'label' => __('Prospect', 'forms-bridge'), - ], - [ - 'value' => '3', - 'label' => __('Client/Prospect', 'forms-bridge'), - ], - [ - 'value' => '0', - 'label' => __( - 'Neither customer nor supplier', - 'forms-bridge' - ), - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Company type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Appointments', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'company_name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'contact_email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'poste', - 'label' => __('Job position', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'date', - 'label' => __('Date', 'forms-bridge'), - 'type' => 'date', - 'required' => true, - ], - [ - 'name' => 'hour', - 'label' => __('Hour', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - [ - 'label' => __('1 AM', 'forms-bridge'), - 'value' => '01', - ], - [ - 'label' => __('2 AM', 'forms-bridge'), - 'value' => '02', - ], - [ - 'label' => __('3 AM', 'forms-bridge'), - 'value' => '03', - ], - [ - 'label' => __('4 AM', 'forms-bridge'), - 'value' => '04', - ], - [ - 'label' => __('5 AM', 'forms-bridge'), - 'value' => '05', - ], - [ - 'label' => __('6 AM', 'forms-bridge'), - 'value' => '06', - ], - [ - 'label' => __('7 AM', 'forms-bridge'), - 'value' => '07', - ], - [ - 'label' => __('8 AM', 'forms-bridge'), - 'value' => '08', - ], - [ - 'label' => __('9 AM', 'forms-bridge'), - 'value' => '09', - ], - [ - 'label' => __('10 AM', 'forms-bridge'), - 'value' => '10', - ], - [ - 'label' => __('11 AM', 'forms-bridge'), - 'value' => '11', - ], - [ - 'label' => __('12 AM', 'forms-bridge'), - 'value' => '12', - ], - [ - 'label' => __('1 PM', 'forms-bridge'), - 'value' => '13', - ], - [ - 'label' => __('2 PM', 'forms-bridge'), - 'value' => '14', - ], - [ - 'label' => __('3 PM', 'forms-bridge'), - 'value' => '15', - ], - [ - 'label' => __('4 PM', 'forms-bridge'), - 'value' => '16', - ], - [ - 'label' => __('5 PM', 'forms-bridge'), - 'value' => '17', - ], - [ - 'label' => __('6 PM', 'forms-bridge'), - 'value' => '18', - ], - [ - 'label' => __('7 PM', 'forms-bridge'), - 'value' => '19', - ], - [ - 'label' => __('8 PM', 'forms-bridge'), - 'value' => '20', - ], - [ - 'label' => __('9 PM', 'forms-bridge'), - 'value' => '21', - ], - [ - 'label' => __('10 PM', 'forms-bridge'), - 'value' => '22', - ], - [ - 'label' => __('11 PM', 'forms-bridge'), - 'value' => '23', - ], - [ - 'label' => __('12 PM', 'forms-bridge'), - 'value' => '24', - ], - ], - ], - [ - 'name' => 'minute', - 'label' => __('Minute', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - ['label' => '00', 'value' => '00.0'], - ['label' => '05', 'value' => '05'], - ['label' => '10', 'value' => '10'], - ['label' => '15', 'value' => '15'], - ['label' => '20', 'value' => '20'], - ['label' => '25', 'value' => '25'], - ['label' => '30', 'value' => '30'], - ['label' => '35', 'value' => '35'], - ['label' => '40', 'value' => '40'], - ['label' => '45', 'value' => '45'], - ['label' => '50', 'value' => '50'], - ['label' => '55', 'value' => '55'], - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/agendaevents', - 'mutations' => [ - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?duration', - 'to' => 'duration', - 'cast' => 'number', - ], - ], - [ - [ - 'from' => 'datetime', - 'to' => 'date', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - ], - [], - ], - 'workflow' => [ - 'date-fields-to-date', - 'appointment-dates', - 'contact-socid', - 'appointment-attendee', - ], - ], -]; diff --git a/addons/dolibarr/templates/company-leads.php b/addons/dolibarr/templates/company-leads.php deleted file mode 100644 index fb461cc6..00000000 --- a/addons/dolibarr/templates/company-leads.php +++ /dev/null @@ -1,279 +0,0 @@ - __('Company Leads', 'forms-bridge'), - 'description' => __( - 'Leads form template. The resulting bridge will convert form submissions into company lead projects linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/projects', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'userownerid', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __('Owner user of the lead', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/users', - 'finger' => ['value' => '[].id', 'label' => '[].email'], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Company type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'stcomm_id', - 'label' => __('Prospect status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Never contacted', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('To contact', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Contact in progress', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Contacted', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Do not contact', 'forms-bridge'), - 'value' => '-1', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'opp_status', - 'label' => __('Lead status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Prospection', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Qualification', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Proposal', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Negociation', 'forms-bridge'), - 'value' => '4', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'opp_amount', - 'label' => __('Lead amount', 'forms-bridge'), - 'type' => 'number', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Leads', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Company Leads', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'company_name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'idprof1', - 'label' => __('Tax ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'address', - 'label' => __('Address', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'zip', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'town', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'poste', - 'label' => __('Job position', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'note_private', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/projects', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'client', - 'value' => '2', - ], - [ - 'name' => 'usage_opportunity', - 'value' => '1', - ], - [ - 'name' => 'date_start', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => '?userownerid', - 'to' => 'userid', - 'cast' => 'integer', - ], - ], - [], - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'title', - 'cast' => 'copy', - ], - [ - 'from' => 'email', - 'to' => 'contact_email', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'socid', - 'to' => 'lead_socid', - 'cast' => 'copy', - ], - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'lead_socid', - 'to' => 'socid', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'iso2-country-code', - 'country-id', - 'contact-socid', - 'contact-id', - 'next-project-ref', - ], - ], -]; diff --git a/addons/dolibarr/templates/company-proposals.php b/addons/dolibarr/templates/company-proposals.php deleted file mode 100644 index 9061622d..00000000 --- a/addons/dolibarr/templates/company-proposals.php +++ /dev/null @@ -1,248 +0,0 @@ - __('Company Proposals', 'forms-bridge'), - 'description' => __( - 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/proposals', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Thirdparty type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'stcomm_id', - 'label' => __('Prospect status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Never contacted', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('To contact', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Contact in progress', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Contacted', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Do not contact', 'forms-bridge'), - 'value' => '-1', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'fk_product', - 'label' => __('Product', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/products', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].label', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Proposals', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Company Proposals', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'quantity', - 'label' => __('Quantity', 'forms-bridge'), - 'type' => 'number', - 'required' => true, - 'default' => 1, - 'min' => 1, - ], - [ - 'name' => 'company_name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'tva_intra', - 'label' => __('Tax ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'address', - 'label' => __('Address', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'zip', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'town', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/proposals', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'client', - 'value' => '2', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - [ - 'name' => 'lines[0].product_type', - 'value' => '1', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'quantity', - 'to' => 'lines[0].qty', - 'cast' => 'integer', - ], - [ - 'from' => 'fk_product', - 'to' => 'lines[0].fk_product', - 'cast' => 'integer', - ], - ], - [], - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'contact_email', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'socid', - 'to' => 'order_socid', - 'cast' => 'copy', - ], - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'order_socid', - 'to' => 'socid', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'iso2-country-code', - 'country-id', - 'contact-socid', - 'contact-id', - ], - ], -]; diff --git a/addons/dolibarr/templates/company-prospects.php b/addons/dolibarr/templates/company-prospects.php deleted file mode 100644 index 749bcc26..00000000 --- a/addons/dolibarr/templates/company-prospects.php +++ /dev/null @@ -1,208 +0,0 @@ - __('Company Prospects', 'forms-bridge'), - 'description' => __( - 'Leads form template. The resulting bridge will convert form submissions into company prospects linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/contacts', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'stcomm_id', - 'label' => __('Prospect status', 'forms-bridge'), - 'required' => true, - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Never contacted', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('To contact', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Contact in progress', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Contacted', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Do not contact', 'forms-bridge'), - 'value' => '-1', - ], - ], - 'default' => '0', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Thirdparty type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Prospects', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Company Prospects', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'company_name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'idprof1', - 'label' => __('Tax ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'address', - 'label' => __('Address', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'zip', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'town', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'poste', - 'label' => __('Job position', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'note_private', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/contacts', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'client', - 'value' => '2', - ], - ], - 'mutations' => [ - [], - [], - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'contact_email', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - ], - ], - 'workflow' => [ - 'iso2-country-code', - 'country-id', - 'contact-socid', - 'skip-if-contact-exists', - ], - ], -]; diff --git a/addons/dolibarr/templates/contacts.php b/addons/dolibarr/templates/contacts.php deleted file mode 100644 index cb4aa3cc..00000000 --- a/addons/dolibarr/templates/contacts.php +++ /dev/null @@ -1,74 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/contacts', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'no_email', - 'label' => __('Subscrive to email', 'forms-bridge'), - 'type' => 'boolean', - 'default' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Contacts', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Contacts', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/contacts', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'no_email', - 'to' => 'no_email', - 'cast' => 'string', - ], - ], - ], - 'workflow' => ['skip-if-contact-exists'], - ], -]; diff --git a/addons/dolibarr/templates/customers.php b/addons/dolibarr/templates/customers.php deleted file mode 100644 index ee94ba26..00000000 --- a/addons/dolibarr/templates/customers.php +++ /dev/null @@ -1,174 +0,0 @@ - __('Customers', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into customers.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/contacts', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Customer type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Customers', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Customers', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'company_name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'tva_intra', - 'label' => __('Tax ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'address', - 'label' => __('Address', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'zip', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'town', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'contact_email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'poste', - 'label' => __('Job position', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'note_private', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/contacts', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'client', - 'value' => '1', - ], - ], - 'mutations' => [ - [], - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - [], - [ - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - ], - [], - ], - 'workflow' => [ - 'iso2-country-code', - 'country-id', - 'contact-socid', - 'skip-if-contact-exists', - ], - ], -]; diff --git a/addons/dolibarr/templates/individual-leads.php b/addons/dolibarr/templates/individual-leads.php deleted file mode 100644 index d2840665..00000000 --- a/addons/dolibarr/templates/individual-leads.php +++ /dev/null @@ -1,187 +0,0 @@ - __('Leads', 'forms-bridge'), - 'description' => __( - 'Lead form template. The resulting bridge will convert form submissions into lead projects linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/projects', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'userownerid', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __('Owner user of the lead', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/users', - 'finger' => ['value' => '[].id', 'label' => '[].email'], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'stcomm_id', - 'label' => __('Prospect status', 'forms-bridge'), - 'required' => true, - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Never contacted', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('To contact', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Contact in progress', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Contacted', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Do not contact', 'forms-bridge'), - 'value' => '-1', - ], - ], - 'default' => '0', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'opp_status', - 'label' => __('Lead status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Prospection', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Qualification', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Proposal', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Negociation', 'forms-bridge'), - 'value' => '4', - ], - ], - 'required' => true, - 'default' => '1', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'opp_amount', - 'label' => __('Lead amount', 'forms-bridge'), - 'type' => 'number', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Leads', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Leads', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'note_private', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/projects', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'typent_id', - 'value' => '8', - ], - [ - 'name' => 'client', - 'value' => '2', - ], - [ - 'name' => 'usage_opportunity', - 'value' => '1', - ], - [ - 'name' => 'date_start', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'firstname', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => 'lastname', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => 'name', - 'to' => 'title', - 'cast' => 'copy', - ], - [ - 'from' => 'userownerid', - 'to' => 'userid', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => ['contact-socid', 'next-project-ref'], - ], -]; diff --git a/addons/dolibarr/templates/individual-proposals.php b/addons/dolibarr/templates/individual-proposals.php deleted file mode 100644 index 8004b7ac..00000000 --- a/addons/dolibarr/templates/individual-proposals.php +++ /dev/null @@ -1,200 +0,0 @@ - __('Proposals', 'forms-bridge'), - 'description' => __( - 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/proposals', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'stcomm_id', - 'label' => __('Prospect status', 'forms-bridge'), - 'required' => true, - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Never contacted', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('To contact', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Contact in progress', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Contacted', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Do not contact', 'forms-bridge'), - 'value' => '-1', - ], - ], - 'default' => '0', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'fk_product', - 'label' => __('Product', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/index.php/products', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].label', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Proposals', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Proposals', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'quantity', - 'label' => __('Quantity', 'forms-bridge'), - 'type' => 'number', - 'required' => true, - 'default' => 1, - 'min' => 1, - ], - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'tva_intra', - 'label' => __('Tax ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'address', - 'label' => __('Address', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'zip', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'town', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/proposals', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'typent_id', - 'value' => '8', - ], - [ - 'name' => 'client', - 'value' => '2', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - [ - 'name' => 'lines[0].product_type', - 'value' => '1', - ], - ], - 'mutations' => [ - [], - [ - [ - 'from' => 'firstname', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => 'lastname', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => 'quantity', - 'to' => 'lines[0].qty', - 'cast' => 'integer', - ], - [ - 'from' => 'fk_product', - 'to' => 'lines[0].fk_product', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'country-id', 'contact-socid'], - ], -]; diff --git a/addons/dolibarr/templates/individual-prospects.php b/addons/dolibarr/templates/individual-prospects.php deleted file mode 100644 index 70084fd8..00000000 --- a/addons/dolibarr/templates/individual-prospects.php +++ /dev/null @@ -1,125 +0,0 @@ - __('Prospects', 'forms-bridge'), - 'description' => __( - 'Lead form template. The resulting bridge will convert form submissions into prospect contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/thirdparties', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'stcomm_id', - 'label' => __('Prospect status', 'forms-bridge'), - 'required' => true, - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Never contacted', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('To contact', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Contact in progress', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Contacted', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Do not contact', 'forms-bridge'), - 'value' => '-1', - ], - ], - 'default' => '0', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Prospects', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Prospects', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'firstname', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'lastname', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'note_private', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/thirdparties', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'typent_id', - 'value' => '8', - ], - [ - 'name' => 'client', - 'value' => '2', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'firstname', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => 'lastname', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - ], - ], - 'workflow' => ['skip-if-thirdparty-exists', 'next-client-code'], - ], -]; diff --git a/addons/dolibarr/templates/thirdparties.php b/addons/dolibarr/templates/thirdparties.php deleted file mode 100644 index 4e7b8a69..00000000 --- a/addons/dolibarr/templates/thirdparties.php +++ /dev/null @@ -1,182 +0,0 @@ - __('Thirdparties', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into thirdparties.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/thirdparties', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'client', - 'label' => __('Thirdparty status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'value' => '1', - 'label' => __('Client', 'forms-bridge'), - ], - [ - 'value' => '2', - 'label' => __('Prospect', 'forms-bridge'), - ], - [ - 'value' => '3', - 'label' => __('Client/Prospect', 'forms-bridge'), - ], - [ - 'value' => '0', - 'label' => __( - 'Neither customer nor supplier', - 'forms-bridge' - ), - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Thirdparty type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Retailer', 'forms-bridge'), - 'value' => '7', - ], - [ - 'label' => __('Private individual', 'forms-bridge'), - 'value' => '8', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - // [ - // 'ref' => '#bridge/custom_fields[]', - // 'name' => 'fournisseur', - // 'label' => __('Provider', 'forms-bridge'), - // 'type' => 'boolean', - // 'default' => false, - // ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Thirdparties', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Thirdparties', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'tva_intra', - 'label' => __('Tax ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'address', - 'label' => __('Address', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'zip', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'town', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'name' => 'note_private', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/thirdparties', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - ], - 'workflow' => [ - 'iso2-country-code', - 'country-id', - 'skip-if-thirdparty-exists', - 'next-client-code', - ], - ], -]; diff --git a/addons/dolibarr/templates/woo-orders.php b/addons/dolibarr/templates/woo-orders.php deleted file mode 100644 index 5fe7a28e..00000000 --- a/addons/dolibarr/templates/woo-orders.php +++ /dev/null @@ -1,428 +0,0 @@ - __('Sale Orders', 'forms-bridge'), - 'description' => __( - 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into sale orders linked to new contacts. To work propertly, the bridge needs that your WooCommerce product sku values matches with the dolibarr\'s product refs..', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/orders', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Thirdparty type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Retailer', 'forms-bridge'), - 'value' => '7', - ], - [ - 'label' => __('Private individual', 'forms-bridge'), - 'value' => '8', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/orders', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'typent_id', - 'value' => '8', - ], - [ - 'name' => 'client', - 'value' => '1', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'shipping.email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'customer_note', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'lines[].qty', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].subtotal_tax.percentage', - 'to' => 'lines[].tva_tx', - 'cast' => 'number', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'lines[].subprice', - 'cast' => 'number', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'lines[].ref', - 'cast' => 'string', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'socid', - 'to' => 'order_socid', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'firstname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'lastname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone_perso', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'lines[].ref', - 'to' => 'product_refs', - 'cast' => 'inherit', - ], - [ - 'from' => 'contact_ids', - 'to' => 'contact_ids', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => '?customer_note', - 'to' => 'note_private', - 'cast' => 'string', - ], - [ - 'from' => 'order_socid', - 'to' => 'socid', - 'cast' => 'integer', - ], - [ - 'from' => 'fk_products[]', - 'to' => 'lines[].fk_product', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => ['contact-socid', 'contact-id', 'products-by-ref'], - ], -]; diff --git a/addons/dolibarr/templates/woo-sync-orders.php b/addons/dolibarr/templates/woo-sync-orders.php deleted file mode 100644 index ed6ffcff..00000000 --- a/addons/dolibarr/templates/woo-sync-orders.php +++ /dev/null @@ -1,435 +0,0 @@ - __('Sale Orders + Sync', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Dolibarr by product refs.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/orders', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Thirdparty type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Retailer', 'forms-bridge'), - 'value' => '7', - ], - [ - 'label' => __('Private individual', 'forms-bridge'), - 'value' => '8', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/orders', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'typent_id', - 'value' => '8', - ], - [ - 'name' => 'client', - 'value' => '1', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'shipping.email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'customer_note', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'lines[].qty', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].subtotal_tax.percentage', - 'to' => 'lines[].tva_tx', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'lines[].subprice', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'lines[].ref', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'socid', - 'to' => 'order_socid', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'firstname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'lastname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone_perso', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'lines[].ref', - 'to' => 'product_refs', - 'cast' => 'inherit', - ], - [ - 'from' => 'contact_ids', - 'to' => 'contact_ids', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => '?customer_note', - 'to' => 'note_private', - 'cast' => 'string', - ], - [ - 'from' => 'order_socid', - 'to' => 'socid', - 'cast' => 'integer', - ], - [ - 'from' => 'fk_products[]', - 'to' => 'lines[].fk_product', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'sync-products-by-ref', - 'contact-socid', - 'contact-id', - 'products-by-ref', - ], - ], -]; diff --git a/addons/dolibarr/templates/woo-validated-orders.php b/addons/dolibarr/templates/woo-validated-orders.php deleted file mode 100644 index 5afad45a..00000000 --- a/addons/dolibarr/templates/woo-validated-orders.php +++ /dev/null @@ -1,429 +0,0 @@ - __('Validated Orders', 'forms-bridge'), - 'description' => __( - 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into validated sale orders linked to new third parties. To work properly, the bridge needs that your WooCommerce product sku values matches with the dolibarr\'s product refs..', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/orders', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Thirdparty type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Retailer', 'forms-bridge'), - 'value' => '7', - ], - [ - 'label' => __('Private individual', 'forms-bridge'), - 'value' => '8', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/orders', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'client', - 'value' => '1', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'shipping.email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'customer_note', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'lines[].qty', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].subtotal_tax.percentage', - 'to' => 'lines[].tva_tx', - 'cast' => 'number', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'lines[].subprice', - 'cast' => 'number', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'lines[].ref', - 'cast' => 'string', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'socid', - 'to' => 'order_socid', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'firstname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'lastname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone_perso', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'lines[].ref', - 'to' => 'product_refs', - 'cast' => 'inherit', - ], - [ - 'from' => 'contact_ids', - 'to' => 'contact_ids', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => '?customer_note', - 'to' => 'note_private', - 'cast' => 'string', - ], - [ - 'from' => 'order_socid', - 'to' => 'socid', - 'cast' => 'integer', - ], - [ - 'from' => 'fk_products[]', - 'to' => 'lines[].fk_product', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'contact-socid', - 'contact-id', - 'products-by-ref', - 'validate-order', - ], - ], -]; diff --git a/addons/dolibarr/templates/woo-validated-sync-orders.php b/addons/dolibarr/templates/woo-validated-sync-orders.php deleted file mode 100644 index 6702b6a2..00000000 --- a/addons/dolibarr/templates/woo-validated-sync-orders.php +++ /dev/null @@ -1,432 +0,0 @@ - __('Validated Orders + Sync', 'forms-bridge'), - 'description' => __( - 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into validated sale orders linked to new third parties. The template includes a job that synchronize products between WooCommerce and Dolibarr by product refs..', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/index.php/orders', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'typent_id', - 'label' => __('Thirdparty type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Large company', 'forms-bridge'), - 'value' => '2', - ], - [ - 'label' => __('Medium company', 'forms-bridge'), - 'value' => '3', - ], - [ - 'label' => __('Small company', 'forms-bridge'), - 'value' => '4', - ], - [ - 'label' => __('Governmental', 'forms-bridge'), - 'value' => '5', - ], - [ - 'label' => __('Startup', 'forms-bridge'), - 'value' => '1', - ], - [ - 'label' => __('Retailer', 'forms-bridge'), - 'value' => '7', - ], - [ - 'label' => __('Private individual', 'forms-bridge'), - 'value' => '8', - ], - [ - 'label' => __('Other', 'forms-bridge'), - 'value' => '100', - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/api/index.php/orders', - 'custom_fields' => [ - [ - 'name' => 'status', - 'value' => '1', - ], - [ - 'name' => 'client', - 'value' => '1', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'shipping.email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'customer_note', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'lines[].qty', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].subtotal_tax.percentage', - 'to' => 'lines[].tva_tx', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'lines[].subprice', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'lines[].ref', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'socid', - 'to' => 'order_socid', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'firstname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'lastname', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone_perso', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'address', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'town', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'lines[].ref', - 'to' => 'product_refs', - 'cast' => 'inherit', - ], - [ - 'from' => 'contact_ids', - 'to' => 'contact_ids', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => '?customer_note', - 'to' => 'note_private', - 'cast' => 'string', - ], - [ - 'from' => 'order_socid', - 'to' => 'socid', - 'cast' => 'integer', - ], - [ - 'from' => 'fk_products[]', - 'to' => 'lines[].fk_product', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'sync-products-by-ref', - 'contact-socid', - 'contact-id', - 'products-by-ref', - 'validate-order', - ], - ], -]; diff --git a/addons/financoop/class-financoop-addon.php b/addons/financoop/class-financoop-addon.php deleted file mode 100644 index 5b0c0798..00000000 --- a/addons/financoop/class-financoop-addon.php +++ /dev/null @@ -1,198 +0,0 @@ - '__financoop-' . time(), - 'endpoint' => '/api/campaign', - 'method' => 'GET', - 'backend' => $backend, - ]); - - $response = $bridge->submit(); - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge = new Finan_Coop_Form_Bridge([ - 'name' => '__financoop-' . time(), - 'endpoint' => $endpoint, - 'backend' => $backend, - 'method' => 'GET', - ]); - - return $bridge->submit(); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array - */ - public function get_endpoint_schema($endpoint, $backend) - { - $bridge = new Finan_Coop_Form_Bridge([ - 'name' => '__financoop-' . time(), - 'endpoint' => $endpoint, - 'backend' => $backend, - 'method' => 'GET', - ]); - - if ( - !preg_match( - '/\/api\/campaign\/\d+\/([a-z_]+)$/', - $bridge->endpoint, - $matches - ) - ) { - return []; - } - - $source = $matches[1]; - - $common_schema = [ - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'firstname', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'lastname', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'address', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'lang', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - ], - ]; - - switch ($source) { - case 'subscription_request': - return array_merge( - [ - [ - 'name' => 'ordered_parts', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'type', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'remuneration_type', - 'schema' => ['type' => 'string'], - ], - ], - $common_schema - ); - break; - case 'donation_request': - return array_merge( - [ - [ - 'name' => 'donation_amount', - 'schema' => ['type' => 'integer'], - ], - // [ - // 'name' => 'tax_receipt_option', - // 'schema' => ['type' => 'string'], - // ], - ], - $common_schema - ); - break; - case 'loan_request': - return array_merge( - [ - [ - 'name' => 'loan_amount', - 'schema' => ['type' => 'integer'], - ], - ], - $common_schema - ); - break; - } - } -} - -Finan_Coop_Addon::setup(); diff --git a/addons/financoop/class-financoop-form-bridge.php b/addons/financoop/class-financoop-form-bridge.php deleted file mode 100644 index 884930f0..00000000 --- a/addons/financoop/class-financoop-form-bridge.php +++ /dev/null @@ -1,120 +0,0 @@ - '2.0', - 'params' => $payload, - ]; - } - - add_filter( - 'http_bridge_backend_headers', - function ($headers, $backend) { - if ($backend->name === $this->data['backend']) { - $credential = $backend->credential; - if (!$credential) { - return $headers; - } - - [ - $database, - $username, - $password, - ] = $credential->authorization(); - $headers['X-Odoo-Db'] = $database; - $headers['X-Odoo-Username'] = $username; - $headers['X-Odoo-Api-Key'] = $password; - } - - return $headers; - }, - 10, - 2 - ); - - add_filter( - 'http_bridge_request', - static function ($request) { - self::$request = $request; - return $request; - }, - 10, - 1 - ); - - $response = parent::submit($payload); - - if (is_wp_error($response)) { - return $response; - } - - if (isset($response['data']['error'])) { - $error = new WP_Error( - 'response_code_' . $response['data']['error']['code'], - $response['data']['error']['message'], - $response['data']['error']['data'] - ); - - $error_data = ['response' => $response]; - if (self::$request) { - $error_data['request'] = self::$request; - } - - $error->add_data($error_data); - return $error; - } - - if (isset($response['data']['result']['error'])) { - $error = new WP_Error( - 'response_code_' . $response['data']['result']['status'], - $response['data']['result']['error'], - $response['data']['result']['details'] - ); - - $error_data = ['response' => $response]; - if (self::$request) { - $error_data['request'] = self::$request; - } - - $error->add_data($error_data); - return $error; - } - - $response['data'] = $response['data']['data'] ?? []; - return $response; - } -} diff --git a/addons/financoop/hooks.php b/addons/financoop/hooks.php deleted file mode 100644 index fee7e261..00000000 --- a/addons/financoop/hooks.php +++ /dev/null @@ -1,230 +0,0 @@ - [ - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'campaign_id', - 'label' => __('Campaign', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/campaign', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#backend', - 'name' => 'name', - 'default' => 'FinanCoop', - ], - [ - 'ref' => '#credential', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'schema', - 'type' => 'text', - 'value' => 'RPC', - ], - [ - 'ref' => '#credential', - 'name' => 'database', - 'label' => __('Database', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_id', - 'label' => __('Username', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_secret', - 'label' => __('Password', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - ], - 'bridge' => [ - 'backend' => 'FinanCoop', - 'method' => 'POST', - ], - 'backend' => [ - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'credential' => [ - 'name' => '', - 'schema' => 'RPC', - 'client_id' => '', - 'client_secret' => '', - 'database' => '', - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'financoop-') !== 0) { - return $data; - } - - $index = array_search( - 'campaign_id', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $campaign_id = $data['bridge']['custom_fields'][$index]['value']; - - $data['bridge']['endpoint'] = preg_replace( - '/\{campaign_id\}/', - $campaign_id, - $data['bridge']['endpoint'] - ); - - array_splice($data['bridge']['custom_fields'], $index, 1); - } else { - return new WP_Error( - 'invalid_fields', - __( - 'Financoop template requireds the field $campaign_id', - 'forms-bridge' - ), - ['status' => 400] - ); - } - - $endpoint = implode( - '/', - array_slice(explode('/', $data['bridge']['endpoint']), 0, 4) - ); - - $data['backend']['credential'] = $data['credential']['name']; - - Backend::temp_registration($data['backend']); - Credential::temp_registration($data['credential']); - - $addon = FBAPI::get_addon('financoop'); - $response = $addon->fetch( - $endpoint, - $data['backend']['name'], - $data['credential']['name'] - ); - - if (is_wp_error($response)) { - return new WP_Error( - 'financoop_api_error', - __('Can\'t fetch campaign data', 'forms-bridge'), - ['status' => 500] - ); - } - - $campaign = $response['data']; - $field_names = array_column($data['form']['fields'], 'name'); - - $index = array_search('donation_amount', $field_names); - if ($index !== false) { - $field = &$data['form']['fields'][$index]; - - $min = $campaign['minimal_donation_amount']; - if (!empty($min)) { - $field['min'] = $min; - $field['default'] = $min; - } - } - - $index = array_search('loan_amount', $field_names); - if ($index !== false) { - $field = &$data['form']['fields'][$index]; - - $min = $campaign['minimal_loan_amount']; - if (!empty($min)) { - $field['min'] = $min; - $field['default'] = $min; - } - - $max = $campaign['maximal_loan_amount']; - if (!empty($max)) { - $field['max'] = $max; - } - } - - $index = array_search('ordered_parts', $field_names); - if ($index !== false) { - $field = &$data['form']['fields'][$index]; - - $min = $campaign['minimal_subscription_amount']; - if (!empty($min)) { - $field['min'] = $min; - $field['step'] = $min; - $field['default'] = $min; - } - - $max = $campaign['maximal_subscription_amount']; - if (!empty($max)) { - $field['max'] = $max; - } - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/financoop/jobs/vat-id.php b/addons/financoop/jobs/vat-id.php deleted file mode 100644 index 006d185e..00000000 --- a/addons/financoop/jobs/vat-id.php +++ /dev/null @@ -1,84 +0,0 @@ - __('Prefixed vat ID', 'forms-bridge'), - 'description' => __( - 'Prefix the vat with country code, or the current locale, if it isn\'t prefixed', - 'forms-bridge' - ), - 'method' => 'forms_bridge_financoop_vat_id', - 'input' => [ - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - 'requires' => ['country'], - ], - ], -]; diff --git a/addons/financoop/shortcodes.php b/addons/financoop/shortcodes.php deleted file mode 100644 index 62dc4e7d..00000000 --- a/addons/financoop/shortcodes.php +++ /dev/null @@ -1,336 +0,0 @@ - '__financoop-' . time(), - 'endpoint' => "/api/campaign/{$atts['id']}", - 'backend' => $atts['backend'], - 'method' => 'GET', - ], - 'financoop' - ); - - $response = $bridge->submit(); - if (is_wp_error($response)) { - $message = $response->get_error_message(); - return forms_bridge_financoop_shortcode_error( - "Fetch campaign error message: {$message}", - $atts - ); - } - - $campaign = $response['data']; - } catch (Error | Exception $e) { - return forms_bridge_financoop_shortcode_error( - "Fetch campaign error message: {$e->getMessage()}", - $atts - ); - } - - ob_start(); - ?>
-
-
-

- -

- -
-
- - -
-
-

- -

- : / -

- -

- : -

- -

- : -

- -

- : % -

- -
- % -
-
- $source, - 'label' => $label, - 'progress' => $progress, - 'amount' => $amount, - 'goal' => $goal, - ] - ); -} - -function financoop_render_campaign_dates($campaign) -{ - if ($campaign['is_permanent']) { - return ''; - } - - $start = $campaign['start_date']; - $end = $campaign['end_date']; - $days_to_start = null; - $days_to_end = null; - - $start_time = strtotime($start); - $end_time = null; - - $now = time(); - - if ($start_time > $now) { - $days_to_start = max(1, ($start_time - $now) / (60 * 60 * 24)); - } - - if ($end) { - $end_time = strtotime($end); - $days_to_end = max(0, ($end_time - $now) / (60 * 60 * 24)); - } - - if (!$start) { - return ''; - } - - $output = '
'; - - if ($days_to_start) { - $output .= sprintf( - '

%s: %s

', - esc_html( - _x('Days to start', 'financoop campaign widget', 'forms-bridge') - ), - (int) $days_to_start - ); - } - - if ($end) { - $end_date = new DateTime($end); - $output .= sprintf( - '

%s: %s

', - esc_html( - _x('End date', 'financoop campaign widget', 'forms-bridge') - ), - esc_html(IntlDateFormatter::formatObject($end_date, 'dd/MM/Y')) - ); - - if (!$days_to_start && $days_to_end > 0) { - $output .= sprintf( - '

%s: %s

', - esc_html( - _x( - 'Days to end', - 'financoop campaign widget', - 'forms-bridge' - ) - ), - (int) $days_to_end - ); - } - } - - $output .= '
'; - - return apply_filters( - 'forms_bridge_financoop_dates_html', - $output, - $campaign, - [ - 'start' => $start, - 'end' => $end, - 'days_to_start' => $days_to_start, - 'days_to_end' => $days_to_end, - ] - ); -} - -function forms_bridge_financoop_shortcode_error($message, $atts) -{ - $atts = implode( - ' ', - array_reduce( - ['id', 'backend', 'sources', 'currency'], - function ($handle, $attr) use ($atts) { - if (isset($atts[$attr])) { - $value = implode(',', (array) $atts[$attr]); - $handle[] = "{$attr}='{$value}'"; - } - - return $handle; - }, - [] - ) - ); - - return "[financoop_campaign {$atts}]{$message}[/financoop_campaign]"; -} diff --git a/addons/financoop/templates/donation-requests.php b/addons/financoop/templates/donation-requests.php deleted file mode 100644 index 34f643f0..00000000 --- a/addons/financoop/templates/donation-requests.php +++ /dev/null @@ -1,143 +0,0 @@ - __('Donation Requests', 'forms-bridge'), - 'description' => __( - 'Donations form template. The resulting bridge will convert form submissions into donation requests.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/campaign/{campaign_id}/donation_request', - ], - // [ - // 'ref' => '#bridge/custom_fields[]', - // 'name' => 'tax_receipt_option', - // 'label' => __('Tax receipt', 'forms-bridge'), - // 'type' => 'select', - // 'options' => [ - // [ - // 'label' => 'foo', - // 'value' => 'foo', - // ], - // [ - // 'label' => 'bar', - // 'value' => 'bar', - // ], - // ], - // 'required' => true, - // ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Donation Requests', 'forms-bridge'), - ], - ], - 'bridge' => [ - 'endpoint' => '/api/campaign/{campaign_id}/donation_request', - 'mutations' => [ - [ - [ - 'from' => 'donation_amount', - 'to' => 'donation_amount', - 'cast' => 'integer', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'country_code', - 'cast' => 'string', - ], - ], - ], - 'custom_fields' => [ - [ - 'name' => 'lang', - 'value' => '$locale', - ], - ], - 'workflow' => ['iso2-country-code', 'vat-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Donation amount', 'forms-bridge'), - 'name' => 'donation_amount', - 'type' => 'number', - 'required' => true, - 'min' => 0, - ], - [ - 'label' => __('First name', 'forms-bridge'), - 'name' => 'firstname', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Last name', 'forms-bridge'), - 'name' => 'lastname', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('ID number', 'forms-bridge'), - 'name' => 'vat', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Nationality', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip_code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/financoop/templates/loan-requests.php b/addons/financoop/templates/loan-requests.php deleted file mode 100644 index 1c87601e..00000000 --- a/addons/financoop/templates/loan-requests.php +++ /dev/null @@ -1,126 +0,0 @@ - __('Loan Requests', 'forms-bridge'), - 'description' => __( - 'Loans form template. The resulting bridge will convert form submissions into loan requests.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/campaign/{campaign_id}/loan_request', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Loan Requests', 'forms-bridge'), - ], - ], - 'bridge' => [ - 'endpoint' => '/api/campaign/{campaign_id}/loan_request', - 'mutations' => [ - [ - [ - 'from' => 'loan_amount', - 'to' => 'loan_amount', - 'cast' => 'integer', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'country_code', - 'cast' => 'string', - ], - ], - ], - 'custom_fields' => [ - [ - 'name' => 'lang', - 'value' => '$locale', - ], - ], - 'workflow' => ['iso2-country-code', 'vat-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Loan amount', 'forms-bridge'), - 'name' => 'loan_amount', - 'type' => 'number', - 'required' => true, - 'min' => 0, - ], - [ - 'label' => __('First name', 'forms-bridge'), - 'name' => 'firstname', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Last name', 'forms-bridge'), - 'name' => 'lastname', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('ID number', 'forms-bridge'), - 'name' => 'vat', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Nationality', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip_code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/financoop/templates/subscription-requests.php b/addons/financoop/templates/subscription-requests.php deleted file mode 100644 index e23472cf..00000000 --- a/addons/financoop/templates/subscription-requests.php +++ /dev/null @@ -1,146 +0,0 @@ - __('Subscription Requests', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will convert form submissions into subscription requests.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Subscription Requests', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/campaign/{campaign_id}/subscription_request', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/campaign/{campaign_id}/subscription_request', - 'custom_fields' => [ - [ - 'name' => 'lang', - 'value' => '$locale', - ], - [ - 'name' => 'type', - 'value' => 'increase', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'ordered_parts', - 'to' => 'ordered_parts', - 'cast' => 'integer', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'country_code', - 'cast' => 'string', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'vat-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Ordered parts', 'forms-bridge'), - 'name' => 'ordered_parts', - 'type' => 'number', - 'required' => true, - 'min' => 1, - ], - [ - 'label' => __('Remuneration type', 'forms-bridge'), - 'name' => 'remuneration_type', - 'type' => 'select', - 'required' => true, - 'options' => [ - [ - 'value' => 'cash', - 'label' => __('Cash', 'forms-bridge'), - ], - [ - 'value' => 'wallet', - 'label' => __('Wallet', 'forms-bridge'), - ], - ], - ], - [ - 'label' => __('First name', 'forms-bridge'), - 'name' => 'firstname', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Last name', 'forms-bridge'), - 'name' => 'lastname', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('ID number', 'forms-bridge'), - 'name' => 'vat', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Nationality', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip_code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/gsheets/class-gsheets-addon.php b/addons/gsheets/class-gsheets-addon.php deleted file mode 100644 index 86278c5d..00000000 --- a/addons/gsheets/class-gsheets-addon.php +++ /dev/null @@ -1,185 +0,0 @@ -addon === 'gsheets') { - return false; - } - - return $prune; - }, - 5, - 2 - ); - } - - /** - * Performs a request against the backend to check the connexion status. - * - * @param string $backend Backend name. - * - * @return boolean - */ - public function ping($backend) - { - $bridge = new Google_Sheets_Form_Bridge([ - 'name' => '__gsheets-' . time(), - 'backend' => $backend, - 'endpoint' => '/', - 'method' => 'GET', - 'tab' => 'foo', - ]); - - $backend = $bridge->backend; - if (!$backend) { - return false; - } - - $credential = $backend->credential; - if (!$credential) { - return false; - } - - $parsed = wp_parse_url($backend->base_url); - $host = $parsed['host'] ?? ''; - - if ($host !== 'sheets.googleapis.com') { - return false; - } - - $access_token = $credential->get_access_token(); - return !!$access_token; - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint Concatenation of spreadsheet ID and tab name. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $backend = FBAPI::get_backend($backend); - if (!$backend) { - return new WP_Error('invalid_backend'); - } - - $credential = $backend->credential; - if (!$credential) { - return new WP_Error('invalid_credential'); - } - - $access_token = $credential->get_access_token(); - if (!$access_token) { - return new WP_Error('invalid_credential'); - } - - $response = http_bridge_get( - 'https://www.googleapis.com/drive/v3/files', - ['q' => "mimeType = 'application/vnd.google-apps.spreadsheet'"], - [ - 'Authorization' => "Bearer {$access_token}", - 'Accept' => 'application/json', - ] - ); - - if (is_wp_error($response)) { - return $response; - } - - return $response; - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint Concatenation of spreadsheet ID and tab name. - * @param string $backend Backend name. - * - * @return array List of fields and content type of the endpoint. - */ - public function get_endpoint_schema($endpoint, $backend) - { - $bridges = FBAPI::get_addon_bridges(self::name); - foreach ($bridges as $candidate) { - $data = $candidate->data(); - if (!$data) { - continue; - } - - if ( - $data['endpoint'] === $endpoint && - $data['backend'] === $backend - ) { - $bridge = $candidate; - } - } - - if (!isset($bridge)) { - return []; - } - - $headers = $bridge->get_headers(); - - if (is_wp_error($headers)) { - return []; - } - - $fields = []; - foreach ($headers as $header) { - $fields[] = [ - 'name' => $header, - 'schema' => ['type' => 'string'], - ]; - } - - return $fields; - } -} - -Google_Sheets_Addon::setup(); diff --git a/addons/gsheets/class-gsheets-form-bridge.php b/addons/gsheets/class-gsheets-form-bridge.php deleted file mode 100644 index 45719be1..00000000 --- a/addons/gsheets/class-gsheets-form-bridge.php +++ /dev/null @@ -1,239 +0,0 @@ -tab); - - if (empty($values)) { - return $range; - } - - $abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - - $len = strlen($abc); - - $columns = []; - for ($row = 0; $row < count($values); $row++) { - $rowcols = []; - $i = -1; - - for ($col = 0; $col < count($values[$row]); $col++) { - if ($col > 0 && $col % $len === 0) { - $i++; - } - - if ($col >= $len) { - $index = $col % $len; - $rowcols[] = $abc[$i] . $abc[$index]; - } else { - $rowcols[] = $abc[$col]; - } - } - - if (count($rowcols) > count($columns)) { - $columns = $rowcols; - } - } - - $range .= '!' . $columns[0] . '1'; - $range .= ':' . $columns[count($columns) - 1] . $row; - - return $range; - } - - public function get_headers($backend = null) - { - if (!$this->is_valid) { - return new WP_Error('invalid_bridge'); - } - - if (!$backend) { - $backend = $this->backend; - } - - $range = rawurlencode($this->tab) . '!1:1'; - - $response = $backend->get($this->endpoint . '/values/' . $range); - - if (is_wp_error($response)) { - return $response; - } - - return $response['data']['values'][0] ?? []; - } - - private function add_sheet($index, $title, $backend) - { - $response = $backend->post($this->endpoint . ':batchUpdate', [ - 'requests' => [ - [ - 'addSheet' => [ - 'properties' => [ - 'sheetId' => time(), - 'index' => $index, - 'title' => $title, - 'sheetType' => 'GRID', - 'gridProperties' => [ - 'rowCount' => 1000, - 'columnCount' => 26, - ], - 'hidden' => false, - ], - ], - ], - ], - ]); - - if (is_wp_error($response)) { - return $response; - } - - return $response['data']; - } - - private function get_sheets($backend) - { - $response = $backend->get($this->endpoint); - - if (is_wp_error($response)) { - return $response; - } - - $sheets = []; - foreach ($response['data']['sheets'] as $sheet) { - $sheets[] = strtolower($sheet['properties']['title']); - } - - return $sheets; - } - - /** - * - * @param array $payload Submission data. - * @param array $attachments Submission's attached files. Will be ignored. - * - * @return array|WP_Error Http request response. - */ - public function submit($payload = [], $attachments = []) - { - if (!$this->is_valid) { - return new WP_Error('invalid_bridge'); - } - - $backend = $this->backend; - - $sheets = $this->get_sheets($backend); - if (is_wp_error($sheets)) { - return $sheets; - } - - if (!in_array(strtolower($this->tab), $sheets, true)) { - $result = $this->add_sheet(count($sheets), $this->tab, $backend); - if (is_wp_error($result)) { - return $result; - } - } - - $endpoint = $this->endpoint . '/values/' . rawurlencode($this->tab); - $method = $this->method; - - if ($method === 'POST' || $method === 'PUT') { - $endpoint .= '!A1:Z:append/?valueInputOption=USER_ENTERED'; - - $headers = $this->get_headers($backend); - if (is_wp_error($headers)) { - return $headers; - } - - $payload = self::flatten_payload($payload); - $values = []; - - if (empty($headers)) { - $headers = array_keys($payload); - $values[] = $headers; - } - - $row = []; - foreach ($headers as $header) { - if (isset($payload[$header])) { - $row[] = $payload[$header] ?? ''; - } else { - $row[] = ''; - } - } - - $values[] = $row; - - $payload = [ - // 'range' => $this->value_range($values), - 'majorDimension' => 'ROWS', - 'values' => $values, - ]; - } - - return $this->backend->$method($endpoint, $payload); - } - - /** - * Sheets are flat, if payload has nested arrays, flattens it and concatenate its keys - * as field names. - * - * @param array $payload Submission payload. - * @param string $path Prefix to prepend to the field name. - * - * @return array Flattened payload. - */ - private static function flatten_payload($payload, $path = '') - { - $flat = []; - foreach ($payload as $field => $value) { - $key = $path . $field; - $value = self::flatten_value($value, $key); - - if (!is_array($value)) { - $flat[$key] = $value; - } else { - foreach ($value as $_key => $_val) { - $flat[$_key] = $_val; - } - } - } - - return $flat; - } - - private static function flatten_value($value, $path = '') - { - if (!is_array($value)) { - return $value; - } - - if (wp_is_numeric_array($value)) { - $simple_items = array_filter($value, fn($item) => !is_array($item)); - - if (count($simple_items) === count($value)) { - return implode(',', $value); - } - } - - return self::flatten_payload($value, $path . '.'); - } -} diff --git a/addons/gsheets/hooks.php b/addons/gsheets/hooks.php deleted file mode 100644 index fea144e5..00000000 --- a/addons/gsheets/hooks.php +++ /dev/null @@ -1,194 +0,0 @@ - __('Name of the spreadsheet tab', 'forms-bridge'), - 'type' => 'string', - 'minLength' => 1, - 'required' => true, - 'default' => 'Sheet1', - ]; - - return $schema; - }, - 10, - 2 -); - -add_filter( - 'forms_bridge_template_defaults', - function ($defaults, $addon, $schema) { - if ($addon !== 'gsheets') { - return $defaults; - } - - $defaults = wpct_plugin_merge_object( - [ - 'fields' => [ - [ - 'ref' => '#credential', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'schema', - 'type' => 'text', - 'value' => 'Bearer', - ], - [ - 'ref' => '#credential', - 'name' => 'oauth_url', - 'label' => __('Authorization URL', 'forms-bridge'), - 'type' => 'text', - 'value' => 'https://accounts.google.com/o/oauth2/v2', - ], - [ - 'ref' => '#credential', - 'name' => 'client_id', - 'label' => __('Client ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_secret', - 'label' => __('Client secret', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'scope', - 'label' => __('Scope', 'forms-bridge'), - 'type' => 'text', - 'value' => - 'https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/spreadsheets', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'label' => __('Spreadsheet', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/drive/v3/files', - 'finger' => [ - 'value' => 'files[].id', - 'label' => 'files[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - [ - 'ref' => '#bridge', - 'name' => 'tab', - 'label' => __('Tab', 'forms-bridge'), - 'type' => 'text', - 'default' => '', - ], - [ - 'ref' => '#backend', - 'name' => 'name', - 'default' => 'Sheets API', - ], - [ - 'ref' => '#backend', - 'name' => 'base_url', - 'value' => 'https://sheets.googleapis.com', - ], - ], - 'backend' => [ - 'name' => 'Sheets API', - 'base_url' => 'https://sheets.googleapis.com', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'bridge' => [ - 'backend' => 'Sheets API', - 'endpoint' => '', - 'tab' => '', - ], - 'credential' => [ - 'name' => '', - 'schema' => 'Bearer', - 'oauth_url' => 'https://accounts.google.com/o/oauth2/v2', - 'scope' => - 'https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/spreadsheets', - 'client_id' => '', - 'client_secret' => '', - 'access_token' => '', - 'expires_at' => 0, - 'refresh_token' => '', - ], - ], - $defaults, - $schema - ); - - return $defaults; - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'gsheets-') !== 0) { - return $data; - } - - $data['bridge']['endpoint'] = - '/v4/spreadsheets/' . $data['bridge']['endpoint']; - return $data; - }, - 10, - 2 -); - -add_filter( - 'http_bridge_oauth_url', - function ($url, $verb) { - if (strpos($url, 'accounts.google.com') === false) { - return $url; - } - - if ($verb === 'auth') { - return $url; - } - - return "https://oauth2.googleapis.com/{$verb}"; - }, - 10, - 2 -); diff --git a/addons/gsheets/templates/woo-orders.php b/addons/gsheets/templates/woo-orders.php deleted file mode 100644 index 26c45fd3..00000000 --- a/addons/gsheets/templates/woo-orders.php +++ /dev/null @@ -1,322 +0,0 @@ - __('WC Orders', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into new lines on a Google Spreadsheet with order and customer information.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - ], - 'bridge' => [ - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'Order ID', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'Name[][0]', - 'cast' => 'string', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'Name[][1]', - 'cast' => 'string', - ], - [ - 'from' => 'Name[]', - 'to' => 'Name[]', - 'cast' => 'concat', - ], - [ - 'from' => 'Name', - 'to' => 'Name', - 'cast' => 'csv', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'Date', - 'cast' => 'string', - ], - [ - 'from' => 'total', - 'to' => 'Total', - 'cast' => 'number', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines[].name', - 'to' => 'Shipping', - 'cast' => 'string', - ], - [ - 'from' => 'Shipping', - 'to' => 'Shipping', - 'cast' => 'csv', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'Shipping Total', - 'cast' => 'number', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines[].code', - 'to' => 'Coupons', - 'cast' => 'string', - ], - [ - 'from' => 'Coupons', - 'to' => 'Coupons', - 'cast' => 'csv', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'Discount Total', - 'cast' => 'number', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'First Name', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'Last Name', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'Email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'Phone', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'Address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_2', - 'to' => 'Address 2', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'City', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'Postal Code', - 'cast' => 'string', - ], - [ - 'from' => '?billing.state', - 'to' => 'State', - 'cast' => 'string', - ], - [ - 'from' => '?billing.country', - 'to' => 'Country', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'Payment Method', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'Note', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - ], - ], - ], -]; diff --git a/addons/holded/api.php b/addons/holded/api.php deleted file mode 100644 index a60008f3..00000000 --- a/addons/holded/api.php +++ /dev/null @@ -1,126 +0,0 @@ -patch([ - 'name' => 'holded-search-contact-query', - 'endpoint' => '/api/invoicing/v1/contacts', - 'method' => 'GET', - ]) - ->submit($query); - - if (is_wp_error($response)) { - return $response; - } - - if (empty($response['data'])) { - return; - } - - return $response['data'][0]; -} - -function forms_bridge_holded_update_contact($payload, $bridge) -{ - return forms_bridge_holded_create_contact($payload, $bridge, true); -} - -function forms_bridge_holded_create_contact($payload, $bridge, $update = false) -{ - if (!$update) { - $contact = forms_bridge_holded_search_contact($payload, $bridge); - - if (!is_wp_error($contact) && isset($contact['id'])) { - $payload['id'] = $contact['id']; - return forms_bridge_holded_update_contact($payload, $bridge); - } - } - - $contact = [ - 'name' => $payload['name'], - ]; - - $contact_fields = [ - 'tradeName', - 'email', - 'mobile', - 'phone', - 'type', - 'code', - 'vatnumber', - 'iban', - 'swift', - 'billAddress', - 'defaults', - 'tags', - 'note', - 'isperson', - 'contactPersons', - 'shippingAddresses', - 'CustomId', - ]; - - foreach ($contact_fields as $field) { - if (isset($payload[$field])) { - $contact[$field] = $payload[$field]; - } - } - - $method = 'POST'; - $endpoint = '/api/invoicing/v1/contacts'; - - if ($update && isset($payload['id'])) { - $method = 'PUT'; - $endpoint = $endpoint . '/' . $payload['id']; - } - - $response = $bridge - ->patch([ - 'name' => 'holded-create-contact', - 'endpoint' => $endpoint, - 'method' => $method, - ]) - ->submit($contact); - - if (is_wp_error($response)) { - return $response; - } - - $contact_id = $response['data']['id']; - - $response = $bridge - ->patch([ - 'name' => 'holded-get-contact-by-id', - 'endpoint' => '/api/invoicing/v1/contacts/' . $contact_id, - 'method' => 'GET', - ]) - ->submit(); - - if (is_wp_error($response)) { - return $response; - } - - return $response['data']; -} diff --git a/addons/holded/class-holded-addon.php b/addons/holded/class-holded-addon.php deleted file mode 100644 index 0486f6dc..00000000 --- a/addons/holded/class-holded-addon.php +++ /dev/null @@ -1,176 +0,0 @@ - '__holded-' . time(), - 'endpoint' => '/api/invoicing/v1/contacts', - 'method' => 'GET', - 'backend' => $backend, - ]); - - $response = $bridge->submit(['limit' => 1]); - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge = new Holded_Form_Bridge([ - 'name' => '__holded-' . time(), - 'endpoint' => $endpoint, - 'backend' => $backend, - 'method' => 'GET', - ]); - - return $bridge->submit(); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array List of fields and content type of the endpoint. - */ - public function get_endpoint_schema($endpoint, $backend) - { - $chunks = array_values(array_filter(explode('/', $endpoint))); - if (empty($chunks)) { - return []; - } - - $api_base = $chunks[0]; - if ($api_base !== 'api') { - array_unshift($chunks, 'api'); - } - - [, $module, $version, $resource] = $chunks; - - if ( - !in_array($module, [ - 'invoicing', - 'crm', - 'projects', - 'team', - 'accounting', - ]) || - $version !== 'v1' - ) { - return []; - } - - $path = plugin_dir_path(__FILE__) . "/data/swagger/{$module}.json"; - if (!is_file($path)) { - return []; - } - - $file_content = file_get_contents($path); - try { - $paths = json_decode($file_content, true); - } catch (TypeError) { - return []; - } - - $path = '/' . $resource; - if ($resource === 'documents') { - $path .= '/{docType}'; - } - - if (!isset($paths[$path])) { - return []; - } - - $schema = $paths[$path]; - if (!isset($schema['post'])) { - return []; - } - - $schema = $schema['post']; - - $fields = []; - if (isset($schema['parameters'])) { - foreach ($schema['parameters'] as $param) { - $fields[] = [ - 'name' => $param['name'], - 'schema' => $param['schema'], - ]; - } - } elseif ( - isset( - $schema['requestBody']['content']['application/json']['schema'][ - 'properties' - ] - ) - ) { - $properties = - $schema['requestBody']['content']['application/json']['schema'][ - 'properties' - ]; - foreach ($properties as $name => $schema) { - $fields[] = [ - 'name' => $name, - 'schema' => $schema, - ]; - } - } - - return $fields; - } -} - -Holded_Addon::setup(); diff --git a/addons/holded/class-holded-form-bridge.php b/addons/holded/class-holded-form-bridge.php deleted file mode 100644 index d690480f..00000000 --- a/addons/holded/class-holded-form-bridge.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - [ - 'ref' => '#backend', - 'name' => 'name', - 'description' => __( - 'Label of the Holded API backend connection', - 'forms-bridge' - ), - 'default' => 'Holded API', - ], - [ - 'ref' => '#backend', - 'name' => 'base_url', - 'value' => 'https://api.holded.com', - ], - [ - 'ref' => '#backend/headers[]', - 'name' => 'key', - 'label' => __('API Key', 'forms-bridge'), - 'description' => __( - 'Get it from your account', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - ], - 'bridge' => [ - 'method' => 'POST', - ], - 'backend' => [ - 'base_url' => 'https://api.holded.com', - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'holded-') !== 0) { - return $data; - } - - $index = array_search( - 'tags', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = &$data['bridge']['custom_fields'][$index]; - - if (!empty($field['value'])) { - $tags = array_filter( - array_map('trim', explode(',', strval($field['value']))) - ); - - for ($i = 0; $i < count($tags); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "tags[{$i}]", - 'value' => $tags[$i], - ]; - } - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/holded/jobs/appointment-dates.php b/addons/holded/jobs/appointment-dates.php deleted file mode 100644 index 138bcdb8..00000000 --- a/addons/holded/jobs/appointment-dates.php +++ /dev/null @@ -1,52 +0,0 @@ -getTimestamp(); - $payload['startDate'] = $timestamp; - $payload['duration'] = floatval($payload['duration'] ?? 1); - - return $payload; -} - -return [ - 'title' => __('Appointment dates', 'forms-bridge'), - 'description' => __( - 'Sets appointment start time and duration from datetime and duration fields', - 'forms-bridge' - ), - 'method' => 'forms_bridge_holded_appointment_dates', - 'input' => [ - [ - 'name' => 'date', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'duration', - 'schema' => ['type' => 'number'], - ], - ], - 'output' => [ - [ - 'name' => 'startDate', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'duration', - 'schema' => ['type' => 'number'], - ], - ], -]; diff --git a/addons/holded/jobs/contact-id.php b/addons/holded/jobs/contact-id.php deleted file mode 100644 index 259cd606..00000000 --- a/addons/holded/jobs/contact-id.php +++ /dev/null @@ -1,153 +0,0 @@ - __('Contact ID', 'forms-bridge'), - 'description' => __( - 'Creates a new contact and sets its ID as the contactId field of the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_holded_contact_id', - 'input' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'CustomId', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'tradeName', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'type', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vatnumber', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'iban', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'swift', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'billAddress', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'address' => ['type' => 'string'], - 'postalCode' => ['type' => 'string'], - 'city' => ['type' => 'string'], - 'countryCode' => ['type' => 'string'], - ], - 'additionalProperties' => true, - ], - ], - [ - 'name' => 'defaults', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'language' => ['type' => 'string'], - ], - 'additionalProperties' => true, - ], - ], - [ - 'name' => 'tags', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - 'additionalItems' => true, - ], - ], - [ - 'name' => 'note', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'isperson', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'contactPersons', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => ['type' => 'string'], - 'email' => ['type' => 'string'], - 'phone' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - ], - [ - 'name' => 'shippingAddresses', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => ['type' => 'string'], - 'address' => ['type' => 'string'], - 'city' => ['type' => 'string'], - 'postalCode' => ['type' => 'string'], - 'province' => ['type' => 'string'], - 'country' => ['type' => 'string'], - 'note' => ['type' => 'string'], - 'privateNote' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - ], - ], - 'output' => [ - [ - 'name' => 'contactId', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/holded/jobs/prefix-vatnumber.php b/addons/holded/jobs/prefix-vatnumber.php deleted file mode 100644 index b97eb8d8..00000000 --- a/addons/holded/jobs/prefix-vatnumber.php +++ /dev/null @@ -1,79 +0,0 @@ - __('Vatnumber prefix', 'forms-bridge'), - 'description' => __( - 'Prefix the vat with country code, or the current locale, if it isn\'t prefixed', - 'forms-bridge' - ), - 'method' => 'forms_bridge_holded_prefix_vatnumber', - 'input' => [ - [ - 'name' => 'vatnumber', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'countryCode', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'vatnumber', - 'schema' => ['type' => 'string'], - 'requires' => ['vatnumber'], - ], - [ - 'name' => 'countryCode', - 'schema' => ['type' => 'string'], - 'requires' => ['countryCode'], - ], - ], -]; diff --git a/addons/holded/jobs/skip-if-contact-exists.php b/addons/holded/jobs/skip-if-contact-exists.php deleted file mode 100644 index eaf24167..00000000 --- a/addons/holded/jobs/skip-if-contact-exists.php +++ /dev/null @@ -1,67 +0,0 @@ - __('Skip if contact exists', 'forms-bridge'), - 'description' => __( - 'Search for a contact and skip submission if it exists', - 'forms-bridge' - ), - 'method' => 'forms_bridge_holded_skip_contact', - 'input' => [ - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'customId', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - 'requires' => ['phone'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - 'requires' => ['mobile'], - ], - [ - 'name' => 'customId', - 'schema' => ['type' => 'string'], - 'requires' => ['customId'], - ], - ], -]; diff --git a/addons/holded/jobs/sync-products-by-sku.php b/addons/holded/jobs/sync-products-by-sku.php deleted file mode 100644 index 1be9279c..00000000 --- a/addons/holded/jobs/sync-products-by-sku.php +++ /dev/null @@ -1,134 +0,0 @@ - __('Sync woo products', 'forms-bridge'), - 'description' => __( - 'Search for products from the WooCommerce order by sku on Holded and creates new ones if someone does not exists', - 'forms-bridge' - ), - 'method' => 'forms_bridge_holded_sync_products_by_sku', - 'input' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - 'required' => ['sku', 'name', 'price'], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_holded_sync_products_by_sku($payload, $bridge) -{ - $product_refs = []; - foreach ($payload['line_items'] as $line_item) { - if (empty($line_item['product']['sku'])) { - return new WP_Error( - "SKU is required on product {$line_item['product']['name']}" - ); - } - - $product_refs[] = $line_item['product']['sku']; - } - - $response = $bridge - ->patch([ - 'name' => 'holded-search-products', - 'endpoint' => '/api/invoicing/v1/products', - 'method' => 'GET', - ]) - ->submit(); - - if (is_wp_error($response)) { - return $response; - } - - foreach ($payload['line_items'] as $line_item) { - $product = null; - foreach ($response['data'] as $candidate) { - if ($candidate['sku'] === $line_item['product']['sku']) { - $product = $candidate; - break; - } - } - - if (!$product) { - $product_response = $bridge - ->patch([ - 'name' => 'holded-sync-product-by-sku', - 'endpoint' => '/api/invoicing/v1/products', - 'method' => 'POST', - ]) - ->submit([ - 'kind' => 'simple', - 'name' => $line_item['product']['name'], - 'price' => $line_item['product']['price'], - 'sku' => $line_item['product']['sku'], - ]); - - if (is_wp_error($product_response)) { - return $product_response; - } - } - } - return $payload; -} diff --git a/addons/holded/templates/appointments.php b/addons/holded/templates/appointments.php deleted file mode 100644 index 91f54bd7..00000000 --- a/addons/holded/templates/appointments.php +++ /dev/null @@ -1,394 +0,0 @@ - __('Appointments', 'forms-bridge'), - 'description' => __( - 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Appointments', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/crm/v1/events', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'event_name', - 'label' => __('Event name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => 'Web appointment', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'kind', - 'label' => __('Event type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'value' => 'meeting', - 'label' => __('Meeting', 'forms-bridge'), - ], - [ - 'value' => 'call', - 'label' => __('Call', 'forms-bridge'), - ], - [ - 'value' => 'lunch', - 'label' => __('Lunch', 'forms-bridge'), - ], - [ - 'value' => 'dinner', - 'label' => __('Dinner', 'forms-bridge'), - ], - ], - 'required' => true, - 'default' => 'meeting', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'duration', - 'label' => __('Duration (Hours)', 'forms-bridge'), - 'type' => 'number', - 'default' => 1, - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'type', - 'label' => __('Contact type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Unspecified', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('Client', 'forms-bridge'), - 'value' => 'client', - ], - [ - 'label' => __('Lead', 'forms-bridge'), - 'value' => 'lead', - ], - [ - 'label' => __('Supplier', 'forms-bridge'), - 'value' => 'supplier', - ], - [ - 'label' => __('Debtor', 'forms-bridge'), - 'value' => 'debtor', - ], - [ - 'label' => __('Creditor', 'forms-bridge'), - 'value' => 'creditor', - ], - ], - 'required' => true, - 'default' => '0', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/crm/v1/events', - 'custom_fields' => [ - [ - 'name' => 'isperson', - 'value' => '1', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'isperson', - 'to' => 'isperson', - 'cast' => 'integer', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => '?tags', - 'to' => 'event_tags', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'datetime', - 'to' => 'date', - 'cast' => 'string', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'country', - 'cast' => 'null', - ], - [ - 'from' => 'country_code', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'event_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?event_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => [ - 'date-fields-to-date', - 'appointment-dates', - 'iso2-country-code', - 'prefix-vatnumber', - 'contact-id', - ], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'name' => 'date', - 'label' => __('Date', 'forms-bridge'), - 'type' => 'date', - 'required' => true, - ], - [ - 'name' => 'hour', - 'label' => __('Hour', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - [ - 'label' => __('1 AM', 'forms-bridge'), - 'value' => '01', - ], - [ - 'label' => __('2 AM', 'forms-bridge'), - 'value' => '02', - ], - [ - 'label' => __('3 AM', 'forms-bridge'), - 'value' => '03', - ], - [ - 'label' => __('4 AM', 'forms-bridge'), - 'value' => '04', - ], - [ - 'label' => __('5 AM', 'forms-bridge'), - 'value' => '05', - ], - [ - 'label' => __('6 AM', 'forms-bridge'), - 'value' => '06', - ], - [ - 'label' => __('7 AM', 'forms-bridge'), - 'value' => '07', - ], - [ - 'label' => __('8 AM', 'forms-bridge'), - 'value' => '08', - ], - [ - 'label' => __('9 AM', 'forms-bridge'), - 'value' => '09', - ], - [ - 'label' => __('10 AM', 'forms-bridge'), - 'value' => '10', - ], - [ - 'label' => __('11 AM', 'forms-bridge'), - 'value' => '11', - ], - [ - 'label' => __('12 AM', 'forms-bridge'), - 'value' => '12', - ], - [ - 'label' => __('1 PM', 'forms-bridge'), - 'value' => '13', - ], - [ - 'label' => __('2 PM', 'forms-bridge'), - 'value' => '14', - ], - [ - 'label' => __('3 PM', 'forms-bridge'), - 'value' => '15', - ], - [ - 'label' => __('4 PM', 'forms-bridge'), - 'value' => '16', - ], - [ - 'label' => __('5 PM', 'forms-bridge'), - 'value' => '17', - ], - [ - 'label' => __('6 PM', 'forms-bridge'), - 'value' => '18', - ], - [ - 'label' => __('7 PM', 'forms-bridge'), - 'value' => '19', - ], - [ - 'label' => __('8 PM', 'forms-bridge'), - 'value' => '20', - ], - [ - 'label' => __('9 PM', 'forms-bridge'), - 'value' => '21', - ], - [ - 'label' => __('10 PM', 'forms-bridge'), - 'value' => '22', - ], - [ - 'label' => __('11 PM', 'forms-bridge'), - 'value' => '23', - ], - [ - 'label' => __('12 PM', 'forms-bridge'), - 'value' => '24', - ], - ], - ], - [ - 'name' => 'minute', - 'label' => __('Minute', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - ['label' => '00', 'value' => '00.0'], - ['label' => '05', 'value' => '05'], - ['label' => '10', 'value' => '10'], - ['label' => '15', 'value' => '15'], - ['label' => '20', 'value' => '20'], - ['label' => '25', 'value' => '25'], - ['label' => '30', 'value' => '30'], - ['label' => '35', 'value' => '35'], - ['label' => '40', 'value' => '40'], - ['label' => '45', 'value' => '45'], - ['label' => '50', 'value' => '50'], - ['label' => '55', 'value' => '55'], - ], - ], - ], - ], -]; diff --git a/addons/holded/templates/company-contacts.php b/addons/holded/templates/company-contacts.php deleted file mode 100644 index 538a6618..00000000 --- a/addons/holded/templates/company-contacts.php +++ /dev/null @@ -1,203 +0,0 @@ - __('Company Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form for companies template. The resulting bridge will convert form submissions into new companies linked to contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Contacts', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/contacts', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'type', - 'label' => __('Contact type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Unspecified', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('Client', 'forms-bridge'), - 'value' => 'client', - ], - [ - 'label' => __('Lead', 'forms-bridge'), - 'value' => 'lead', - ], - [ - 'label' => __('Supplier', 'forms-bridge'), - 'value' => 'supplier', - ], - [ - 'label' => __('Debtor', 'forms-bridge'), - 'value' => 'debtor', - ], - [ - 'label' => __('Creditor', 'forms-bridge'), - 'value' => 'creditor', - ], - ], - 'required' => true, - 'default' => '0', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/contacts', - 'custom_fields' => [ - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => 'contact_name', - 'to' => 'contactPersons[0].name', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'contactPersons[0].email', - 'cast' => 'copy', - ], - [ - 'from' => 'phone', - 'to' => 'contactPersons[0].phone', - 'cast' => 'copy', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - ], - 'workflow' => [ - 'skip-if-contact-exists', - 'iso2-country-code', - 'prefix-vatnumber', - ], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Company', 'forms-bridge'), - 'name' => 'company_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/company-leads.php b/addons/holded/templates/company-leads.php deleted file mode 100644 index 34565366..00000000 --- a/addons/holded/templates/company-leads.php +++ /dev/null @@ -1,214 +0,0 @@ - __('Company Leads', 'forms-bridge'), - 'description' => __( - 'Lead form template. The resulting bridge will convert form submissions into leads linked to new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Leads', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/crm/v1/leads', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'funnelId', - 'label' => __('Funnel', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/crm/v1/funnels', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'value', - 'label' => __('Lead value', 'forms-bridge'), - 'description' => __( - 'Estimated deal value in currency units', - 'forms-bridge' - ), - 'type' => 'number', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'potential', - 'description' => __( - 'Deal potential as a percentage', - 'forms-bridge' - ), - 'label' => __('Lead potential (%)', 'forms-bridge'), - 'type' => 'number', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Contact tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/crm/v1/leads', - 'custom_fields' => [ - [ - 'name' => 'type', - 'value' => 'lead', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => 'contact_name', - 'to' => 'contactPersons[0].name', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'contactPersons[0].email', - 'cast' => 'copy', - ], - [ - 'from' => 'phone', - 'to' => 'contactPersons[0].phone', - 'cast' => 'copy', - ], - [ - 'from' => '?tags', - 'to' => 'lead_tags', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => '?lead_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'prefix-vatnumber', 'contact-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Company', 'forms-bridge'), - 'name' => 'company_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/contacts.php b/addons/holded/templates/contacts.php deleted file mode 100644 index ec85d61d..00000000 --- a/addons/holded/templates/contacts.php +++ /dev/null @@ -1,191 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Contacts', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/contacts', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'type', - 'label' => __('Contact type', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Unspecified', 'forms-bridge'), - 'value' => '0', - ], - [ - 'label' => __('Client', 'forms-bridge'), - 'value' => 'client', - ], - [ - 'label' => __('Lead', 'forms-bridge'), - 'value' => 'lead', - ], - [ - 'label' => __('Supplier', 'forms-bridge'), - 'value' => 'supplier', - ], - [ - 'label' => __('Debtor', 'forms-bridge'), - 'value' => 'debtor', - ], - [ - 'label' => __('Creditor', 'forms-bridge'), - 'value' => 'creditor', - ], - ], - 'required' => true, - 'default' => '0', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/contacts', - 'custom_fields' => [ - [ - 'name' => 'isperson', - 'value' => '1', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'isperson', - 'to' => 'isperson', - 'cast' => 'integer', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - ], - [], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - ], - 'workflow' => [ - 'skip-if-contact-exists', - 'iso2-country-code', - 'prefix-vatnumber', - ], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/leads.php b/addons/holded/templates/leads.php deleted file mode 100644 index 3e9a0a1a..00000000 --- a/addons/holded/templates/leads.php +++ /dev/null @@ -1,204 +0,0 @@ - __('Leads', 'forms-bridge'), - 'description' => __( - 'Lead form template. The resulting bridge will convert form submissions into leads linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Leads', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/crm/v1/leads', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'funnelId', - 'label' => __('Funnel', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/crm/v1/funnels', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'value', - 'label' => __('Lead value', 'forms-bridge'), - 'description' => __( - 'Estimated deal value in currency units', - 'forms-bridge' - ), - 'type' => 'number', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'potential', - 'description' => __( - 'Deal potential as a percentage', - 'forms-bridge' - ), - 'label' => __('Lead potential (%)', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - 'max' => 100, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Contact tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/crm/v1/leads', - 'custom_fields' => [ - [ - 'name' => 'isperson', - 'value' => '1', - ], - [ - 'name' => 'type', - 'value' => 'lead', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'isperson', - 'to' => 'isperson', - 'cast' => 'integer', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => '?tags', - 'to' => 'lead_tags', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => '?lead_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'prefix-vatnumber', 'contact-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/product-company-quotations.php b/addons/holded/templates/product-company-quotations.php deleted file mode 100644 index c89a4d08..00000000 --- a/addons/holded/templates/product-company-quotations.php +++ /dev/null @@ -1,219 +0,0 @@ - __('Product Company Quotations', 'forms-bridge'), - 'description' => __( - 'Product quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Product Company Quotations', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/documents/estimate', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'sku', - 'label' => __('Product', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/invoicing/v1/products', - 'finger' => [ - 'value' => '[].sku', - 'label' => '[].name', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/documents/estimate', - 'custom_fields' => [ - [ - 'name' => 'type', - 'value' => 'client', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => '?tags', - 'to' => 'quotation_tags', - 'cast' => 'inherit', - ], - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => 'sku', - 'to' => 'items[0].sku', - 'cast' => 'string', - ], - [ - 'from' => 'quantity', - 'to' => 'items[0].units', - 'cast' => 'integer', - ], - [ - 'from' => 'contact_name', - 'to' => 'contactPersons[0].name', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'contactPersons[0].email', - 'cast' => 'copy', - ], - [ - 'from' => 'phone', - 'to' => 'contactPersons[0].phone', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => '?quotation_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'prefix-vatnumber', 'contact-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Quantity', 'forms-bridge'), - 'name' => 'quantity', - 'type' => 'number', - 'default' => 1, - 'min' => 0, - 'required' => true, - ], - [ - 'label' => __('Company', 'forms-bridge'), - 'name' => 'company_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/product-quotations.php b/addons/holded/templates/product-quotations.php deleted file mode 100644 index 3d384b17..00000000 --- a/addons/holded/templates/product-quotations.php +++ /dev/null @@ -1,204 +0,0 @@ - __('Product Quotations', 'forms-bridge'), - 'description' => __( - 'Product quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Product Quotations', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/documents/estimate', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'sku', - 'label' => __('Product', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/invoicing/v1/products', - 'finger' => [ - 'value' => '[].sku', - 'label' => '[].name', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/documents/estimate', - 'custom_fields' => [ - [ - 'name' => 'type', - 'value' => 'client', - ], - [ - 'name' => 'isperson', - 'value' => '1', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'isperson', - 'to' => 'isperson', - 'cast' => 'integer', - ], - [ - 'from' => '?tags', - 'to' => 'quotation_tags', - 'cast' => 'inherit', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => 'sku', - 'to' => 'items[0].sku', - 'cast' => 'string', - ], - [ - 'from' => 'quantity', - 'to' => 'items[0].units', - 'cast' => 'integer', - ], - ], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => '?quotation_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'prefix-vatnumber', 'contact-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Quantity', 'forms-bridge'), - 'name' => 'quantity', - 'type' => 'number', - 'default' => 1, - 'min' => 0, - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/service-company-quotations.php b/addons/holded/templates/service-company-quotations.php deleted file mode 100644 index 9f2fc33e..00000000 --- a/addons/holded/templates/service-company-quotations.php +++ /dev/null @@ -1,219 +0,0 @@ - __('Service Company Quotations', 'forms-bridge'), - 'description' => __( - 'Service quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Service Company Quotations', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/documents/estimate', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'serviceId', - 'label' => __('Service', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/invoicing/v1/services', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].name', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/documents/estimate', - 'custom_fields' => [ - [ - 'name' => 'type', - 'value' => 'client', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => '?tags', - 'to' => 'quotation_tags', - 'cast' => 'inherit', - ], - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => 'serviceId', - 'to' => 'items[0].serviceId', - 'cast' => 'string', - ], - [ - 'from' => 'quantity', - 'to' => 'items[0].units', - 'cast' => 'integer', - ], - [ - 'from' => 'contact_name', - 'to' => 'contactPersons[0].name', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'contactPersons[0].email', - 'cast' => 'copy', - ], - [ - 'from' => 'phone', - 'to' => 'contactPersons[0].phone', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => '?quotation_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'prefix-vatnumber', 'contact-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Quantity', 'forms-bridge'), - 'name' => 'quantity', - 'type' => 'number', - 'default' => 1, - 'min' => 0, - 'required' => true, - ], - [ - 'label' => __('Company', 'forms-bridge'), - 'name' => 'company_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/service-quotations.php b/addons/holded/templates/service-quotations.php deleted file mode 100644 index 184ffea5..00000000 --- a/addons/holded/templates/service-quotations.php +++ /dev/null @@ -1,204 +0,0 @@ - __('Service Quotations', 'forms-bridge'), - 'description' => __( - 'Service quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Service Quotations', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/documents/estimate', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'serviceId', - 'label' => __('Service', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/invoicing/v1/services', - 'finger' => [ - 'value' => '[].id', - 'label' => '[].name', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/documents/estimate', - 'custom_fields' => [ - [ - 'name' => 'type', - 'value' => 'client', - ], - [ - 'name' => 'isperson', - 'value' => '1', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'isperson', - 'to' => 'isperson', - 'cast' => 'integer', - ], - [ - 'from' => '?tags', - 'to' => 'quotation_tags', - 'cast' => 'inherit', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'code', - 'to' => 'vatnumber', - 'cast' => 'copy', - ], - [ - 'from' => 'address', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => 'postalCode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => 'city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => 'serviceId', - 'to' => 'items[0].serviceId', - 'cast' => 'string', - ], - [ - 'from' => 'quantity', - 'to' => 'items[0].units', - 'cast' => 'integer', - ], - ], - [ - [ - 'from' => 'country', - 'to' => 'countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'countryCode', - 'to' => 'billAddress.countryCode', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => '?quotation_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'prefix-vatnumber', 'contact-id'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Quantity', 'forms-bridge'), - 'name' => 'quantity', - 'type' => 'number', - 'default' => 1, - 'min' => 0, - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'code', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'address', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'postalCode', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - ], - ], -]; diff --git a/addons/holded/templates/woo-product-orders-sync.php b/addons/holded/templates/woo-product-orders-sync.php deleted file mode 100644 index 68e43479..00000000 --- a/addons/holded/templates/woo-product-orders-sync.php +++ /dev/null @@ -1,378 +0,0 @@ - __('Product Orders + Sync', 'forms-bridge'), - 'description' => __( - 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Holded by product SKUs.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/documents/salesorder', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/documents/salesorder', - 'custom_fields' => [ - [ - 'name' => 'type', - 'value' => 'client', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - [ - 'name' => 'approveDoc', - 'value' => '1', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'approveDoc', - 'to' => 'approveDoc', - 'cast' => 'boolean', - ], - [ - 'from' => 'id', - 'to' => 'customFields.wp_order_id', - 'cast' => 'integer', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'CustomId', - 'cast' => 'string', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => '?billing.state', - 'to' => 'billAddress.province', - 'cast' => 'string', - ], - [ - 'from' => '?billing.country', - 'to' => 'billAddress.country', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'shippingAddresses[0].address', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'shippingAddresses[0].postalCode', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'shippingAddresses[0].city', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.state', - 'to' => 'shippingAddresses[0].province', - 'cast' => 'string', - ], - [ - 'from' => 'shipping.country', - 'to' => 'shippingAddresses[0].country', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'notes', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].name', - 'to' => 'items[].name', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'items[].subtotal', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].total_tax.percentage', - 'to' => 'items[].tax', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'items[].units', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'items[].sku', - 'cast' => 'copy', - ], - [ - 'from' => '?tags', - 'to' => 'order_tags', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => '?order_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['sync-products-by-sku', 'contact-id'], - ], -]; diff --git a/addons/holded/templates/woo-product-orders.php b/addons/holded/templates/woo-product-orders.php deleted file mode 100644 index b592d583..00000000 --- a/addons/holded/templates/woo-product-orders.php +++ /dev/null @@ -1,376 +0,0 @@ - __('Product Orders', 'forms-bridge'), - 'description' => __( - 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. To work propertly, the bridge needs that your WooCommerce product sku values matches with the holded ones.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/invoicing/v1/documents/salesorder', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Tags', 'forms-bridge'), - 'description' => __('Tags separated by commas', 'forms-bridge'), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'endpoint' => '/api/invoicing/v1/documents/salesorder', - 'custom_fields' => [ - [ - 'name' => 'type', - 'value' => 'client', - ], - [ - 'name' => 'defaults.language', - 'value' => '$locale', - ], - [ - 'name' => 'approveDoc', - 'value' => '1', - ], - [ - 'name' => 'date', - 'value' => '$timestamp', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'approveDoc', - 'to' => 'approveDoc', - 'cast' => 'boolean', - ], - [ - 'from' => 'id', - 'to' => 'customFields.wp_order_id', - 'cast' => 'integer', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'CustomId', - 'cast' => 'string', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'billAddress.address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'billAddress.city', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'billAddress.postalCode', - 'cast' => 'string', - ], - [ - 'from' => '?billing.state', - 'to' => 'billAddress.province', - 'cast' => 'string', - ], - [ - 'from' => '?billing.country', - 'to' => 'billAddress.country', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'shippingAddresses[0].address', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'shippingAddresses[0].postalCode', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'shippingAddresses[0].city', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.state', - 'to' => 'shippingAddresses[0].province', - 'cast' => 'string', - ], - [ - 'from' => 'shipping.country', - 'to' => 'shippingAddresses[0].country', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'notes', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].name', - 'to' => 'items[].name', - 'cast' => 'string', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'items[].subtotal', - 'cast' => 'number', - ], - [ - 'from' => 'line_items[].total_tax.percentage', - 'to' => 'items[].tax', - 'cast' => 'number', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'items[].units', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'items[].sku', - 'cast' => 'string', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => '?tags', - 'to' => 'order_tags', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => '?order_tags', - 'to' => 'tags', - 'cast' => 'inherit', - ], - ], - ], - 'workflow' => ['contact-id'], - ], -]; diff --git a/addons/listmonk/class-listmonk-addon.php b/addons/listmonk/class-listmonk-addon.php deleted file mode 100644 index 7003c66a..00000000 --- a/addons/listmonk/class-listmonk-addon.php +++ /dev/null @@ -1,129 +0,0 @@ - '__listmonk-' . time(), - 'endpoint' => '/api/lists', - 'method' => 'GET', - 'backend' => $backend, - ]); - - $response = $bridge->submit(); - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge = new Listmonk_Form_Bridge([ - 'name' => '__listmonk-' . time(), - 'method' => 'GET', - 'endpoint' => $endpoint, - 'backend' => $backend, - ]); - - return $bridge->submit(); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array - */ - public function get_endpoint_schema($endpoint, $backend) - { - if ($endpoint === '/api/subscribers') { - return [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'status', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'lists', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'number'], - ], - ], - [ - 'name' => 'preconfirm_subscriptions', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'attribs', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - ], - ], - ]; - } - - return []; - } -} - -Listmonk_Addon::setup(); diff --git a/addons/listmonk/class-listmonk-form-bridge.php b/addons/listmonk/class-listmonk-form-bridge.php deleted file mode 100644 index 77be6c63..00000000 --- a/addons/listmonk/class-listmonk-form-bridge.php +++ /dev/null @@ -1,71 +0,0 @@ -get_error_data()['response'] ?? null; - - $code = $error_response['response']['code'] ?? null; - if ($code !== 409) { - return $response; - } - - if ( - !isset($payload['email']) || - $this->endpoint !== '/api/subscribers' - ) { - return $response; - } - - $get_response = $this->patch([ - 'name' => 'listmonk-get-subscriber-by-email', - 'method' => 'GET', - ])->submit([ - 'per_page' => '1', - 'query' => "subscribers.email = '{$payload['email']}'", - ]); - - if (is_wp_error($get_response)) { - return $response; - } - - $subscriber_id = $get_response['data']['data']['results'][0]['id']; - - return $this->patch([ - 'name' => 'listmonk-update-subscriber', - 'method' => 'PUT', - 'endpoint' => $this->endpoint . '/' . $subscriber_id, - ])->submit($payload); - } - - return $response; - } -} diff --git a/addons/listmonk/hooks.php b/addons/listmonk/hooks.php deleted file mode 100644 index 0d81cc17..00000000 --- a/addons/listmonk/hooks.php +++ /dev/null @@ -1,148 +0,0 @@ - [ - [ - 'ref' => '#backend', - 'name' => 'name', - 'description' => __( - 'Label of the Listmonk API backend connection', - 'forms-bridge' - ), - 'default' => 'Listmonk API', - ], - [ - 'ref' => '#credential', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'schema', - 'type' => 'text', - 'value' => 'Token', - ], - [ - 'ref' => '#credential', - 'name' => 'client_id', - 'label' => __('API user', 'forms-bridge'), - 'description' => __( - 'You have to generate an API user on your listmonk instance. See the documentation for more information', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_secret', - 'label' => __('API token', 'forms-bridge'), - 'description' => __( - 'Token of the API user. The token will be shown only once on user creation time, be sure to copy its value and store it in a save place', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'lists', - 'label' => __('Mailing lists', 'forms-bridge'), - 'description' => __( - 'Select, at least, one list that users will subscribe to', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/api/lists', - 'finger' => [ - 'value' => 'data.results[].id', - 'label' => 'data.results[].name', - ], - ], - 'is_multi' => true, - 'required' => true, - ], - ], - 'bridge' => [ - 'backend' => 'Listmonk API', - 'endpoint' => '', - 'method' => 'POST', - ], - 'backend' => [ - 'name' => 'Listmonk', - ], - 'credential' => [ - 'name' => '', - 'schema' => 'Token', - 'client_id' => '', - 'client_secret' => '', - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'listmonk-') !== 0) { - return $data; - } - - $index = array_search( - 'lists', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = &$data['bridge']['custom_fields'][$index]; - if (is_array($field['value'])) { - for ($i = 0; $i < count($field['value']); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "lists[{$i}]", - 'value' => (int) $field['value'][$i], - ]; - - $data['bridge']['mutations'][0][] = [ - 'from' => "lists[{$i}]", - 'to' => "lists[{$i}]", - 'cast' => 'integer', - ]; - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - $data['bridge']['custom_fields'] = array_values( - $data['bridge']['custom_fields'] - ); - } - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/listmonk/jobs/skip-subscription.php b/addons/listmonk/jobs/skip-subscription.php deleted file mode 100644 index c75fb2e6..00000000 --- a/addons/listmonk/jobs/skip-subscription.php +++ /dev/null @@ -1,31 +0,0 @@ - __('Skip subscription', 'forms-bridge'), - 'description' => __( - 'Skip subscription if the listmonk field is not true', - 'forms-bridge' - ), - 'method' => 'forms_bridge_listmonk_skip_subscription', - 'input' => [ - [ - 'name' => 'listmonk', - 'schema' => ['type' => 'boolean'], - 'required' => true, - ], - ], - 'output' => [], -]; - -function forms_bridge_listmonk_skip_subscription($payload) -{ - if ($payload['listmonk'] != true) { - return; - } - - return $payload; -} diff --git a/addons/listmonk/templates/optin-subscriptions.php b/addons/listmonk/templates/optin-subscriptions.php deleted file mode 100644 index 7cd9cd08..00000000 --- a/addons/listmonk/templates/optin-subscriptions.php +++ /dev/null @@ -1,103 +0,0 @@ - __('Opt-in Subscriptions', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions with a double opt-in confirmation check.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/subscribers', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'status', - 'label' => __('Subscription status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Enabled', 'forms-bridge'), - 'value' => 'enabled', - ], - [ - 'label' => __('Disabled', 'forms-bridge'), - 'value' => 'blocklisted', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Opt-in subscriptions', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Opt-in subscriptions', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'your-email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'your-name', - 'label' => __('Your name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'backend' => [ - 'headers' => [ - [ - 'name' => 'Content-Type', - 'value' => 'application/json', - ], - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/api/subscribers', - 'custom_fields' => [ - [ - 'name' => 'attribs.locale', - 'value' => '$locale', - ], - [ - 'name' => 'preconfirm_subscriptions', - 'value' => '0', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'your-email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'preconfirm_subscriptions', - 'to' => 'preconfirm_subscriptions', - 'cast' => 'boolean', - ], - ], - ], - ], -]; diff --git a/addons/listmonk/templates/subscriptions.php b/addons/listmonk/templates/subscriptions.php deleted file mode 100644 index 63852c55..00000000 --- a/addons/listmonk/templates/subscriptions.php +++ /dev/null @@ -1,108 +0,0 @@ - __('Subscriptions', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/subscribers', - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'status', - 'label' => __('Subscription status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Enabled', 'forms-bridge'), - 'value' => 'enabled', - ], - [ - 'label' => __('Disabled', 'forms-bridge'), - 'value' => 'blocklisted', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Subscriptions', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Subscriptions', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'your-email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'your-name', - 'label' => __('Your name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'backend' => [ - 'headers' => [ - [ - 'name' => 'Content-Type', - 'value' => 'application/json', - ], - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/api/subscribers', - 'custom_fields' => [ - [ - 'name' => 'attribs.locale', - 'value' => '$locale', - ], - [ - 'name' => 'preconfirm_subscriptions', - 'value' => '1', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'your-email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'preconfirm_subscriptions', - 'to' => 'preconfirm_subscriptions', - 'cast' => 'boolean', - ], - ], - ], - ], -]; diff --git a/addons/listmonk/templates/woo-optin-subscriptions.php b/addons/listmonk/templates/woo-optin-subscriptions.php deleted file mode 100644 index 3be2f2d0..00000000 --- a/addons/listmonk/templates/woo-optin-subscriptions.php +++ /dev/null @@ -1,296 +0,0 @@ - __('Opt-in Subscriptions', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list with a double opt in check.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/subscribers', - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'status', - 'label' => __('Subscription status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Enabled', 'forms-bridge'), - 'value' => 'enabled', - ], - [ - 'label' => __('Disabled', 'forms-bridge'), - 'value' => 'blocklisted', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/api/subscribers', - 'custom_fields' => [ - [ - 'name' => 'attribs.locale', - 'value' => '$locale', - ], - [ - 'name' => 'preconfirm_subscriptions', - 'value' => '0', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'preconfirm_subscriptions', - 'to' => 'preconfirm_subscriptions', - 'cast' => 'boolean', - ], - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'attribs.wp_customer_id', - 'cast' => 'string', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => 'billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - ], - ], -]; diff --git a/addons/listmonk/templates/woo-subscriptions.php b/addons/listmonk/templates/woo-subscriptions.php deleted file mode 100644 index 28273b6b..00000000 --- a/addons/listmonk/templates/woo-subscriptions.php +++ /dev/null @@ -1,296 +0,0 @@ - __('Subscriptions', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/api/subscribers', - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'status', - 'label' => __('Subscription status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Enabled', 'forms-bridge'), - 'value' => 'enabled', - ], - [ - 'label' => __('Disabled', 'forms-bridge'), - 'value' => 'blocklisted', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/api/subscribers', - 'custom_fields' => [ - [ - 'name' => 'attribs.locale', - 'value' => '$locale', - ], - [ - 'name' => 'preconfirm_subscriptions', - 'value' => '1', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'preconfirm_subscriptions', - 'to' => 'preconfirm_subscriptions', - 'cast' => 'boolean', - ], - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'attribs.wp_customer_id', - 'cast' => 'string', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => 'billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - ], - ], -]; diff --git a/addons/mailchimp/class-mailchimp-addon.php b/addons/mailchimp/class-mailchimp-addon.php deleted file mode 100644 index ed5f030f..00000000 --- a/addons/mailchimp/class-mailchimp-addon.php +++ /dev/null @@ -1,206 +0,0 @@ - '__mailchimp-' . time(), - 'endpoint' => '/3.0/lists', - 'method' => 'GET', - 'backend' => $backend, - ]); - - $response = $bridge->submit(); - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge = new Mailchimp_Form_Bridge([ - 'name' => '__mailchimp-' . time(), - 'method' => 'GET', - 'endpoint' => $endpoint, - 'backend' => $backend, - ]); - - return $bridge->submit(); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array - */ - public function get_endpoint_schema($endpoint, $backend) - { - if (strstr($endpoint, '/lists/') !== false) { - $fields = [ - [ - 'name' => 'email_address', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'status', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'email_type', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'interests', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - ], - ], - [ - 'name' => 'language', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vip', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'location', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'latitude' => ['type' => 'number'], - 'longitude' => ['type' => 'number'], - ], - ], - ], - [ - 'name' => 'marketing_permissions', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'marketing_permission_id' => [ - 'type' => 'string', - ], - 'enabled' => ['type' => 'boolean'], - ], - ], - ], - ], - [ - 'name' => 'ip_signup', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'ip_opt', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'timestamp_opt', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'tags', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - ], - ], - [ - 'name' => 'merge_fields', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - ], - ], - ]; - - $fields_endpoint = str_replace( - '/members', - '/merge-fields', - $endpoint - ); - - $bridge = new Mailchimp_Form_Bridge([ - 'name' => '__mailchimp-' . time(), - 'endpoint' => $fields_endpoint, - 'method' => 'GET', - 'backend' => $backend, - ]); - - $response = $bridge->submit(); - - if (is_wp_error($response)) { - return []; - } - - foreach ($response['data']['merge_fields'] as $field) { - $fields[] = [ - 'name' => 'merge_fields.' . $field['tag'], - 'schema' => ['type' => 'string'], - ]; - } - - return $fields; - } - - return []; - } -} - -Mailchimp_Addon::setup(); diff --git a/addons/mailchimp/class-mailchimp-form-bridge.php b/addons/mailchimp/class-mailchimp-form-bridge.php deleted file mode 100644 index df325eb2..00000000 --- a/addons/mailchimp/class-mailchimp-form-bridge.php +++ /dev/null @@ -1,107 +0,0 @@ -get_error_data()['response'] ?? null; - - $code = $error_response['response']['code'] ?? null; - if ($code !== 400) { - return $response; - } - - try { - $body = json_decode($error_response['body'] ?? '', true); - $title = $body['title'] ?? null; - } catch (TypeError) { - return $response; - } - - if ($title === 'Member Exists') { - if ( - !preg_match( - '/(?<=lists\/).+(?=\/members)/', - $this->endpoint, - $matches - ) - ) { - return $response; - } - - $list_id = $matches[0]; - - $search_response = $this->patch([ - 'name' => 'mailchimp-search-member', - 'method' => 'GET', - 'endpoint' => '/3.0/search-members', - ])->submit([ - 'fiels' => 'exact_matches.members.id', - 'list_id' => $list_id, - 'query' => $payload['email_address'], - ]); - - if (is_wp_error($search_response)) { - return $response; - } - - $member_id = - $search_response['data']['exact_matches']['members'][0][ - 'id' - ] ?? null; - - if (!$member_id) { - return $response; - } - - $update_endpoint = "/3.0/lists/{$list_id}/members/{$member_id}"; - if ( - strstr($this->endpoint, 'skip_merge_validation') !== false - ) { - $update_endpoint .= '?skip_merge_validation=true'; - } - - $update_response = $this->patch([ - 'name' => 'mailchimp-update-subscription', - 'method' => 'PUT', - 'endpoint' => $update_endpoint, - ])->submit($payload); - - if (is_wp_error($update_response)) { - return $response; - } - - return $update_response; - } - } - - return $response; - } -} diff --git a/addons/mailchimp/hooks.php b/addons/mailchimp/hooks.php deleted file mode 100644 index aff83867..00000000 --- a/addons/mailchimp/hooks.php +++ /dev/null @@ -1,195 +0,0 @@ - [ - [ - 'ref' => '#backend', - 'name' => 'name', - 'description' => __( - 'Label of the MailChimp API backend connection', - 'forms-bridge' - ), - 'default' => 'MailChimp API', - ], - [ - 'ref' => '#backend', - 'name' => 'base_url', - 'description' => __( - 'If needed, replace the datacenter param from the URL to match your account servers.', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => array_map(function ($i) { - return [ - 'value' => "https://us{$i}.api.mailchimp.com", - 'label' => "us{$i}.api.mailchimp.com", - ]; - }, array_keys(array_fill(1, 12, null))), - 'default' => 'https://us1.api.mailchimp.com', - ], - [ - 'ref' => '#credential', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'schema', - 'type' => 'text', - 'value' => 'Basic', - ], - [ - 'ref' => '#credential', - 'name' => 'client_id', - 'type' => 'text', - 'value' => 'forms-bridge', - ], - [ - 'ref' => '#credential', - 'name' => 'client_secret', - 'label' => __('API key', 'forms-bridge'), - 'description' => __( - 'Get it from your account', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'list_id', - 'label' => __('Audience', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/3.0/lists', - 'finger' => [ - 'value' => 'lists[].id', - 'label' => 'lists[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'skip_merge_validation', - 'label' => __( - 'Skip merge fields validation', - 'forms-bridge' - ), - 'type' => 'boolean', - 'default' => false, - ], - ], - 'bridge' => [ - 'backend' => '', - 'endpoint' => '', - 'method' => 'POST', - ], - 'backend' => [ - 'name' => 'Mailchimp API', - 'base_url' => 'https://{dc}.api.mailchimp.com', - ], - 'credential' => [ - 'name' => '', - 'schema' => 'Basic', - 'client_id' => '', - 'client_secret' => '', - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'mailchimp-') !== 0) { - return $data; - } - - $index = array_search( - 'skip_merge_validation', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $endpoint = $data['bridge']['endpoint']; - $parsed = wp_parse_url($endpoint); - - $path = $parsed['path'] ?? ''; - - $query = []; - wp_parse_str($parsed['query'] ?? '', $query); - $query['skip_merge_validation'] = 'true'; - $querystr = http_build_query($query); - - $data['bridge']['endpoint'] = $path . '?' . $querystr; - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'list_id', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $list_id = $data['bridge']['custom_fields'][$index]['value']; - $data['bridge']['endpoint'] = preg_replace( - '/\{list_id\}/', - $list_id, - $data['bridge']['endpoint'] - ); - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'tags', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = &$data['bridge']['custom_fields'][$index]; - - $tags = array_filter( - array_map('trim', explode(',', strval($field['value']))) - ); - for ($i = 0; $i < count($tags); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "tags[{$i}]", - 'value' => $tags[$i], - ]; - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/mailchimp/jobs/list-contact.php b/addons/mailchimp/jobs/list-contact.php deleted file mode 100644 index 0709bf99..00000000 --- a/addons/mailchimp/jobs/list-contact.php +++ /dev/null @@ -1,145 +0,0 @@ - __('Audience subscription', 'forms-bridge'), - 'description' => __('Subscribe a new user to an audience', 'forms-bridge'), - 'method' => 'forms_bridge_mailchimp_audience_subscription', - 'input' => [ - [ - 'name' => 'list_id', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email_address', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'status', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email_type', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'interests', - 'schema' => [ - 'type' => 'object', - 'properties' => [], - 'additionalProperties' => true, - ], - ], - [ - 'name' => 'language', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vip', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'location', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'latitude' => ['type' => 'number'], - 'longitude' => ['type' => 'number'], - ], - 'additionalProperties' => false, - ], - ], - [ - 'name' => 'marketing_permissions', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'marketing_permission_id' => ['type' => 'string'], - 'enabled' => ['type' => 'boolean'], - ], - ], - 'additionalItems' => true, - ], - ], - [ - 'name' => 'ip_signup', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'timestamp_signup', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'ip_opt', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'timestamp_opt', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'tags', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - 'additionalItems' => true, - ], - ], - ], - 'output' => [ - [ - 'name' => 'email_address', - 'schema' => ['type' => 'string'], - ], - ], -]; - -function forms_bridge_mailchimp_audience_subscription($payload, $bridge) -{ - $contact = [ - 'email_address' => $payload['email_address'], - ]; - - $contact_fields = [ - 'status', - 'email_type', - 'interests', - 'language', - 'vip', - 'location', - 'marketing_permissions', - 'ip_signup', - 'ip_opt', - 'timestamp_opt', - 'tags', - 'merge_fields', - ]; - - foreach ($contact_fields as $field) { - if (isset($payload[$field])) { - $contact[$field] = $payload[$field]; - } - } - - $response = $bridge - ->patch([ - 'endpoint' => "/3.0/lists/{$payload['list_id']}/members", - 'method' => 'POST', - 'name' => 'mailchimp-subscribe-member-to-list', - ]) - ->submit($contact); - - if (is_wp_error($response)) { - return $response; - } - - $payload['email_address'] = $response['data']['email_address']; - return $payload; -} diff --git a/addons/mailchimp/jobs/skip-subscription.php b/addons/mailchimp/jobs/skip-subscription.php deleted file mode 100644 index 6fba8756..00000000 --- a/addons/mailchimp/jobs/skip-subscription.php +++ /dev/null @@ -1,31 +0,0 @@ - __('Skip subscription', 'forms-bridge'), - 'description' => __( - 'Skip subscription if the mailchimp field is not true', - 'forms-bridge' - ), - 'method' => 'forms_bridge_mailchimp_skip_subscription', - 'input' => [ - [ - 'name' => 'mailchimp', - 'schema' => ['type' => 'boolean'], - 'required' => true, - ], - ], - 'output' => [], -]; - -function forms_bridge_mailchimp_skip_subscription($payload) -{ - if ($payload['mailchimp'] != true) { - return; - } - - return $payload; -} diff --git a/addons/mailchimp/templates/subscription.php b/addons/mailchimp/templates/subscription.php deleted file mode 100644 index fb63c70e..00000000 --- a/addons/mailchimp/templates/subscription.php +++ /dev/null @@ -1,118 +0,0 @@ - __('Subscription', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/3.0/lists/{list_id}/members', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'status', - 'label' => __('Subscription status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Subscribed', 'forms-bridge'), - 'value' => 'subscribed', - ], - [ - 'label' => __('Unsubscribed', 'forms-bridge'), - 'value' => 'unsubscribed', - ], - [ - 'label' => __('Pending', 'forms-bridge'), - 'value' => 'pending', - ], - [ - 'label' => __('Cleaned', 'forms-bridge'), - 'value' => 'cleand', - ], - [ - 'label' => __('Transactional', 'forms-bridge'), - 'value' => 'transactional', - ], - ], - 'default' => 'subscribed', - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Subscription tags', 'forms-bridge'), - 'description' => __( - 'Tag names separated by commas', - 'forms-bridge' - ), - 'type' => 'text', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Subscription', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => __('Subscription', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'email_address', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'fname', - 'label' => __('Your first name', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'lname', - 'label' => __('Your last name', 'forms-bridge'), - 'type' => 'text', - ], - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/3.0/lists/{list_id}/members', - 'custom_fields' => [ - [ - 'name' => 'language', - 'value' => '$locale', - ], - [ - 'name' => 'ip_signup', - 'value' => '$ip_address', - ], - [ - 'name' => 'timestamp_signup', - 'value' => '$iso_date', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'fname', - 'to' => 'merge_fields.FNAME', - 'cast' => 'string', - ], - [ - 'from' => 'lname', - 'to' => 'merge_fields.LNAME', - 'cast' => 'string', - ], - ], - ], - ], -]; diff --git a/addons/mailchimp/templates/woo-subscription.php b/addons/mailchimp/templates/woo-subscription.php deleted file mode 100644 index d8ea6050..00000000 --- a/addons/mailchimp/templates/woo-subscription.php +++ /dev/null @@ -1,304 +0,0 @@ - __('Subscription', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given mailchimp audience.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/3.0/lists/{list_id}/members', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'status', - 'label' => __('Subscription status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Subscribed', 'forms-bridge'), - 'value' => 'subscribed', - ], - [ - 'label' => __('Unsubscribed', 'forms-bridge'), - 'value' => 'unsubscribed', - ], - [ - 'label' => __('Pending', 'forms-bridge'), - 'value' => 'pending', - ], - [ - 'label' => __('Cleaned', 'forms-bridge'), - 'value' => 'cleand', - ], - [ - 'label' => __('Transactional', 'forms-bridge'), - 'value' => 'transactional', - ], - ], - 'default' => 'subscribed', - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tags', - 'label' => __('Subscription tags', 'forms-bridge'), - 'description' => __( - 'Tag names separated by commas', - 'forms-bridge' - ), - 'type' => 'text', - ], - ], - 'bridge' => [ - 'method' => 'POST', - 'endpoint' => '/3.0/lists/{list_id}/members', - 'custom_fields' => [ - [ - 'name' => 'language', - 'value' => '$locale', - ], - [ - 'name' => 'timestamp_signup', - 'value' => '$iso_date', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'merge_fields.FNAME', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'merge_fields.LNAME', - 'cast' => 'string', - ], - [ - 'from' => 'billing.email', - 'to' => 'email_address', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'string', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - ], - ], -]; diff --git a/addons/nextcloud/class-nextcloud-addon.php b/addons/nextcloud/class-nextcloud-addon.php deleted file mode 100644 index b2776a1e..00000000 --- a/addons/nextcloud/class-nextcloud-addon.php +++ /dev/null @@ -1,136 +0,0 @@ -addon === 'nextcloud') { - return false; - } - - return $prune; - }, - 5, - 2 - ); - } - /** - * Performs a request against the backend to check the connexion status. - * - * @param string $backend Target backend name. - * - * @return boolean - */ - public function ping($backend) - { - $backend = FBAPI::get_backend($backend); - - if (!$backend) { - return false; - } - - $credential = $backend->credential; - if (!$credential) { - return false; - } - - $response = $backend->get( - '/remote.php/dav/files/' . rawurlencode($credential->client_id) - ); - - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend model and retrive the response data. - * - * @param string $endpoint Target model name. - * @param string $backend Target backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - return []; - } - - /** - * Performs an introspection of the backend model and returns API fields - * and accepted content type. - * - * @param string $filepath Filepath. - * @param string $backend Backend name. - * - * @return array List of fields and content type of the model. - */ - public function get_endpoint_schema($filepath, $backend) - { - $bridge = new Nextcloud_Form_Bridge([ - 'name' => '__nextcloud-' . time(), - 'endpoint' => $filepath, - 'backend' => $backend, - ]); - - $headers = $bridge->table_headers(); - if (is_wp_error($headers) || !$headers) { - return []; - } - - $fields = []; - foreach ($headers as $header) { - $fields[] = [ - 'name' => $header, - 'schema' => ['type' => 'string'], - ]; - } - - return $fields; - } -} - -Nextcloud_Addon::setup(); - -add_filter('http_request_args', function ($args) { - $args['timeout'] = 30; - return $args; -}); diff --git a/addons/nextcloud/class-nextcloud-form-bridge.php b/addons/nextcloud/class-nextcloud-form-bridge.php deleted file mode 100644 index 8fd28502..00000000 --- a/addons/nextcloud/class-nextcloud-form-bridge.php +++ /dev/null @@ -1,302 +0,0 @@ -data['endpoint']); - $name = str_replace('/', '-', $endpoint); - $filepath = $uploads . '/' . $name; - - if (!is_file($filepath)) { - $touched = true; - $result = touch($filepath); - - if (!$result) { - return new WP_Error('file_permission_error'); - } - } - - return $filepath; - } - - public function table_headers() - { - $filepath = $this->filepath(); - - if (is_wp_error($filepath)) { - return $filepath; - } - - $stream = fopen($filepath, 'r'); - $line = fgets($stream); - fclose($stream); - - if ($line === false) { - return; - } - - return $this->decode_row($line); - } - - private function get_dav_modified_date($backend) - { - $response = $backend->head($this->endpoint); - - if (is_wp_error($response)) { - $error_data = $response->get_error_data(); - - $code = $error_data['response']['response']['code'] ?? null; - if ($code !== 404) { - return $response; - } - - return; - } - - $last_modified = $response['headers']['last-modified'] ?? null; - if (!$last_modified) { - return; - } - - return strtotime($last_modified); - } - - private function payload_to_headers($payload) - { - return $this->encode_row(array_keys($payload)); - } - - private function payload_to_row($payload) - { - $headers = $this->table_headers(); - if (!is_array($headers)) { - $headers = array_keys($payload); - } - - $row = []; - foreach ($headers as $header) { - $row[] = $payload[$header] ?? ''; - } - - return $this->encode_row($row); - } - - private function encode_row($row) - { - return implode( - ',', - array_map( - fn($value) => json_encode( - $value, - JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE - ), - $row - ) - ); - } - - private function decode_row($row) - { - $row = preg_replace('/\n+/', '', $row); - return array_map(function ($value) { - if ($decoded = json_decode($value)) { - return $decoded; - } - - return $value; - }, explode(',', $row)); - } - - private function add_row($payload) - { - $row = $this->payload_to_row($payload); - - $filepath = $this->filepath(); - $sock = fopen($filepath, 'r'); - $cursor = -1; - fseek($sock, $cursor, SEEK_END); - $char = fgetc($sock); - fclose($sock); - - if ($char !== "\n" && $char !== "\r") { - $row = "\n" . $row; - } - - file_put_contents($filepath, $row, FILE_APPEND); - } - - /** - * Submits submission to the backend. - * - * @param array $payload Submission data. - * @param array $attachments Submission attachments. - * - * @return array|WP_Error Http - */ - public function submit($payload = [], $attachments = []) - { - if (!$this->is_valid) { - return new WP_Error('invalid_bridge'); - } - - $backend = $this->backend; - - if (!$backend) { - return new WP_Error('invalid_bridge'); - } - - $payload = self::flatten_payload($payload); - - add_filter( - 'http_bridge_backend_url', - function ($url, $backend) { - if ($backend->name === $this->data['backend']) { - $credential = $backend->credential; - if (!$credential) { - return; - } - - $user = $credential->client_id; - [$pre] = explode($this->endpoint, $url); - $url = - preg_replace('/\/+$/', '', $pre) . - "/remote.php/dav/files/{$user}/" . - preg_replace('/^\/+/', '', $this->endpoint); - } - - return $url; - }, - 10, - 2 - ); - - $filepath = $this->filepath($touched); - - if (is_wp_error($filepath)) { - return $filepath; - } - - $dav_modified = $this->get_dav_modified_date($backend); - if (is_wp_error($dav_modified)) { - return $dav_modified; - } - - if (!$dav_modified) { - $headers = $this->payload_to_headers($payload); - $row = $this->payload_to_row($payload); - $csv = implode("\n", [$headers, $row]); - - file_put_contents($filepath, $csv); - $response = parent::submit($csv); - } else { - if ($touched) { - $headers = $this->payload_to_headers($payload); - $row = $this->payload_to_row($payload); - $csv = implode("\n", [$headers, $row]); - - file_put_contents($filepath, $csv); - $response = parent::submit($csv); - } else { - $local_modified = filemtime($filepath); - - if ($dav_modified > $local_modified) { - $response = $backend->get( - $this->endpoint, - [], - [], - [ - 'stream' => true, - 'filename' => $filepath, - ] - ); - - if (is_wp_error($response)) { - return $response; - } - } - - $this->add_row($payload); - - $csv = file_get_contents($filepath); - $response = parent::submit($csv); - } - } - - if (is_wp_error($response)) { - return $response; - } - - touch($filepath, time()); - return $response; - } - - /** - * Sheets are flat, if payload has nested arrays, flattens it and concatenate its keys - * as field names. - * - * @param array $payload Submission payload. - * @param string $path Prefix to prepend to the field name. - * - * @return array Flattened payload. - */ - private static function flatten_payload($payload, $path = '') - { - $flat = []; - foreach ($payload as $field => $value) { - $key = $path . $field; - $value = self::flatten_value($value, $key); - - if (!is_array($value)) { - $flat[$key] = $value; - } else { - foreach ($value as $_key => $_val) { - $flat[$_key] = $_val; - } - } - } - - return $flat; - } - - private static function flatten_value($value, $path = '') - { - if (!is_array($value)) { - return $value; - } - - if (wp_is_numeric_array($value)) { - $simple_items = array_filter($value, fn($item) => !is_array($item)); - - if (count($simple_items) === count($value)) { - return implode(',', $value); - } - } - - return self::flatten_payload($value, $path . '.'); - } -} diff --git a/addons/nextcloud/hooks.php b/addons/nextcloud/hooks.php deleted file mode 100644 index d9157263..00000000 --- a/addons/nextcloud/hooks.php +++ /dev/null @@ -1,144 +0,0 @@ - [ - [ - 'ref' => '#backend', - 'name' => 'name', - 'default' => 'Nextcloud', - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'label' => __('Filepath', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'pattern' => '.+.csv$', - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'label' => __('Method', 'forms-bridge'), - 'type' => 'text', - 'value' => 'PUT', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'label' => __('Filepath', 'forms-bridge'), - 'pattern' => '.+\.csv$', - ], - [ - 'ref' => '#credential', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'schema', - 'type' => 'text', - 'value' => 'Basic', - ], - [ - 'ref' => '#credential', - 'name' => 'client_id', - 'label' => __('User login', 'forms-bridge'), - 'description' => __( - 'Either, a user name or email', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_secret', - 'label' => __('Password', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - ], - 'bridge' => [ - 'backend' => 'Nextcloud', - ], - 'backend' => [ - 'name' => 'Nextcloud', - 'headers' => [ - [ - 'name' => 'Content-Type', - 'value' => 'application/octet-stream', - ], - ], - ], - 'credential' => [ - 'name' => '', - 'schema' => 'Basic', - 'client_id' => '', - 'client_secret' => '', - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'nextcloud-') !== 0) { - return $data; - } - - if (!preg_match('/\.csv$/i', $data['bridge']['endpoint'])) { - $data['bridge']['endpoint'] .= '.csv'; - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/nextcloud/templates/woo-orders.php b/addons/nextcloud/templates/woo-orders.php deleted file mode 100644 index 933e5633..00000000 --- a/addons/nextcloud/templates/woo-orders.php +++ /dev/null @@ -1,322 +0,0 @@ - __('WC Orders', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into new lines on a Nextcloud CSV with order and customer information.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - ], - 'bridge' => [ - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'Order ID', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'Name[][0]', - 'cast' => 'string', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'Name[][1]', - 'cast' => 'string', - ], - [ - 'from' => 'Name[]', - 'to' => 'Name[]', - 'cast' => 'concat', - ], - [ - 'from' => 'Name', - 'to' => 'Name', - 'cast' => 'csv', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'Date', - 'cast' => 'string', - ], - [ - 'from' => 'total', - 'to' => 'Total', - 'cast' => 'number', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines[].name', - 'to' => 'Shipping', - 'cast' => 'string', - ], - [ - 'from' => 'Shipping', - 'to' => 'Shipping', - 'cast' => 'csv', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'Shipping Total', - 'cast' => 'number', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines[].code', - 'to' => 'Coupons', - 'cast' => 'string', - ], - [ - 'from' => 'Coupons', - 'to' => 'Coupons', - 'cast' => 'csv', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'Discount Total', - 'cast' => 'number', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'First Name', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'Last Name', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'Email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'Phone', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'Address', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_2', - 'to' => 'Address 2', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'City', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'Postal Code', - 'cast' => 'string', - ], - [ - 'from' => '?billing.state', - 'to' => 'State', - 'cast' => 'string', - ], - [ - 'from' => '?billing.country', - 'to' => 'Country', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'Payment Method', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'Note', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - ], - ], - ], -]; diff --git a/addons/odoo/api.php b/addons/odoo/api.php deleted file mode 100644 index 00d1ab3e..00000000 --- a/addons/odoo/api.php +++ /dev/null @@ -1,164 +0,0 @@ - false, - 'name' => $payload['name'], - ]; - - $partner_fields = [ - 'title', - 'parent_id', - 'lang', - 'vat', - 'website', - 'employee', - 'function', - 'street', - 'street2', - 'zip', - 'city', - 'country_code', - 'country_id', - 'email', - 'phone', - 'mobile', - 'is_public', - 'additional_info', - ]; - - foreach ($partner_fields as $field) { - if (isset($payload[$field])) { - $partner[$field] = $payload[$field]; - } - } - - $query = [['name', '=', $partner['name']]]; - - if (isset($partner['email'])) { - $query[] = ['email', '=', $partner['email']]; - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-search-partner', - 'method' => 'search_read', - 'endpoint' => 'res.partner', - ]) - ->submit($query); - - if (!is_wp_error($response)) { - return $response['data']['result'][0]; - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-create-partner', - 'method' => 'create', - 'endpoint' => 'res.partner', - ]) - ->submit($partner); - - if (is_wp_error($response)) { - return $response; - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-get-partner-data', - 'method' => 'read', - 'endpoint' => 'res.partner', - ]) - ->submit([$response['data']['result']]); - - if (is_wp_error($response)) { - return $response; - } - - return $response['data']['result'][0]; -} - -function forms_bridge_odoo_create_company($payload, $bridge) -{ - $company = [ - 'is_company' => true, - 'name' => $payload['name'], - ]; - - $company_fields = [ - 'lang', - 'vat', - 'website', - 'street', - 'street2', - 'zip', - 'city', - 'country_code', - 'country_id', - 'email', - 'phone', - 'mobile', - 'is_public', - 'additional_info', - ]; - - foreach ($company_fields as $field) { - if (isset($payload[$field])) { - $company[$field] = $payload[$field]; - } - } - - $query = [['name', '=', $company['name']]]; - - if (isset($company['email'])) { - $query[] = ['email', '=', $company['email']]; - } - - if (isset($company['vat'])) { - $query[] = ['vat', '=', $company['vat']]; - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-search-company', - 'template' => null, - 'method' => 'search_read', - 'endpoint' => 'res.partner', - ]) - ->submit($query); - - if (!is_wp_error($response)) { - return $response['data']['result'][0]; - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-create-company', - 'method' => 'create', - 'endpoint' => 'res.partner', - ]) - ->submit($company); - - if (is_wp_error($response)) { - return $response; - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-get-company-data', - 'method' => 'read', - 'endpoint' => 'res.partner', - ]) - ->submit([$response['data']['result']]); - - if (is_wp_error($response)) { - return $response; - } - - return $response['data']['result'][0]; -} diff --git a/addons/odoo/class-odoo-addon.php b/addons/odoo/class-odoo-addon.php deleted file mode 100644 index e4a6d263..00000000 --- a/addons/odoo/class-odoo-addon.php +++ /dev/null @@ -1,141 +0,0 @@ - '__odoo-' . time(), - 'method' => 'search', - 'endpoint' => 'res.users', - 'backend' => $backend, - ]); - - $response = $bridge->submit(); - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend model and retrive the response data. - * - * @param string $endpoint Target model name. - * @param string $backend Target backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge = new Odoo_Form_Bridge([ - 'name' => '__odoo-' . time(), - 'method' => 'search_read', - 'endpoint' => $endpoint, - 'backend' => $backend, - ]); - - return $bridge->submit([], ['id', 'name']); - } - - /** - * Performs an introspection of the backend model and returns API fields - * and accepted content type. - * - * @param string $model Target model name. - * @param string $backend Target backend name. - * - * @return array List of fields and content type of the model. - */ - public function get_endpoint_schema($model, $backend) - { - $bridge = new Odoo_Form_Bridge([ - 'name' => '__odoo-' . time(), - 'method' => 'fields_get', - 'endpoint' => $model, - 'backend' => $backend, - ]); - - $response = $bridge->submit(); - - if (is_wp_error($response)) { - return []; - } - - $fields = []; - foreach ($response['data']['result'] as $name => $spec) { - if ($spec['readonly']) { - continue; - } - - if ($spec['type'] === 'char' || $spec['type'] === 'html') { - $schema = ['type' => 'string']; - } elseif ($spec['type'] === 'float') { - $schema = ['type' => 'number']; - } elseif ( - in_array( - $spec['type'], - ['one2many', 'many2one', 'many2many'], - true - ) - ) { - $schema = [ - 'type' => 'array', - 'items' => [['type' => 'integer'], ['type' => 'string']], - 'additionalItems' => false, - ]; - } else { - $schema = ['type' => $spec['type']]; - } - - $schema['required'] = $spec['required']; - - $fields[] = [ - 'name' => $name, - 'schema' => $schema, - ]; - } - - return $fields; - } -} - -Odoo_Addon::setup(); diff --git a/addons/odoo/class-odoo-form-bridge.php b/addons/odoo/class-odoo-form-bridge.php deleted file mode 100644 index 2134f3cb..00000000 --- a/addons/odoo/class-odoo-form-bridge.php +++ /dev/null @@ -1,257 +0,0 @@ - '2.0', - 'method' => 'call', - 'id' => $session_id, - 'params' => [ - 'service' => $service, - 'method' => $method, - 'args' => $args, - ], - ]; - } - - /** - * Handle RPC responses and catch errors on the application layer. - * - * @param array $response Request response. - * @param boolean $is_single Should the result be an entity or an array. - * - * @return mixed|WP_Error Request result. - */ - public static function rpc_response($res) - { - if (is_wp_error($res)) { - return $res; - } - - if (empty($res['data'])) { - $content_type = - Http_Client::get_content_type($res['headers']) ?? 'undefined'; - - return new WP_Error( - 'unkown_content_type', - /* translators: %s: Content-Type header value */ - sprintf( - __('Unkown HTTP response content type %s', 'forms-bridge'), - sanitize_text_field($content_type) - ), - $res - ); - } - - if (isset($res['data']['error'])) { - $error = new WP_Error( - 'response_code_' . $res['data']['error']['code'], - $res['data']['error']['message'], - $res['data']['error']['data'] - ); - - $error_data = ['response' => $res]; - if (self::$request) { - $error_data['request'] = self::$request; - } - - $error->add_data($error_data); - return $error; - } - - $data = $res['data']; - - if (empty($data['result'])) { - $error = new WP_Error('not_found'); - - $error_data = ['response' => $res]; - if (self::$request) { - $error_data['request'] = self::$request; - } - - $error->add_data($error_data); - return $error; - } - - return $data['result']; - } - - /** - * JSON RPC login request. - * - * @param [string, string, string] $login - * @param Backend $backend - * - * @return array|WP_Error Tuple with RPC session id and user id. - */ - private static function rpc_login($login, $backend) - { - if (self::$session) { - return self::$session; - } - - $session_id = 'forms-bridge-' . time(); - - $payload = self::rpc_payload($session_id, 'common', 'login', $login); - - $response = $backend->post(self::endpoint, $payload); - - $user_id = self::rpc_response($response); - - if (is_wp_error($user_id)) { - return $user_id; - } - - self::$session = [$session_id, $user_id]; - return self::$session; - } - - public function __construct($data, $addon = null) - { - parent::__construct($data, 'odoo'); - } - - /** - * Submits submission to the backend. - * - * @param array $payload Submission data. - * @param array|null $more_args Additional RPC call params. - * - * @return array|WP_Error Http - */ - public function submit($payload = [], $more_args = []) - { - if (!$this->is_valid) { - return new WP_Error('invalid_bridge'); - } - - $backend = $this->backend(); - - if (!$backend) { - return new WP_Error( - 'invalid_backend', - 'The bridge does not have a valid backend' - ); - } - - $credential = $backend->credential; - if (!$credential) { - return new WP_Error( - 'invalid_credential', - 'The bridge does not have a valid credential' - ); - } - - add_filter( - 'http_bridge_request', - static function ($request) { - self::$request = $request; - return $request; - }, - 10, - 1 - ); - - $backend_name = $backend->name; - - add_filter( - 'http_bridge_backend_headers', - static function ($headers, $backend) use ($backend_name) { - if ($backend->name !== $backend_name) { - return $headers; - } - - $locale = get_locale(); - if (!$locale) { - return $headers; - } - - if ('ca' === $locale) { - $locale = 'ca_ES'; - } - - $headers['Accept-Language'] = $locale; - return $headers; - }, - 20, - 2 - ); - - $login = $credential->authorization(); - $session = self::rpc_login($login, $backend); - - if (is_wp_error($session)) { - return $session; - } - - [$sid, $uid] = $session; - $login[1] = $uid; - - $payload = self::rpc_payload( - $sid, - 'object', - 'execute', - array_merge($login, [$this->endpoint, $this->method, $payload]), - $more_args - ); - - $response = $backend->post(self::endpoint, $payload); - - $result = self::rpc_response($response); - if (is_wp_error($result)) { - return $result; - } - - return $response; - } -} diff --git a/addons/odoo/data/state-codes.php b/addons/odoo/data/state-codes.php deleted file mode 100644 index 557fd13e..00000000 --- a/addons/odoo/data/state-codes.php +++ /dev/null @@ -1,801 +0,0 @@ - __('Ajman', 'forms-bridge'), - 'AZ' => __('Abu Dhabi', 'forms-bridge'), - 'DU' => __('Dubai', 'forms-bridge'), - 'FU' => __('Fujairah', 'forms-bridge'), - 'RK' => __('Ras al-Khaimah', 'forms-bridge'), - 'SH' => __('Sharjah', 'forms-bridge'), - 'UQ' => __('Umm al-Quwain', 'forms-bridge'), -]; - -global $forms_bridge_odoo_ar_states; -$forms_bridge_odoo_ar_states = [ - 'A' => __('Salta', 'forms-bridge'), - 'B' => __('Buenos Aires', 'forms-bridge'), - 'C' => __('Ciudad Autónoma de Buenos Aires', 'forms-bridge'), - 'D' => __('San Luis', 'forms-bridge'), - 'E' => __('Entre Ríos', 'forms-bridge'), - 'F' => __('La Rioja', 'forms-bridge'), - 'G' => __('Santiago Del Estero', 'forms-bridge'), - 'H' => __('Chaco', 'forms-bridge'), - 'J' => __('San Juan', 'forms-bridge'), - 'K' => __('Catamarca', 'forms-bridge'), - 'L' => __('La Pampa', 'forms-bridge'), - 'M' => __('Mendoza', 'forms-bridge'), - 'N' => __('Misiones', 'forms-bridge'), - 'P' => __('Formosa', 'forms-bridge'), - 'Q' => __('Neuquén', 'forms-bridge'), - 'R' => __('Río Negro', 'forms-bridge'), - 'S' => __('Santa Fe', 'forms-bridge'), - 'T' => __('Tucumán', 'forms-bridge'), - 'U' => __('Chubut', 'forms-bridge'), - 'V' => __('Tierra del Fuego', 'forms-bridge'), - 'W' => __('Corrientes', 'forms-bridge'), - 'X' => __('Córdoba', 'forms-bridge'), - 'Y' => __('Jujuy', 'forms-bridge'), - 'Z' => __('Santa Cruz', 'forms-bridge'), -]; - -global $forms_bridge_odoo_au_states; -$forms_bridge_odoo_au_states = [ - 'ACT' => __('Australian Capital Territory', 'forms-bridge'), - 'NSW' => __('New South Wales', 'forms-bridge'), - 'NT' => __('Northern Territory', 'forms-bridge'), - 'QLD' => __('Queensland', 'forms-bridge'), - 'SA' => __('South Australia', 'forms-bridge'), - 'TAS' => __('Tasmania', 'forms-bridge'), - 'VIC' => __('Victoria', 'forms-bridge'), - 'WA' => __('Western Australia', 'forms-bridge'), -]; - -global $forms_bridge_odoo_br_states; -$forms_bridge_odoo_br_states = [ - 'AC' => __('Acre', 'forms-bridge'), - 'AL' => __('Alagoas', 'forms-bridge'), - 'AM' => __('Amazonas', 'forms-bridge'), - 'AP' => __('Amapá', 'forms-bridge'), - 'BA' => __('Bahia', 'forms-bridge'), - 'CE' => __('Ceará', 'forms-bridge'), - 'DF' => __('Distrito Federal', 'forms-bridge'), - 'ES' => __('Espírito Santo', 'forms-bridge'), - 'GO' => __('Goiás', 'forms-bridge'), - 'MA' => __('Maranhão', 'forms-bridge'), - 'MG' => __('Minas Gerais', 'forms-bridge'), - 'MS' => __('Mato Grosso do Sul', 'forms-bridge'), - 'MT' => __('Mato Grosso', 'forms-bridge'), - 'PA' => __('Pará', 'forms-bridge'), - 'PB' => __('Paraíba', 'forms-bridge'), - 'PE' => __('Pernambuco', 'forms-bridge'), - 'PI' => __('Piauí', 'forms-bridge'), - 'PR' => __('Paraná', 'forms-bridge'), - 'RJ' => __('Rio de Janeiro', 'forms-bridge'), - 'RN' => __('Rio Grande do Norte', 'forms-bridge'), - 'RO' => __('Rondônia', 'forms-bridge'), - 'RR' => __('Roraima', 'forms-bridge'), - 'RS' => __('Rio Grande do Sul', 'forms-bridge'), - 'SC' => __('Santa Catarina', 'forms-bridge'), - 'SE' => __('Sergipe', 'forms-bridge'), - 'SP' => __('São Paulo', 'forms-bridge'), - 'TO' => __('Tocantins', 'forms-bridge'), -]; - -global $forms_bridge_odoo_ca_states; -$forms_bridge_odoo_ca_states = [ - 'AB' => __('Alberta', 'forms-bridge'), - 'BC' => __('British Columbia', 'forms-bridge'), - 'MB' => __('Manitoba', 'forms-bridge'), - 'NB' => __('New Brunswick', 'forms-bridge'), - 'NL' => __('Newfoundland and Labrador', 'forms-bridge'), - 'NS' => __('Nova Scotia', 'forms-bridge'), - 'NT' => __('Northwest Territories', 'forms-bridge'), - 'NU' => __('Nunavut', 'forms-bridge'), - 'ON' => __('Ontario', 'forms-bridge'), - 'PE' => __('Prince Edward Island', 'forms-bridge'), - 'QC' => __('Quebec', 'forms-bridge'), - 'SK' => __('Saskatchewan', 'forms-bridge'), - 'YT' => __('Yukon', 'forms-bridge'), -]; - -global $forms_bridge_odoo_co_states; -$forms_bridge_odoo_co_states = [ - 'AMA' => __('Amazonas', 'forms-bridge'), - 'ANT' => __('Antioquia', 'forms-bridge'), - 'ARA' => __('Arauca', 'forms-bridge'), - 'ATL' => __('Atlántico', 'forms-bridge'), - 'BOL' => __('Bolívar', 'forms-bridge'), - 'BOY' => __('Boyacá', 'forms-bridge'), - 'CAL' => __('Caldas', 'forms-bridge'), - 'CAQ' => __('Caquetá', 'forms-bridge'), - 'CAS' => __('Casanare', 'forms-bridge'), - 'CAU' => __('Cauca', 'forms-bridge'), - 'CES' => __('Cesar', 'forms-bridge'), - 'CHO' => __('Chocó', 'forms-bridge'), - 'COR' => __('Córdoba', 'forms-bridge'), - 'CUN' => __('Cundinamarca', 'forms-bridge'), - 'DC' => __('D.C.', 'forms-bridge'), - 'GUA' => __('Guainía', 'forms-bridge'), - 'GUV' => __('Guaviare', 'forms-bridge'), - 'HUI' => __('Huila', 'forms-bridge'), - 'LAG' => __('La Guajira', 'forms-bridge'), - 'MAG' => __('Magdalena', 'forms-bridge'), - 'MET' => __('Meta', 'forms-bridge'), - 'NAR' => __('Nariño', 'forms-bridge'), - 'NSA' => __('Norte de Santander', 'forms-bridge'), - 'PUT' => __('Putumayo', 'forms-bridge'), - 'QUI' => __('Quindio', 'forms-bridge'), - 'RIS' => __('Risaralda', 'forms-bridge'), - 'SAN' => __('Santander', 'forms-bridge'), - 'SAP' => __( - 'Archipiélago de San Andrés, Providencia y Santa Catalina', - 'forms-bridge' - ), - 'SUC' => __('Sucre', 'forms-bridge'), - 'TOL' => __('Tolima', 'forms-bridge'), - 'VAC' => __('Valle del Cauca', 'forms-bridge'), - 'VAU' => __('Vaupés', 'forms-bridge'), - 'VID' => __('Vichada', 'forms-bridge'), -]; - -global $forms_bridge_odoo_eg_states; -$forms_bridge_odoo_eg_states = [ - 'ALX' => __('Alexandria', 'forms-bridge'), - 'ASN' => __('Aswan', 'forms-bridge'), - 'AST' => __('Asyut', 'forms-bridge'), - 'BA' => __('Red Sea', 'forms-bridge'), - 'BH' => __('Beheira', 'forms-bridge'), - 'BNS' => __('Beni Suef', 'forms-bridge'), - 'C' => __('Cairo', 'forms-bridge'), - 'DK' => __('Dakahlia', 'forms-bridge'), - 'DT' => __('Damietta', 'forms-bridge'), - 'FYM' => __('Faiyum', 'forms-bridge'), - 'GH' => __('Gharbia', 'forms-bridge'), - 'GZ' => __('Giza', 'forms-bridge'), - 'HU' => __('Helwan', 'forms-bridge'), - 'IS' => __('Ismailia', 'forms-bridge'), - 'JS' => __('South Sinai', 'forms-bridge'), - 'KB' => __('Qalyubia', 'forms-bridge'), - 'KFS' => __('Kafr el-Sheikh', 'forms-bridge'), - 'KN' => __('Qena', 'forms-bridge'), - 'LX' => __('Luxor', 'forms-bridge'), - 'MN' => __('Minya', 'forms-bridge'), - 'MNF' => __('Monufia', 'forms-bridge'), - 'MT' => __('Matrouh', 'forms-bridge'), - 'PTS' => __('Port Said', 'forms-bridge'), - 'SHG' => __('Sohag', 'forms-bridge'), - 'SHR' => __('Al Sharqia', 'forms-bridge'), - 'SIN' => __('North Sinai', 'forms-bridge'), - 'SU' => __('6th of October', 'forms-bridge'), - 'SUZ' => __('Suez', 'forms-bridge'), - 'WAD' => __('New Valley', 'forms-bridge'), -]; - -global $forms_bridge_odoo_es_states; -$forms_bridge_odoo_es_states = [ - 'A' => __('Alacant (Alicante)', 'forms-bridge'), - 'AB' => __('Albacete', 'forms-bridge'), - 'AL' => __('Almería', 'forms-bridge'), - 'AV' => __('Ávila', 'forms-bridge'), - 'B' => __('Barcelona', 'forms-bridge'), - 'BA' => __('Badajoz', 'forms-bridge'), - 'BI' => __('Bizkaia (Vizcaya)', 'forms-bridge'), - 'BU' => __('Burgos', 'forms-bridge'), - 'C' => __('A Coruña (La Coruña)', 'forms-bridge'), - 'CA' => __('Cádiz', 'forms-bridge'), - 'CC' => __('Cáceres', 'forms-bridge'), - 'CE' => __('Ceuta', 'forms-bridge'), - 'CO' => __('Córdoba', 'forms-bridge'), - 'CR' => __('Ciudad Real', 'forms-bridge'), - 'CS' => __('Castelló (Castellón)', 'forms-bridge'), - 'CU' => __('Cuenca', 'forms-bridge'), - 'GC' => __('Las Palmas', 'forms-bridge'), - 'GI' => __('Girona (Gerona)', 'forms-bridge'), - 'GR' => __('Granada', 'forms-bridge'), - 'GU' => __('Guadalajara', 'forms-bridge'), - 'H' => __('Huelva', 'forms-bridge'), - 'HU' => __('Huesca', 'forms-bridge'), - 'J' => __('Jaén', 'forms-bridge'), - 'L' => __('Lleida (Lérida)', 'forms-bridge'), - 'LE' => __('León', 'forms-bridge'), - 'LO' => __('La Rioja', 'forms-bridge'), - 'LU' => __('Lugo', 'forms-bridge'), - 'M' => __('Madrid', 'forms-bridge'), - 'MA' => __('Málaga', 'forms-bridge'), - 'ME' => __('Melilla', 'forms-bridge'), - 'MU' => __('Murcia', 'forms-bridge'), - 'NA' => __('Navarra (Nafarroa)', 'forms-bridge'), - 'O' => __('Asturias', 'forms-bridge'), - 'OR' => __('Ourense (Orense)', 'forms-bridge'), - 'P' => __('Palencia', 'forms-bridge'), - 'PM' => __('Illes Balears (Islas Baleares)', 'forms-bridge'), - 'PO' => __('Pontevedra', 'forms-bridge'), - 'S' => __('Cantabria', 'forms-bridge'), - 'SA' => __('Salamanca', 'forms-bridge'), - 'SE' => __('Sevilla', 'forms-bridge'), - 'SG' => __('Segovia', 'forms-bridge'), - 'SO' => __('Soria', 'forms-bridge'), - 'SS' => __('Gipuzkoa (Guipúzcoa)', 'forms-bridge'), - 'T' => __('Tarragona', 'forms-bridge'), - 'TE' => __('Teruel', 'forms-bridge'), - 'TF' => __('Santa Cruz de Tenerife', 'forms-bridge'), - 'TO' => __('Toledo', 'forms-bridge'), - 'V' => __('València (Valencia)', 'forms-bridge'), - 'VA' => __('Valladolid', 'forms-bridge'), - 'VI' => __('Araba/Álava', 'forms-bridge'), - 'Z' => __('Zaragoza', 'forms-bridge'), - 'ZA' => __('Zamora', 'forms-bridge'), -]; - -global $forms_bridge_odoo_gt_states; -$forms_bridge_odoo_gt_states = [ - 'AVE' => __('Alta Verapaz', 'forms-bridge'), - 'BVE' => __('Baja Verapaz', 'forms-bridge'), - 'CMT' => __('Chimaltenango', 'forms-bridge'), - 'CQM' => __('Chiquimula', 'forms-bridge'), - 'EPR' => __('El Progreso', 'forms-bridge'), - 'ESC' => __('Escuintla', 'forms-bridge'), - 'GUA' => __('Guatemala', 'forms-bridge'), - 'HUE' => __('Huehuetenango', 'forms-bridge'), - 'IZA' => __('Izabal', 'forms-bridge'), - 'JAL' => __('Jalapa', 'forms-bridge'), - 'JUT' => __('Jutiapa', 'forms-bridge'), - 'PET' => __('Petén', 'forms-bridge'), - 'QUE' => __('Quetzaltenango', 'forms-bridge'), - 'QUI' => __('Quiché', 'forms-bridge'), - 'RET' => __('Retalhuleu', 'forms-bridge'), - 'SAC' => __('Sacatepéquez', 'forms-bridge'), - 'SMA' => __('San Marcos', 'forms-bridge'), - 'SOL' => __('Sololá', 'forms-bridge'), - 'SRO' => __('Santa Rosa', 'forms-bridge'), - 'SUC' => __('Suchitepéquez', 'forms-bridge'), - 'TOT' => __('Totonicapán', 'forms-bridge'), - 'ZAC' => __('Zacapa', 'forms-bridge'), -]; - -global $forms_bridge_odoo_id_states; -$forms_bridge_odoo_id_states = [ - 'AC' => __('Aceh', 'forms-bridge'), - 'BA' => __('Bali', 'forms-bridge'), - 'BB' => __('Bangka Belitung', 'forms-bridge'), - 'BE' => __('Bengkulu', 'forms-bridge'), - 'BT' => __('Banten', 'forms-bridge'), - 'GO' => __('Gorontalo', 'forms-bridge'), - 'JA' => __('Jambi', 'forms-bridge'), - 'JB' => __('Jawa Barat', 'forms-bridge'), - 'JI' => __('Jawa Timur', 'forms-bridge'), - 'JK' => __('Jakarta', 'forms-bridge'), - 'JT' => __('Jawa Tengah', 'forms-bridge'), - 'KB' => __('Kalimantan Barat', 'forms-bridge'), - 'KI' => __('Kalimantan Timur', 'forms-bridge'), - 'KR' => __('Kepulauan Riau', 'forms-bridge'), - 'KS' => __('Kalimantan Selatan', 'forms-bridge'), - 'KT' => __('Kalimantan Tengah', 'forms-bridge'), - 'KU' => __('Kalimantan Utara', 'forms-bridge'), - 'LA' => __('Lampung', 'forms-bridge'), - 'MA' => __('Maluku', 'forms-bridge'), - 'MU' => __('Maluku Utara', 'forms-bridge'), - 'NB' => __('Nusa Tenggara Barat', 'forms-bridge'), - 'NT' => __('Nusa Tenggara Timur', 'forms-bridge'), - 'PA' => __('Papua', 'forms-bridge'), - 'PB' => __('Papua Barat', 'forms-bridge'), - 'RI' => __('Riau', 'forms-bridge'), - 'SA' => __('Sulawesi Utara', 'forms-bridge'), - 'SB' => __('Sumatra Barat', 'forms-bridge'), - 'SG' => __('Sulawesi Tenggara', 'forms-bridge'), - 'SN' => __('Sulawesi Selatan', 'forms-bridge'), - 'SR' => __('Sulawesi Barat', 'forms-bridge'), - 'SS' => __('Sumatra Selatan', 'forms-bridge'), - 'ST' => __('Sulawesi Tengah', 'forms-bridge'), - 'SU' => __('Sumatra Utara', 'forms-bridge'), - 'YO' => __('Yogyakarta', 'forms-bridge'), -]; - -global $forms_bridge_odoo_in_states; -$forms_bridge_odoo_in_states = [ - 'AN' => __('Andaman and Nicobar', 'forms-bridge'), - 'AP' => __('Andhra Pradesh', 'forms-bridge'), - 'AR' => __('Arunachal Pradesh', 'forms-bridge'), - 'AS' => __('Assam', 'forms-bridge'), - 'BR' => __('Bihar', 'forms-bridge'), - 'CG' => __('Chattisgarh', 'forms-bridge'), - 'CH' => __('Chandigarh', 'forms-bridge'), - 'DD' => __('Daman and Diu', 'forms-bridge'), - 'DL' => __('Delhi', 'forms-bridge'), - 'DN' => __('Dadra and Nagar Haveli', 'forms-bridge'), - 'GA' => __('Goa', 'forms-bridge'), - 'GJ' => __('Gujarat', 'forms-bridge'), - 'HP' => __('Himachal Pradesh', 'forms-bridge'), - 'HR' => __('Haryana', 'forms-bridge'), - 'JH' => __('Jharkhand', 'forms-bridge'), - 'JK' => __('Jammu and Kashmir', 'forms-bridge'), - 'KA' => __('Karnataka', 'forms-bridge'), - 'KL' => __('Kerala', 'forms-bridge'), - 'LD' => __('Lakshadweep', 'forms-bridge'), - 'MH' => __('Maharashtra', 'forms-bridge'), - 'ML' => __('Meghalaya', 'forms-bridge'), - 'MN' => __('Manipur', 'forms-bridge'), - 'MP' => __('Madhya Pradesh', 'forms-bridge'), - 'MZ' => __('Mizoram', 'forms-bridge'), - 'NL' => __('Nagaland', 'forms-bridge'), - 'OR' => __('Orissa', 'forms-bridge'), - 'PB' => __('Punjab', 'forms-bridge'), - 'PY' => __('Puducherry', 'forms-bridge'), - 'RJ' => __('Rajasthan', 'forms-bridge'), - 'SK' => __('Sikkim', 'forms-bridge'), - 'TN' => __('Tamil Nadu', 'forms-bridge'), - 'TR' => __('Tripura', 'forms-bridge'), - 'TS' => __('Telangana', 'forms-bridge'), - 'UK' => __('Uttarakhand', 'forms-bridge'), - 'UP' => __('Uttar Pradesh', 'forms-bridge'), - 'WB' => __('West Bengal', 'forms-bridge'), -]; - -global $forms_bridge_odoo_it_states; -$forms_bridge_odoo_it_states = [ - 'AG' => __('Agrigento', 'forms-bridge'), - 'AL' => __('Alessandria', 'forms-bridge'), - 'AN' => __('Ancona', 'forms-bridge'), - 'AO' => __('Aosta', 'forms-bridge'), - 'AP' => __('Ascoli Piceno', 'forms-bridge'), - 'AQ' => __('L\'Aquila', 'forms-bridge'), - 'AR' => __('Arezzo', 'forms-bridge'), - 'AT' => __('Asti', 'forms-bridge'), - 'AV' => __('Avellino', 'forms-bridge'), - 'BA' => __('Bari', 'forms-bridge'), - 'BG' => __('Bergamo', 'forms-bridge'), - 'BI' => __('Biella', 'forms-bridge'), - 'BL' => __('Belluno', 'forms-bridge'), - 'BN' => __('Benevento', 'forms-bridge'), - 'BO' => __('Bologna', 'forms-bridge'), - 'BR' => __('Brindisi', 'forms-bridge'), - 'BS' => __('Brescia', 'forms-bridge'), - 'BT' => __('Barletta-Andria-Trani', 'forms-bridge'), - 'BZ' => __('Bolzano', 'forms-bridge'), - 'CA' => __('Cagliari', 'forms-bridge'), - 'CB' => __('Campobasso', 'forms-bridge'), - 'CE' => __('Caserta', 'forms-bridge'), - 'CH' => __('Chieti', 'forms-bridge'), - 'CI' => __('Carbonia-Iglesias', 'forms-bridge'), - 'CL' => __('Caltanissetta', 'forms-bridge'), - 'CN' => __('Cuneo', 'forms-bridge'), - 'CO' => __('Como', 'forms-bridge'), - 'CR' => __('Cremona', 'forms-bridge'), - 'CS' => __('Cosenza', 'forms-bridge'), - 'CT' => __('Catania', 'forms-bridge'), - 'CZ' => __('Catanzaro', 'forms-bridge'), - 'EN' => __('Enna', 'forms-bridge'), - 'FC' => __('Forlì-Cesena', 'forms-bridge'), - 'FE' => __('Ferrara', 'forms-bridge'), - 'FG' => __('Foggia', 'forms-bridge'), - 'FI' => __('Firenze', 'forms-bridge'), - 'FM' => __('Fermo', 'forms-bridge'), - 'FR' => __('Frosinone', 'forms-bridge'), - 'GE' => __('Genova', 'forms-bridge'), - 'GO' => __('Gorizia', 'forms-bridge'), - 'GR' => __('Grosseto', 'forms-bridge'), - 'IM' => __('Imperia', 'forms-bridge'), - 'IS' => __('Isernia', 'forms-bridge'), - 'KR' => __('Crotone', 'forms-bridge'), - 'LC' => __('Lecco', 'forms-bridge'), - 'LE' => __('Lecce', 'forms-bridge'), - 'LI' => __('Livorno', 'forms-bridge'), - 'LO' => __('Lodi', 'forms-bridge'), - 'LT' => __('Latina', 'forms-bridge'), - 'LU' => __('Lucca', 'forms-bridge'), - 'MB' => __('Monza e Brianza', 'forms-bridge'), - 'MC' => __('Macerata', 'forms-bridge'), - 'ME' => __('Messina', 'forms-bridge'), - 'MI' => __('Milano', 'forms-bridge'), - 'MN' => __('Mantova', 'forms-bridge'), - 'MO' => __('Modena', 'forms-bridge'), - 'MS' => __('Massa-Carrara', 'forms-bridge'), - 'MT' => __('Matera', 'forms-bridge'), - 'NA' => __('Napoli', 'forms-bridge'), - 'NO' => __('Novara', 'forms-bridge'), - 'NU' => __('Nuoro', 'forms-bridge'), - 'OG' => __('Ogliastra', 'forms-bridge'), - 'OR' => __('Oristano', 'forms-bridge'), - 'OT' => __('Olbia-Tempio', 'forms-bridge'), - 'PA' => __('Palermo', 'forms-bridge'), - 'PC' => __('Piacenza', 'forms-bridge'), - 'PD' => __('Padova', 'forms-bridge'), - 'PE' => __('Pescara', 'forms-bridge'), - 'PG' => __('Perugia', 'forms-bridge'), - 'PI' => __('Pisa', 'forms-bridge'), - 'PN' => __('Pordenone', 'forms-bridge'), - 'PO' => __('Prato', 'forms-bridge'), - 'PR' => __('Parma', 'forms-bridge'), - 'PT' => __('Pistoia', 'forms-bridge'), - 'PU' => __('Pesaro e Urbino', 'forms-bridge'), - 'PV' => __('Pavia', 'forms-bridge'), - 'PZ' => __('Potenza', 'forms-bridge'), - 'RA' => __('Ravenna', 'forms-bridge'), - 'RC' => __('Reggio Calabria', 'forms-bridge'), - 'RE' => __('Reggio Emilia', 'forms-bridge'), - 'RG' => __('Ragusa', 'forms-bridge'), - 'RI' => __('Rieti', 'forms-bridge'), - 'RM' => __('Roma', 'forms-bridge'), - 'RN' => __('Rimini', 'forms-bridge'), - 'RO' => __('Rovigo', 'forms-bridge'), - 'SA' => __('Salerno', 'forms-bridge'), - 'SI' => __('Siena', 'forms-bridge'), - 'SO' => __('Sondrio', 'forms-bridge'), - 'SP' => __('La Spezia', 'forms-bridge'), - 'SR' => __('Siracusa', 'forms-bridge'), - 'SS' => __('Sassari', 'forms-bridge'), - 'SU' => __('Sud Sardegna', 'forms-bridge'), - 'SV' => __('Savona', 'forms-bridge'), - 'TA' => __('Taranto', 'forms-bridge'), - 'TE' => __('Teramo', 'forms-bridge'), - 'TN' => __('Trento', 'forms-bridge'), - 'TO' => __('Torino', 'forms-bridge'), - 'TP' => __('Trapani', 'forms-bridge'), - 'TR' => __('Terni', 'forms-bridge'), - 'TS' => __('Trieste', 'forms-bridge'), - 'TV' => __('Treviso', 'forms-bridge'), - 'UD' => __('Udine', 'forms-bridge'), - 'VA' => __('Varese', 'forms-bridge'), - 'VB' => __('Verbano-Cusio-Ossola', 'forms-bridge'), - 'VC' => __('Vercelli', 'forms-bridge'), - 'VE' => __('Venezia', 'forms-bridge'), - 'VI' => __('Vicenza', 'forms-bridge'), - 'VR' => __('Verona', 'forms-bridge'), - 'VS' => __('Medio Campidano', 'forms-bridge'), - 'VT' => __('Viterbo', 'forms-bridge'), - 'VV' => __('Vibo Valentia', 'forms-bridge'), -]; - -global $forms_bridge_odoo_jp_states; -$forms_bridge_odoo_jp_states = [ - '10' => __('Gunma', 'forms-bridge'), - '11' => __('Saitama', 'forms-bridge'), - '12' => __('Chiba', 'forms-bridge'), - '13' => __('Tokyo', 'forms-bridge'), - '14' => __('Kanagawa', 'forms-bridge'), - '15' => __('Niigata', 'forms-bridge'), - '16' => __('Toyama', 'forms-bridge'), - '17' => __('Ishikawa', 'forms-bridge'), - '18' => __('Fukui', 'forms-bridge'), - '19' => __('Yamanashi', 'forms-bridge'), - '20' => __('Nagano', 'forms-bridge'), - '21' => __('Gifu', 'forms-bridge'), - '22' => __('Shizuoka', 'forms-bridge'), - '23' => __('Aichi', 'forms-bridge'), - '24' => __('Mie', 'forms-bridge'), - '25' => __('Shiga', 'forms-bridge'), - '26' => __('Kyoto', 'forms-bridge'), - '27' => __('Osaka', 'forms-bridge'), - '28' => __('Hyogo', 'forms-bridge'), - '29' => __('Nara', 'forms-bridge'), - '30' => __('Wakayama', 'forms-bridge'), - '31' => __('Tottori', 'forms-bridge'), - '32' => __('Shimane', 'forms-bridge'), - '33' => __('Okayama', 'forms-bridge'), - '34' => __('Hiroshima', 'forms-bridge'), - '35' => __('Yamaguchi', 'forms-bridge'), - '36' => __('Tokushima', 'forms-bridge'), - '37' => __('Kagawa', 'forms-bridge'), - '38' => __('Ehime', 'forms-bridge'), - '39' => __('Kochi', 'forms-bridge'), - '40' => __('Fukuoka', 'forms-bridge'), - '41' => __('Saga', 'forms-bridge'), - '42' => __('Nagasaki', 'forms-bridge'), - '43' => __('Kumamoto', 'forms-bridge'), - '44' => __('Oita', 'forms-bridge'), - '45' => __('Miyazaki', 'forms-bridge'), - '46' => __('Kagoshima', 'forms-bridge'), - '47' => __('Okinawa', 'forms-bridge'), - '01' => __('Hokkaido', 'forms-bridge'), - '02' => __('Aomori', 'forms-bridge'), - '03' => __('Iwate', 'forms-bridge'), - '04' => __('Miyagi', 'forms-bridge'), - '05' => __('Akita', 'forms-bridge'), - '06' => __('Yamagata', 'forms-bridge'), - '07' => __('Fukushima', 'forms-bridge'), - '08' => __('Ibaraki', 'forms-bridge'), - '09' => __('Tochigi', 'forms-bridge'), -]; - -global $forms_bridge_odoo_mn_states; -$forms_bridge_odoo_mn_states = [ - '10' => __('Өвөрхангай', 'forms-bridge'), - '11' => __('Өмнөговь', 'forms-bridge'), - '12' => __('Сүхбаатар', 'forms-bridge'), - '13' => __('Сэлэнгэ', 'forms-bridge'), - '14' => __('Төв', 'forms-bridge'), - '15' => __('Увс', 'forms-bridge'), - '16' => __('Ховд', 'forms-bridge'), - '17' => __('Хөвсгөл', 'forms-bridge'), - '18' => __('Хэнтий', 'forms-bridge'), - '19' => __('Дархан-Уул', 'forms-bridge'), - '20' => __('Орхон', 'forms-bridge'), - '23' => __('УБ - Хан Уул', 'forms-bridge'), - '24' => __('УБ - Баянзүрх', 'forms-bridge'), - '25' => __('УБ - Сүхбаатар', 'forms-bridge'), - '26' => __('УБ - Баянгол', 'forms-bridge'), - '27' => __('УБ - Багануур', 'forms-bridge'), - '28' => __('УБ - Багахангай', 'forms-bridge'), - '29' => __('УБ - Налайх', 'forms-bridge'), - '32' => __('Говьсүмбэр', 'forms-bridge'), - '34' => __('УБ - Сонгино Хайрхан', 'forms-bridge'), - '35' => __('УБ - Чингэлтэй', 'forms-bridge'), - '01' => __('Архангай', 'forms-bridge'), - '02' => __('Баян-Өлгий', 'forms-bridge'), - '03' => __('Баянхонгор', 'forms-bridge'), - '04' => __('Булган', 'forms-bridge'), - '05' => __('Говь-Алтай', 'forms-bridge'), - '06' => __('Дорноговь', 'forms-bridge'), - '07' => __('Дорнод', 'forms-bridge'), - '08' => __('Дундговь', 'forms-bridge'), - '09' => __('Завхан', 'forms-bridge'), -]; - -global $forms_bridge_odoo_mx_states; -$forms_bridge_odoo_mx_states = [ - 'AGU' => __('Aguascalientes', 'forms-bridge'), - 'BCN' => __('Baja California', 'forms-bridge'), - 'BCS' => __('Baja California Sur', 'forms-bridge'), - 'CAM' => __('Campeche', 'forms-bridge'), - 'CHH' => __('Chihuahua', 'forms-bridge'), - 'CHP' => __('Chiapas', 'forms-bridge'), - 'COA' => __('Coahuila', 'forms-bridge'), - 'COL' => __('Colima', 'forms-bridge'), - 'DIF' => __('Ciudad de México', 'forms-bridge'), - 'DUR' => __('Durango', 'forms-bridge'), - 'GRO' => __('Guerrero', 'forms-bridge'), - 'GUA' => __('Guanajuato', 'forms-bridge'), - 'HID' => __('Hidalgo', 'forms-bridge'), - 'JAL' => __('Jalisco', 'forms-bridge'), - 'MEX' => __('México', 'forms-bridge'), - 'MIC' => __('Michoacán', 'forms-bridge'), - 'MOR' => __('Morelos', 'forms-bridge'), - 'NAY' => __('Nayarit', 'forms-bridge'), - 'NLE' => __('Nuevo León', 'forms-bridge'), - 'OAX' => __('Oaxaca', 'forms-bridge'), - 'PUE' => __('Puebla', 'forms-bridge'), - 'QUE' => __('Querétaro', 'forms-bridge'), - 'ROO' => __('Quintana Roo', 'forms-bridge'), - 'SIN' => __('Sinaloa', 'forms-bridge'), - 'SLP' => __('San Luis Potosí', 'forms-bridge'), - 'SON' => __('Sonora', 'forms-bridge'), - 'TAB' => __('Tabasco', 'forms-bridge'), - 'TAM' => __('Tamaulipas', 'forms-bridge'), - 'TLA' => __('Tlaxcala', 'forms-bridge'), - 'VER' => __('Veracruz', 'forms-bridge'), - 'YUC' => __('Yucatán', 'forms-bridge'), - 'ZAC' => __('Zacatecas', 'forms-bridge'), -]; - -global $forms_bridge_odoo_my_states; -$forms_bridge_odoo_my_states = [ - 'JHR' => __('Johor', 'forms-bridge'), - 'KDH' => __('Kedah', 'forms-bridge'), - 'KTN' => __('Kelantan', 'forms-bridge'), - 'KUL' => __('Kuala Lumpur', 'forms-bridge'), - 'LBN' => __('Labuan', 'forms-bridge'), - 'MLK' => __('Melaka', 'forms-bridge'), - 'NSN' => __('Negeri Sembilan', 'forms-bridge'), - 'PHG' => __('Pahang', 'forms-bridge'), - 'PJY' => __('Putrajaya', 'forms-bridge'), - 'PLS' => __('Perlis', 'forms-bridge'), - 'PNG' => __('Pulau Pinang', 'forms-bridge'), - 'PRK' => __('Perak', 'forms-bridge'), - 'SBH' => __('Sabah', 'forms-bridge'), - 'SGR' => __('Selangor', 'forms-bridge'), - 'SWK' => __('Sarawak', 'forms-bridge'), - 'TRG' => __('Terengganu', 'forms-bridge'), -]; - -global $forms_bridge_odoo_nz_states; -$forms_bridge_odoo_nz_states = [ - 'AUK' => __('Auckland', 'forms-bridge'), - 'BOP' => __('Bay of Plenty', 'forms-bridge'), - 'CAN' => __('Canterbury', 'forms-bridge'), - 'GIS' => __('Gisborne', 'forms-bridge'), - 'HKB' => __('Hawke\'s Bay', 'forms-bridge'), - 'MBH' => __('Marlborough', 'forms-bridge'), - 'MWT' => __('Manawatu-Wanganui', 'forms-bridge'), - 'NSN' => __('Nelson', 'forms-bridge'), - 'NTL' => __('Northland', 'forms-bridge'), - 'OTA' => __('Otago', 'forms-bridge'), - 'STL' => __('Southland', 'forms-bridge'), - 'TAS' => __('Tasman', 'forms-bridge'), - 'TKI' => __('Taranaki', 'forms-bridge'), - 'WGN' => __('Wellington', 'forms-bridge'), - 'WKO' => __('Waikato', 'forms-bridge'), - 'WTC' => __('West Coast', 'forms-bridge'), -]; - -global $forms_bridge_odoo_pt_states; -$forms_bridge_odoo_pt_states = [ - '10' => __('Leiria', 'forms-bridge'), - '11' => __('Lisboa', 'forms-bridge'), - '12' => __('Portalegre', 'forms-bridge'), - '13' => __('Porto', 'forms-bridge'), - '14' => __('Santarém', 'forms-bridge'), - '15' => __('Setúbal', 'forms-bridge'), - '16' => __('Viana do Castelo', 'forms-bridge'), - '17' => __('Vila Real', 'forms-bridge'), - '18' => __('Viseu', 'forms-bridge'), - '20' => __('Açores', 'forms-bridge'), - '30' => __('Madeira', 'forms-bridge'), - '01' => __('Aveiro', 'forms-bridge'), - '02' => __('Beja', 'forms-bridge'), - '03' => __('Braga', 'forms-bridge'), - '04' => __('Bragança', 'forms-bridge'), - '05' => __('Castelo Branco', 'forms-bridge'), - '06' => __('Coimbra', 'forms-bridge'), - '07' => __('Évora', 'forms-bridge'), - '08' => __('Faro', 'forms-bridge'), - '09' => __('Guarda', 'forms-bridge'), -]; - -global $forms_bridge_odoo_ru_states; -$forms_bridge_odoo_ru_states = [ - 'AD' => __('Republic of Adygeya', 'forms-bridge'), - 'AL' => __('Altai Republic', 'forms-bridge'), - 'ALT' => __('Altai Krai', 'forms-bridge'), - 'AMU' => __('Amur Oblast', 'forms-bridge'), - 'ARK' => __('Arkhangelsk Oblast', 'forms-bridge'), - 'AST' => __('Astrakhan Oblast', 'forms-bridge'), - 'BA' => __('Republic of Bashkortostan', 'forms-bridge'), - 'BEL' => __('Belgorod Oblast', 'forms-bridge'), - 'BRY' => __('Bryansk Oblast', 'forms-bridge'), - 'BU' => __('Republic of Buryatia', 'forms-bridge'), - 'CE' => __('Chechen Republic', 'forms-bridge'), - 'CHE' => __('Chelyabinsk Oblast', 'forms-bridge'), - 'CHU' => __('Chukotka Autonomous Okrug', 'forms-bridge'), - 'CU' => __('Chuvash Republic', 'forms-bridge'), - 'DA' => __('Republic of Dagestan', 'forms-bridge'), - 'IN' => __('Republic of Ingushetia', 'forms-bridge'), - 'IRK' => __('Irkutsk Oblast', 'forms-bridge'), - 'IVA' => __('Ivanovo Oblast', 'forms-bridge'), - 'KAM' => __('Kamchatka Krai', 'forms-bridge'), - 'KB' => __('Kabardino-Balkarian Republic', 'forms-bridge'), - 'KC' => __('Karachay–Cherkess Republic', 'forms-bridge'), - 'KDA' => __('Krasnodar Krai', 'forms-bridge'), - 'KEM' => __('Kemerovo Oblast', 'forms-bridge'), - 'KGD' => __('Kaliningrad Oblast', 'forms-bridge'), - 'KGN' => __('Kurgan Oblast', 'forms-bridge'), - 'KHA' => __('Khabarovsk Krai', 'forms-bridge'), - 'KHM' => __('Khanty-Mansi Autonomous Okrug', 'forms-bridge'), - 'KIR' => __('Kirov Oblast', 'forms-bridge'), - 'KK' => __('Republic of Khakassia', 'forms-bridge'), - 'KL' => __('Republic of Kalmykia', 'forms-bridge'), - 'KLU' => __('Kaluga Oblast', 'forms-bridge'), - 'KO' => __('Komi Republic', 'forms-bridge'), - 'KOS' => __('Kostroma Oblast', 'forms-bridge'), - 'KR' => __('Republic of Karelia', 'forms-bridge'), - 'KRS' => __('Kursk Oblast', 'forms-bridge'), - 'KYA' => __('Krasnoyarsk Krai', 'forms-bridge'), - 'LEN' => __('Leningrad Oblast', 'forms-bridge'), - 'LIP' => __('Lipetsk Oblast', 'forms-bridge'), - 'MAG' => __('Magadan Oblast', 'forms-bridge'), - 'ME' => __('Mari El Republic', 'forms-bridge'), - 'MO' => __('Republic of Mordovia', 'forms-bridge'), - 'MOS' => __('Moscow Oblast', 'forms-bridge'), - 'MOW' => __('Moscow', 'forms-bridge'), - 'MUR' => __('Murmansk Oblast', 'forms-bridge'), - 'NGR' => __('Novgorod Oblast', 'forms-bridge'), - 'NIZ' => __('Nizhny Novgorod Oblast', 'forms-bridge'), - 'NVS' => __('Novosibirsk Oblast', 'forms-bridge'), - 'OMS' => __('Omsk Oblast', 'forms-bridge'), - 'ORE' => __('Orenburg Oblast', 'forms-bridge'), - 'ORL' => __('Oryol Oblast', 'forms-bridge'), - 'PER' => __('Perm Krai', 'forms-bridge'), - 'PNZ' => __('Penza Oblast', 'forms-bridge'), - 'PRI' => __('Primorsky Krai', 'forms-bridge'), - 'PSK' => __('Pskov Oblast', 'forms-bridge'), - 'ROS' => __('Rostov Oblast', 'forms-bridge'), - 'RYA' => __('Ryazan Oblast', 'forms-bridge'), - 'SA' => __('Sakha Republic (Yakutia)', 'forms-bridge'), - 'SAK' => __('Sakhalin Oblast', 'forms-bridge'), - 'SAM' => __('Samara Oblast', 'forms-bridge'), - 'SAR' => __('Saratov Oblast', 'forms-bridge'), - 'SE' => __('Republic of North Ossetia–Alania', 'forms-bridge'), - 'SMO' => __('Smolensk Oblast', 'forms-bridge'), - 'SPE' => __('Saint Petersburg', 'forms-bridge'), - 'STA' => __('Stavropol Krai', 'forms-bridge'), - 'SVE' => __('Sverdlovsk Oblast', 'forms-bridge'), - 'TA' => __('Republic of Tatarstan', 'forms-bridge'), - 'TAM' => __('Tambov Oblast', 'forms-bridge'), - 'TOM' => __('Tomsk Oblast', 'forms-bridge'), - 'TUL' => __('Tula Oblast', 'forms-bridge'), - 'TVE' => __('Tver Oblast', 'forms-bridge'), - 'TY' => __('Tyva Republic', 'forms-bridge'), - 'TYU' => __('Tyumen Oblast', 'forms-bridge'), - 'UD' => __('Udmurtia', 'forms-bridge'), - 'ULY' => __('Ulyanovsk Oblast', 'forms-bridge'), - 'VGG' => __('Volgograd Oblast', 'forms-bridge'), - 'VLA' => __('Vladimir Oblast', 'forms-bridge'), - 'VLG' => __('Vologda Oblast', 'forms-bridge'), - 'VOR' => __('Voronezh Oblast', 'forms-bridge'), - 'YAN' => __('Yamalo-Nenets Autonomous Okrug', 'forms-bridge'), - 'YAR' => __('Yaroslavl Oblast', 'forms-bridge'), - 'YEV' => __('Jewish Autonomous Oblast', 'forms-bridge'), -]; - -global $forms_bridge_odoo_us_states; -$forms_bridge_odoo_us_states = [ - 'AA' => __('Armed Forces Americas', 'forms-bridge'), - 'AE' => __('Armed Forces Europe', 'forms-bridge'), - 'AK' => __('Alaska', 'forms-bridge'), - 'AL' => __('Alabama', 'forms-bridge'), - 'AP' => __('Armed Forces Pacific', 'forms-bridge'), - 'AR' => __('Arkansas', 'forms-bridge'), - 'AS' => __('American Samoa', 'forms-bridge'), - 'AZ' => __('Arizona', 'forms-bridge'), - 'CA' => __('California', 'forms-bridge'), - 'CO' => __('Colorado', 'forms-bridge'), - 'CT' => __('Connecticut', 'forms-bridge'), - 'DC' => __('District of Columbia', 'forms-bridge'), - 'DE' => __('Delaware', 'forms-bridge'), - 'FL' => __('Florida', 'forms-bridge'), - 'FM' => __('Federated States of Micronesia', 'forms-bridge'), - 'GA' => __('Georgia', 'forms-bridge'), - 'GU' => __('Guam', 'forms-bridge'), - 'HI' => __('Hawaii', 'forms-bridge'), - 'IA' => __('Iowa', 'forms-bridge'), - 'ID' => __('Idaho', 'forms-bridge'), - 'IL' => __('Illinois', 'forms-bridge'), - 'IN' => __('Indiana', 'forms-bridge'), - 'KS' => __('Kansas', 'forms-bridge'), - 'KY' => __('Kentucky', 'forms-bridge'), - 'LA' => __('Louisiana', 'forms-bridge'), - 'MA' => __('Massachusetts', 'forms-bridge'), - 'MD' => __('Maryland', 'forms-bridge'), - 'ME' => __('Maine', 'forms-bridge'), - 'MH' => __('Marshall Islands', 'forms-bridge'), - 'MI' => __('Michigan', 'forms-bridge'), - 'MN' => __('Minnesota', 'forms-bridge'), - 'MO' => __('Missouri', 'forms-bridge'), - 'MP' => __('Northern Mariana Islands', 'forms-bridge'), - 'MS' => __('Mississippi', 'forms-bridge'), - 'MT' => __('Montana', 'forms-bridge'), - 'NC' => __('North Carolina', 'forms-bridge'), - 'ND' => __('North Dakota', 'forms-bridge'), - 'NE' => __('Nebraska', 'forms-bridge'), - 'NH' => __('New Hampshire', 'forms-bridge'), - 'NJ' => __('New Jersey', 'forms-bridge'), - 'NM' => __('New Mexico', 'forms-bridge'), - 'NV' => __('Nevada', 'forms-bridge'), - 'NY' => __('New York', 'forms-bridge'), - 'OH' => __('Ohio', 'forms-bridge'), - 'OK' => __('Oklahoma', 'forms-bridge'), - 'OR' => __('Oregon', 'forms-bridge'), - 'PA' => __('Pennsylvania', 'forms-bridge'), - 'PR' => __('Puerto Rico', 'forms-bridge'), - 'PW' => __('Palau', 'forms-bridge'), - 'RI' => __('Rhode Island', 'forms-bridge'), - 'SC' => __('South Carolina', 'forms-bridge'), - 'SD' => __('South Dakota', 'forms-bridge'), - 'TN' => __('Tennessee', 'forms-bridge'), - 'TX' => __('Texas', 'forms-bridge'), - 'UT' => __('Utah', 'forms-bridge'), - 'VA' => __('Virginia', 'forms-bridge'), - 'VI' => __('Virgin Islands', 'forms-bridge'), - 'VT' => __('Vermont', 'forms-bridge'), - 'WA' => __('Washington', 'forms-bridge'), - 'WI' => __('Wisconsin', 'forms-bridge'), - 'WV' => __('West Virginia', 'forms-bridge'), - 'WY' => __('Wyoming', 'forms-bridge'), -]; - -global $forms_bridge_odoo_za_states; -$forms_bridge_odoo_za_states = [ - 'EC' => __('Eastern Cape', 'forms-bridge'), - 'FS' => __('Free State', 'forms-bridge'), - 'GT' => __('Gauteng', 'forms-bridge'), - 'LP' => __('Limpopo', 'forms-bridge'), - 'MP' => __('Mpumalanga', 'forms-bridge'), - 'NC' => __('Northern Cape', 'forms-bridge'), - 'NL' => __('KwaZulu-Natal', 'forms-bridge'), - 'NW' => __('North West', 'forms-bridge'), - 'WC' => __('Western Cape', 'forms-bridge'), -]; diff --git a/addons/odoo/hooks.php b/addons/odoo/hooks.php deleted file mode 100644 index 972ac1f2..00000000 --- a/addons/odoo/hooks.php +++ /dev/null @@ -1,268 +0,0 @@ - [ - [ - 'ref' => '#credential', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'schema', - 'type' => 'text', - 'value' => 'RPC', - ], - [ - 'ref' => '#credential', - 'name' => 'database', - 'label' => __('Database', 'forms-bridge'), - 'description' => __( - 'Name of the database', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_id', - 'label' => __('User', 'forms-bridge'), - 'description' => __( - 'User name or email', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_secret', - 'description' => __('User password', 'forms-bridge'), - 'label' => __('Password', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#backend', - 'name' => 'name', - 'default' => 'Odoo', - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'label' => __('Model', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'label' => __('Method', 'forms-bridge'), - 'type' => 'text', - 'value' => 'create', - 'required' => true, - ], - ], - 'bridge' => [ - 'name' => '', - 'form_id' => '', - 'backend' => '', - 'endpoint' => '', - ], - 'backend' => [ - 'name' => 'Odoo', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - 'credential' => [ - 'name' => '', - 'schema' => 'RPC', - 'client_id' => '', - 'client_secret' => '', - 'database' => '', - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'odoo-') !== 0) { - return $data; - } - - $index = array_search( - 'tag_ids', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = $data['bridge']['custom_fields'][$index]; - $tags = $field['value'] ?? []; - - for ($i = 0; $i < count($tags); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "tag_ids[{$i}]", - 'value' => $tags[$i], - ]; - - $data['bridge']['mutations'][0][] = [ - 'from' => "tag_ids[{$i}]", - 'to' => "tag_ids[{$i}]", - 'cast' => 'integer', - ]; - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'categ_ids', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = $data['bridge']['custom_fields'][$index]; - $tags = $field['value'] ?? []; - - for ($i = 0; $i < count($tags); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "categ_ids[{$i}]", - 'value' => $tags[$i], - ]; - - $data['bridge']['mutations'][0][] = [ - 'from' => "categ_ids[{$i}]", - 'to' => "categ_ids[{$i}]", - 'cast' => 'integer', - ]; - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'list_ids', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = $data['bridge']['custom_fields'][$index]; - $lists = $field['value'] ?? []; - - for ($i = 0; $i < count($lists); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "list_ids[{$i}]", - 'value' => $lists[$i], - ]; - - $data['bridge']['mutations'][0][] = [ - 'from' => "list_ids[{$i}]", - 'to' => "list_ids[{$i}]", - 'cast' => 'integer', - ]; - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'allday', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $data['form']['fields'] = array_filter( - $data['form']['fields'], - function ($field) { - return !in_array( - $field['name'], - [ - 'hour', - 'minute', - __('Hour', 'forms-bridge'), - __('Minute', 'forms-bridge'), - ], - true - ); - } - ); - - $index = array_search( - 'duration', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - array_splice($data['bridge']['custom_fields'], $index, 1); - } - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/odoo/jobs/appointment-attendee.php b/addons/odoo/jobs/appointment-attendee.php deleted file mode 100644 index a046b3a4..00000000 --- a/addons/odoo/jobs/appointment-attendee.php +++ /dev/null @@ -1,134 +0,0 @@ -patch([ - 'name' => 'odoo-get-user-by-id', - 'endpoint' => 'res.users', - 'method' => 'read', - ]) - ->submit([$payload['user_id']]); - - if (is_wp_error($user_response)) { - return $user_response; - } - - $payload['partner_ids'][] = - $user_response['data']['result'][0]['partner_id'][0]; - } - - return $payload; -} - -return [ - 'title' => __('Appointment attendees', 'forms-bridge'), - 'description' => __( - 'Search for partner by email or creates a new one and sets it as the appointment attendee. If user_id, also adds user as attendee.', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_appointment_attendees', - 'input' => [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'title', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'lang', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'employee', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'function', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'website', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street2', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'city', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'parent_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'additional_info', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'is_public', - 'schema' => ['type' => 'boolean'], - ], - ], - 'output' => [ - [ - 'name' => 'user_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'partner_ids', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - 'additionalItems' => true, - ], - ], - ], -]; diff --git a/addons/odoo/jobs/appointment-dates.php b/addons/odoo/jobs/appointment-dates.php deleted file mode 100644 index 4eb96d12..00000000 --- a/addons/odoo/jobs/appointment-dates.php +++ /dev/null @@ -1,57 +0,0 @@ -getTimestamp(); - - $duration = floatval($payload['duration'] ?? 1); - - $payload['start'] = date('Y-m-d H:i:s', $timestamp); - - $end = $duration * 3600 + $timestamp; - $payload['stop'] = date('Y-m-d H:i:s', $end); - - return $payload; -} - -return [ - 'title' => __('Appointment dates', 'forms-bridge'), - 'description' => __( - 'Sets appointment start and stop time from "timestamp" and "duration" fields.', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_appointment_dates', - 'input' => [ - [ - 'name' => 'date', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'duration', - 'schema' => ['type' => 'number'], - ], - ], - 'output' => [ - [ - 'name' => 'start', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'stop', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/odoo/jobs/attachments.php b/addons/odoo/jobs/attachments.php deleted file mode 100644 index bbd4d67b..00000000 --- a/addons/odoo/jobs/attachments.php +++ /dev/null @@ -1,90 +0,0 @@ - __('Attachments', 'forms-bridge'), - 'description' => __( - 'Gets submission uploads and sets them as attached files to the newly created model', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_bridge_attachments', - 'input' => [], - 'output' => [], -]; - -function forms_bridge_odoo_bridge_attachments($payload, $bridge) -{ - if (!in_array($bridge->method, ['write', 'create'], true)) { - return $payload; - } - - $uploads = FBAPI::get_uploads(); - - if (empty($uploads)) { - return $payload; - } - - $attachments = []; - - foreach ($uploads as $name => $upload) { - if ($upload['is_multi']) { - $len = count($uploads[$name]['path']); - for ($i = 1; $i <= $len; ++$i) { - $attachments[$name . '_' . $i] = $upload['path'][$i - 1]; - } - } else { - $attachments[$name] = $upload['path']; - } - } - - add_action( - 'forms_bridge_after_submission', - function ($bridge, $response) use ($payload, $attachments) { - $res_id = $response['data']['result']; - $res_model = $bridge->endpoint; - - foreach ($attachments as $filename => $path) { - $content_field = $filename; - $name_field = $filename . '_filename'; - $mimetype = mime_content_type($path); - - if (!isset($payload[$content_field], $payload[$name_field])) { - continue; - } - - $response = $bridge - ->patch([ - 'name' => '__odoo-ir-attachments', - 'endpoint' => 'ir.attachment', - 'method' => 'create', - ]) - ->submit([ - 'name' => $payload[$name_field], - 'datas' => $payload[$content_field], - 'res_id' => $res_id, - 'res_model' => $res_model, - 'mimetype' => $mimetype, - ]); - - if (is_wp_error($response)) { - do_action( - 'forms_bridge_on_failure', - $response, - $bridge, - $payload, - $attachments - ); - - return; - } - } - }, - 20, - 2 - ); - - return $payload; -} diff --git a/addons/odoo/jobs/candidate.php b/addons/odoo/jobs/candidate.php deleted file mode 100644 index da9187a8..00000000 --- a/addons/odoo/jobs/candidate.php +++ /dev/null @@ -1,102 +0,0 @@ - __('Candidate', 'forms-bridge'), - 'description' => __( - 'Creates a recruitement candidate and sets its ID as the candidate_id field of the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_create_candidate', - 'input' => [ - [ - 'name' => 'partner_name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'partner_phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email_from', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'user_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'type_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'partner_id', - 'schema' => ['type' => 'integer'], - ], - ], - 'output' => [ - [ - 'name' => 'candidate_id', - 'schema' => ['type' => 'integer'], - ], - ], -]; - -function forms_bridge_odoo_create_candidate($payload, $bridge) -{ - $candidate = [ - 'partner_name' => $payload['partner_name'], - ]; - - $fields = [ - 'partner_id', - 'partner_phone', - 'email_from', - 'user_id', - 'type_id', - ]; - - foreach ($fields as $field) { - if (isset($payload[$field])) { - $candidate[$field] = $payload[$field]; - } - } - - $query = [['partner_name', '=', $candidate['partner_name']]]; - - if (isset($candidate['email_from'])) { - $query[] = ['email_from', '=', $candidate['email_from']]; - } - - $response = $bridge - ->patch([ - 'name' => '__odoo-search-candidate', - 'endpoint' => 'hr.candidate', - 'method' => 'search', - ]) - ->submit($query); - - if (!is_wp_error($response)) { - $payload['candidate_id'] = (int) $response['data']['result'][0]; - return $payload; - } - - $response = $bridge - ->patch([ - 'name' => '__odoo-create-candidate', - 'endpoint' => 'hr.candidate', - ]) - ->submit($candidate); - - if (is_wp_error($response)) { - return $response; - } - - $payload['candidate_id'] = (int) $response['data']['result']; - - return $payload; -} diff --git a/addons/odoo/jobs/contact-company.php b/addons/odoo/jobs/contact-company.php deleted file mode 100644 index b0259330..00000000 --- a/addons/odoo/jobs/contact-company.php +++ /dev/null @@ -1,95 +0,0 @@ - __('Contact\'s company', 'forms-bridge'), - 'description' => __( - 'Creates a company and sets its ID as the parent_id of the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_contact_company', - 'input' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'lang', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'website', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street2', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'city', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'additional_info', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'is_public', - 'schema' => ['type' => 'boolean'], - ], - ], - 'output' => [ - [ - 'name' => 'parent_id', - 'schema' => ['type' => 'integer'], - ], - ], -]; diff --git a/addons/odoo/jobs/contact.php b/addons/odoo/jobs/contact.php deleted file mode 100644 index 5964706b..00000000 --- a/addons/odoo/jobs/contact.php +++ /dev/null @@ -1,107 +0,0 @@ - __('Contact', 'forms-bridge'), - 'description' => __( - 'Creates a contact and sets its ID as the partner_id field of the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_contact_id', - 'input' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'title', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'lang', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'website', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street2', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'city', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'additional_info', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'is_public', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'parent_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'function', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'partner_id', - 'schema' => ['type' => 'integer'], - ], - ], -]; diff --git a/addons/odoo/jobs/country-id.php b/addons/odoo/jobs/country-id.php deleted file mode 100644 index 157e48dc..00000000 --- a/addons/odoo/jobs/country-id.php +++ /dev/null @@ -1,60 +0,0 @@ -patch([ - 'name' => 'odoo-get-country-id', - 'endpoint' => 'res.country', - 'method' => 'search', - ]) - ->submit([['code', '=', $payload['country']]]); - - if (is_wp_error($response)) { - return $response; - } - - $payload['country_id'] = $response['data']['result'][0]; - return $payload; -} - -return [ - 'title' => __('Country ID from code', 'forms-bridge'), - 'description' => __( - 'Given a iso2 code code gets the internal country ID', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_country_id_from_code', - 'input' => [ - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'country_id', - 'schema' => ['type' => 'integer'], - ], - ], -]; diff --git a/addons/odoo/jobs/crm-contact.php b/addons/odoo/jobs/crm-contact.php deleted file mode 100644 index ca658ebb..00000000 --- a/addons/odoo/jobs/crm-contact.php +++ /dev/null @@ -1,119 +0,0 @@ - __('CRM lead contact', 'forms-bridge'), - 'description' => __( - 'Creates a new contact and sets its email as the email_from and its ID as the partner_id on the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_crm_lead_contact', - 'input' => [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'title', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'lang', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'employee', - 'schema' => ['type' => 'boolean'], - ], - [ - 'name' => 'function', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'website', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street2', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'city', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'parent_id', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'additional_info', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'is_public', - 'schema' => ['type' => 'boolean'], - ], - ], - 'output' => [ - [ - 'name' => 'email_from', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'partner_id', - 'schema' => ['type' => 'integer'], - ], - ], -]; diff --git a/addons/odoo/jobs/delivery-address.php b/addons/odoo/jobs/delivery-address.php deleted file mode 100644 index f7c2ed14..00000000 --- a/addons/odoo/jobs/delivery-address.php +++ /dev/null @@ -1,126 +0,0 @@ - __('Shipping address', 'forms-bridge'), - 'description' => __( - 'Creates a shipping address linked to a contact.', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_shipping_address', - 'input' => [ - [ - 'name' => 'partner_id', - 'schema' => ['type' => 'integer'], - 'required' => true, - ], - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'street2', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'city', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'zip', - 'schema' => ['type' => 'string'], - ], - // [ - // 'name' => 'state', - // 'schema' => ['type' => 'string'], - // ], - // [ - // 'name' => 'country', - // 'schema' => ['type' => 'string'], - // ], - [ - 'name' => 'comment', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [], -]; - -function forms_bridge_odoo_shipping_address($payload, $bridge) -{ - $query = [ - ['type', '=', 'delivery'], - ['parent_id', '=', $payload['partner_id']], - ['name', '=', $payload['name']], - ]; - - $response = $bridge - ->patch([ - 'name' => 'odoo-search-address', - 'method' => 'search', - 'endpoint' => 'res.partner', - ]) - ->submit($query); - - if (!is_wp_error($response)) { - return $payload; - } - - $address = []; - foreach ($query as $filter) { - $address[$filter[0]] = $filter[2]; - } - - $address_fields = [ - 'email', - 'phone', - 'mobile', - 'street', - 'street2', - 'city', - 'zip', - // 'state', - // 'country', - 'comment', - ]; - - foreach ($address_fields as $field) { - if (isset($payload[$field])) { - $address[$field] = $payload[$field]; - } - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-delivery-address', - 'endpoint' => 'res.partner', - 'method' => 'create', - ]) - ->submit($address); - - if (is_wp_error($response)) { - return $response; - } - - return $payload; -} diff --git a/addons/odoo/jobs/mailing-contact.php b/addons/odoo/jobs/mailing-contact.php deleted file mode 100644 index 32b51014..00000000 --- a/addons/odoo/jobs/mailing-contact.php +++ /dev/null @@ -1,70 +0,0 @@ -patch([ - 'name' => 'odoo-search-mailing-contact-by-email', - 'template' => null, - 'method' => 'search', - 'endpoint' => 'mailing.contact', - ]) - ->submit([['email', '=', $payload['email']]]); - - if (!is_wp_error($response)) { - $contact_id = $response['data']['result'][0]; - $list_ids = $payload['list_ids']; - - $response = $bridge - ->patch([ - 'name' => 'odoo-update-mailing-contact-subscriptions', - 'template' => null, - 'endpoint' => 'mailing.contact', - 'method' => 'write', - ]) - ->submit([$contact_id], ['list_ids' => $list_ids]); - - if (is_wp_error($response)) { - return $response; - } - - return; - } - - return $payload; -} - -return [ - 'title' => __('Skip subscription', 'forms-bridge'), - 'description' => __( - 'Search for a subscribed mailing contact, updates its subscriptions and skips if succeed', - 'forms-bridge' - ), - 'method' => 'forms_brige_odoo_update_mailing_contact', - 'input' => [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/odoo/jobs/products-by-ref.php b/addons/odoo/jobs/products-by-ref.php deleted file mode 100644 index b8b7e791..00000000 --- a/addons/odoo/jobs/products-by-ref.php +++ /dev/null @@ -1,80 +0,0 @@ - __('Products by reference', 'forms-bridge'), - 'description' => __( - 'Search for products on Odoo based on a list of internal references and returns its IDs.', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_search_products_by_ref', - 'input' => [ - [ - 'name' => 'internal_refs', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - 'additionalItems' => true, - ], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'product_ids', - 'schema' => [ - 'type' => 'array', - 'items' => ['type' => 'integer'], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_odoo_search_products_by_ref($payload, $bridge) -{ - $response = $bridge - ->patch([ - 'name' => 'odoo-search-products-by-ref', - 'endpoint' => 'product.product', - 'method' => 'search_read', - ]) - ->submit( - [['default_code', 'in', $payload['internal_refs']]], - ['id', 'default_code'] - ); - - if (is_wp_error($response)) { - return $response; - } - - $product_ids = []; - foreach ($payload['internal_refs'] as $ref) { - foreach ($response['data']['result'] as $product) { - if ($product['default_code'] === $ref) { - $product_ids[] = $product['id']; - break; - } - } - } - - if (count($product_ids) !== count($payload['internal_refs'])) { - return new WP_Error( - 'product_search_error', - __( - 'Inconsistencies between amount of found products and search references', - 'forms-bridge' - ), - [ - 'response' => $response, - 'internal_refs' => $payload['internal_refs'], - ] - ); - } - - $payload['product_ids'] = $product_ids; - return $payload; -} diff --git a/addons/odoo/jobs/skip-if-partner-exists.php b/addons/odoo/jobs/skip-if-partner-exists.php deleted file mode 100644 index 8f8c5c71..00000000 --- a/addons/odoo/jobs/skip-if-partner-exists.php +++ /dev/null @@ -1,87 +0,0 @@ -patch([ - 'name' => 'odoo-search-contact-by-email', - 'method' => 'search', - 'endpoint' => 'res.partner', - ]) - ->submit($query); - - if (!is_wp_error($response)) { - $partner_id = $response['data']['result'][0]; - - $response = $bridge - ->patch([ - 'name' => 'odoo-update-contact', - 'method' => 'write', - 'endpoint' => 'res.partner', - ]) - ->submit([$partner_id], $payload); - - if (is_wp_error($response)) { - return $response; - } - - return; - } - - return $payload; -} - -return [ - 'title' => __('Skip if contact exists', 'forms-bridge'), - 'description' => __( - 'Search contacts by name, email and vat and skip submission if it exists', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_skip_if_partner_exists', - 'input' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'name', - 'schema' => ['type' => 'string'], - 'requires' => ['name'], - ], - [ - 'name' => 'email', - 'schema' => ['type' => 'string'], - 'requires' => ['email'], - ], - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - 'requires' => ['vat'], - ], - ], -]; diff --git a/addons/odoo/jobs/state-id.php b/addons/odoo/jobs/state-id.php deleted file mode 100644 index 7d1f5d56..00000000 --- a/addons/odoo/jobs/state-id.php +++ /dev/null @@ -1,73 +0,0 @@ -patch([ - 'endpoint' => 'res.country.state', - 'method' => 'search', - ]) - ->submit([['code', '=', $payload['state_code']]]); - - if (is_wp_error($response)) { - return $response; - } - - $payload['state_id'] = $response['data']['result'][0]; - return $payload; -} - -return [ - 'title' => __('State ID from code', 'forms-bridge'), - 'description' => __( - 'Given a iso2 country code code and a state odoo code gets the internal state ID', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_state_id_from_code', - 'input' => [ - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'state_code', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'country_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'state_code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'state_id', - 'schema' => ['type' => 'integer'], - ], - ], -]; diff --git a/addons/odoo/jobs/sync-products-by-ref.php b/addons/odoo/jobs/sync-products-by-ref.php deleted file mode 100644 index 14197602..00000000 --- a/addons/odoo/jobs/sync-products-by-ref.php +++ /dev/null @@ -1,144 +0,0 @@ - __('Sync woo products', 'forms-bridge'), - 'description' => __( - 'Search for products from the WooCommerce order by sku on Odoo and creates new ones if someone does not exists', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_sync_products_by_ref', - 'input' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - 'required' => ['sku', 'name', 'price'], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - 'required' => true, - ], - ], - 'output' => [ - [ - 'name' => 'line_items', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'product_id' => ['type' => 'integer'], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - ], - ], - 'additionalProperties' => true, - ], - 'additionalItems' => true, - ], - ], - ], -]; - -function forms_bridge_odoo_sync_products_by_ref($payload, $bridge) -{ - $internal_refs = []; - foreach ($payload['line_items'] as $line_item) { - if (empty($line_item['product']['sku'])) { - return new WP_Error( - "SKU is required on product {$line_item['product']['name']}" - ); - } - - $internal_refs[] = $line_item['product']['sku']; - } - - $response = $bridge - ->patch([ - 'name' => 'odoo-search-products-by-ref', - 'endpoint' => 'product.product', - 'method' => 'search_read', - ]) - ->submit( - [['default_code', 'in', $internal_refs]], - ['id', 'default_code'] - ); - - if (is_wp_error($response)) { - if ($response->get_error_code() !== 'not_found') { - return $response; - } - - $response = $response->get_error_data()['response']; - $response['data']['result'] = []; - } - - foreach ($payload['line_items'] as $line_item) { - $product = null; - foreach ($response['data']['result'] as $candidate) { - if ($candidate['default_code'] === $line_item['product']['sku']) { - $product = $candidate; - break; - } - } - - if (!$product) { - $product_response = $bridge - ->patch([ - 'name' => 'odoo-sync-product-by-ref', - 'endpoint' => 'product.product', - 'method' => 'create', - ]) - ->submit([ - 'name' => $line_item['product']['name'], - 'list_price' => $line_item['product']['price'], - 'default_code' => $line_item['product']['sku'], - 'sale_ok' => true, - 'purchase_ok' => false, - ]); - - if (is_wp_error($product_response)) { - return $product_response; - } - } - } - - return $payload; -} diff --git a/addons/odoo/jobs/vat-id.php b/addons/odoo/jobs/vat-id.php deleted file mode 100644 index 17c2cfca..00000000 --- a/addons/odoo/jobs/vat-id.php +++ /dev/null @@ -1,84 +0,0 @@ - __('Prefixed vat ID', 'forms-bridge'), - 'description' => __( - 'Prefix the vat with country code, or the current locale, if it isn\'t prefixed', - 'forms-bridge' - ), - 'method' => 'forms_bridge_odoo_vat_id', - 'input' => [ - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'vat', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - 'requires' => ['country'], - ], - ], -]; diff --git a/addons/odoo/templates/appointments.php b/addons/odoo/templates/appointments.php deleted file mode 100644 index 078d540a..00000000 --- a/addons/odoo/templates/appointments.php +++ /dev/null @@ -1,277 +0,0 @@ - __('Appointments', 'forms-bridge'), - 'description' => __( - 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Appointments', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'calendar.event', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'user_id', - 'label' => __('Host', 'forms-bridge'), - 'description' => __( - 'Name of the host user of the appointment', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'res.users', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'event_name', - 'label' => __('Appointment name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web Appointment', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'allday', - 'label' => __('Is all day event?', 'forms-bridge'), - 'type' => 'boolean', - 'default' => false, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'duration', - 'label' => __('Duration (Hours)', 'forms-bridge'), - 'type' => 'number', - 'default' => 1, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'categ_ids', - 'label' => __('Appointment tags', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'calendar.event.type', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - 'is_multi' => true, - ], - ], - 'bridge' => [ - 'endpoint' => 'calendar.event', - 'mutations' => [ - [ - [ - 'from' => '?user_id', - 'to' => 'user_id', - 'cast' => 'integer', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?allday', - 'to' => 'allday', - 'cast' => 'boolean', - ], - [ - 'from' => '?duration', - 'to' => 'duration', - 'cast' => 'number', - ], - ], - [ - [ - 'from' => 'datetime', - 'to' => 'date', - 'cast' => 'string', - ], - ], - [], - [ - [ - 'from' => 'event_name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - ], - 'workflow' => [ - 'date-fields-to-date', - 'appointment-dates', - 'appointment-attendee', - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'your-name', - 'label' => __('Your name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'phone', - 'label' => __('Your phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'date', - 'label' => __('Date', 'forms-bridge'), - 'type' => 'date', - 'required' => true, - ], - [ - 'name' => 'hour', - 'label' => __('Hour', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - [ - 'label' => __('1 AM', 'forms-bridge'), - 'value' => '01', - ], - [ - 'label' => __('2 AM', 'forms-bridge'), - 'value' => '02', - ], - [ - 'label' => __('3 AM', 'forms-bridge'), - 'value' => '03', - ], - [ - 'label' => __('4 AM', 'forms-bridge'), - 'value' => '04', - ], - [ - 'label' => __('5 AM', 'forms-bridge'), - 'value' => '05', - ], - [ - 'label' => __('6 AM', 'forms-bridge'), - 'value' => '06', - ], - [ - 'label' => __('7 AM', 'forms-bridge'), - 'value' => '07', - ], - [ - 'label' => __('8 AM', 'forms-bridge'), - 'value' => '08', - ], - [ - 'label' => __('9 AM', 'forms-bridge'), - 'value' => '09', - ], - [ - 'label' => __('10 AM', 'forms-bridge'), - 'value' => '10', - ], - [ - 'label' => __('11 AM', 'forms-bridge'), - 'value' => '11', - ], - [ - 'label' => __('12 AM', 'forms-bridge'), - 'value' => '12', - ], - [ - 'label' => __('1 PM', 'forms-bridge'), - 'value' => '13', - ], - [ - 'label' => __('2 PM', 'forms-bridge'), - 'value' => '14', - ], - [ - 'label' => __('3 PM', 'forms-bridge'), - 'value' => '15', - ], - [ - 'label' => __('4 PM', 'forms-bridge'), - 'value' => '16', - ], - [ - 'label' => __('5 PM', 'forms-bridge'), - 'value' => '17', - ], - [ - 'label' => __('6 PM', 'forms-bridge'), - 'value' => '18', - ], - [ - 'label' => __('7 PM', 'forms-bridge'), - 'value' => '19', - ], - [ - 'label' => __('8 PM', 'forms-bridge'), - 'value' => '20', - ], - [ - 'label' => __('9 PM', 'forms-bridge'), - 'value' => '21', - ], - [ - 'label' => __('10 PM', 'forms-bridge'), - 'value' => '22', - ], - [ - 'label' => __('11 PM', 'forms-bridge'), - 'value' => '23', - ], - [ - 'label' => __('12 PM', 'forms-bridge'), - 'value' => '24', - ], - ], - ], - [ - 'name' => 'minute', - 'label' => __('Minute', 'forms-bridge'), - 'type' => 'select', - 'required' => true, - 'options' => [ - ['label' => '00', 'value' => '00.0'], - ['label' => '05', 'value' => '05'], - ['label' => '10', 'value' => '10'], - ['label' => '15', 'value' => '15'], - ['label' => '20', 'value' => '20'], - ['label' => '25', 'value' => '25'], - ['label' => '30', 'value' => '30'], - ['label' => '35', 'value' => '35'], - ['label' => '40', 'value' => '40'], - ['label' => '45', 'value' => '45'], - ['label' => '50', 'value' => '50'], - ['label' => '55', 'value' => '55'], - ], - ], - ], - ], -]; diff --git a/addons/odoo/templates/company-contacts.php b/addons/odoo/templates/company-contacts.php deleted file mode 100644 index 03533d51..00000000 --- a/addons/odoo/templates/company-contacts.php +++ /dev/null @@ -1,148 +0,0 @@ - __('Company Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form for companies template. The resulting bridge will convert form submissions into new companies linked to contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Contacts', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'res.partner', - ], - ], - 'bridge' => [ - 'endpoint' => 'res.partner', - 'workflow' => [ - 'iso2-country-code', - 'vat-id', - 'country-id', - 'contact-company', - 'skip-if-partner-exists', - ], - 'mutations' => [ - [ - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - [], - [ - [ - 'from' => 'email', - 'to' => 'contact_email', - 'cast' => 'copy', - ], - [ - 'from' => 'phone', - 'to' => 'contact_phone', - 'cast' => 'copy', - ], - ], - [], - [ - [ - 'from' => 'contact_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'contact_phone', - 'to' => 'phone', - 'cast' => 'string', - ], - ], - ], - [], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Company name', 'forms-bridge'), - 'name' => 'company_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'vat', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'street', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Job position', 'forms-bridge'), - 'name' => 'function', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - ], - ], - ], -]; diff --git a/addons/odoo/templates/company-quotations.php b/addons/odoo/templates/company-quotations.php deleted file mode 100644 index a90defb5..00000000 --- a/addons/odoo/templates/company-quotations.php +++ /dev/null @@ -1,209 +0,0 @@ - __('Company Quotations', 'forms-bridge'), - 'description' => __( - 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Quotations', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'sale.order', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'product_id', - 'label' => __('Product', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'product.product', - 'finger' => [ - 'value' => 'result[].id', - 'label' => 'result[].name', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'endpoint' => 'sale.order', - 'custom_fields' => [ - [ - 'name' => 'state', - 'value' => 'draft', - ], - [ - 'name' => 'order_line[0][0]', - 'value' => '0', - ], - [ - 'name' => 'order_line[0][1]', - 'value' => '0', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'order_line[0][0]', - 'to' => 'order_line[0][0]', - 'cast' => 'integer', - ], - [ - 'from' => 'order_line[0][1]', - 'to' => 'order_line[0][1]', - 'cast' => 'integer', - ], - [ - 'from' => 'quantity', - 'to' => 'order_line[0][2].product_uom_qty', - 'cast' => 'integer', - ], - [ - 'from' => 'product_id', - 'to' => 'order_line[0][2].product_id', - 'cast' => 'integer', - ], - [ - 'from' => 'company-name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - [], - [], - [ - [ - 'from' => 'email', - 'to' => 'contact_email', - 'cast' => 'copy', - ], - [ - 'from' => 'phone', - 'to' => 'contact_phone', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'parent_id', - 'to' => 'company_partner_id', - 'cast' => 'copy', - ], - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'contact_phone', - 'to' => 'phone', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'company_partner_id', - 'to' => 'partner_id', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'iso2-country-code', - 'vat-id', - 'country-id', - 'contact-company', - 'contact', - ], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Quantity', 'forms-bridge'), - 'name' => 'quantity', - 'type' => 'number', - 'required' => true, - 'default' => 1, - 'min' => 1, - ], - [ - 'label' => __('Company', 'forms-bridge'), - 'name' => 'company-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Vat ID', 'forms-bridge'), - 'name' => 'vat', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'street', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - ], - ], - ], -]; diff --git a/addons/odoo/templates/contacts.php b/addons/odoo/templates/contacts.php deleted file mode 100644 index 152e9d83..00000000 --- a/addons/odoo/templates/contacts.php +++ /dev/null @@ -1,105 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Contacts', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'res.partner', - ], - ], - 'bridge' => [ - 'endpoint' => 'res.partner', - 'custom_fields' => [ - [ - 'name' => 'is_company', - 'value' => '0', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'is_company', - 'to' => 'is_company', - 'cast' => 'boolean', - ], - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - [], - ], - 'workflow' => [ - 'iso2-country-code', - 'country-id', - 'skip-if-partner-exists', - ], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'street', - 'type' => 'text', - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip', - 'type' => 'text', - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/crm-company-leads.php b/addons/odoo/templates/crm-company-leads.php deleted file mode 100644 index a0e1f410..00000000 --- a/addons/odoo/templates/crm-company-leads.php +++ /dev/null @@ -1,231 +0,0 @@ - __('CRM Company Leads', 'forms-bridge'), - 'description' => __( - 'Leads form template. The resulting bridge will convert form submissions into leads linked to new companies.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('CRM Company Leads', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'crm.lead', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'user_id', - 'label' => __('Owner email', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the lead', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'res.users', - 'finger' => [ - 'value' => 'result[].id', - 'label' => 'result[].name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'lead_name', - 'label' => __('Lead name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web Lead', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'priority', - 'label' => __('Priority', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - 'max' => 3, - 'default' => 1, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'expected_revenue', - 'label' => __('Expected revenue', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - 'default' => 0, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tag_ids', - 'label' => __('Lead tags', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'crm.tag', - 'finger' => [ - 'value' => 'result[].id', - 'label' => 'result[].name', - ], - ], - 'is_multi' => true, - ], - ], - 'bridge' => [ - 'endpoint' => 'crm.lead', - 'mutations' => [ - [ - [ - 'from' => '?user_id', - 'to' => 'user_id', - 'cast' => 'integer', - ], - [ - 'from' => 'company_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?priority', - 'to' => 'priority', - 'cast' => 'string', - ], - [ - 'from' => '?expected_revenue', - 'to' => 'expected_revenue', - 'cast' => 'number', - ], - ], - [], - [], - [ - [ - 'from' => 'email', - 'to' => 'contact_email', - 'cast' => 'copy', - ], - [ - 'from' => 'phone', - 'to' => 'contact_phone', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'contact_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'contact_email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'contact_phone', - 'to' => 'phone', - 'cast' => 'string', - ], - ], - [ - [ - 'from' => 'lead_name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - ], - 'workflow' => [ - 'iso2-country-code', - 'vat-id', - 'country-id', - 'contact-company', - 'crm-contact', - ], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Company name', 'forms-bridge'), - 'name' => 'company_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Tax ID', 'forms-bridge'), - 'name' => 'vat', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'street', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Job position', 'forms-bridge'), - 'name' => 'function', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - ], - [ - 'label' => __('Comments', 'forms-bridge'), - 'name' => 'description', - 'type' => 'textarea', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/crm-leads.php b/addons/odoo/templates/crm-leads.php deleted file mode 100644 index db4693ab..00000000 --- a/addons/odoo/templates/crm-leads.php +++ /dev/null @@ -1,143 +0,0 @@ - __('CRM Leads', 'forms-bridge'), - 'description' => __( - 'Lead form template. The resulting bridge will convert form submissions into leads linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('CRM Leads', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'crm.lead', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'user_id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Name of the owner user of the lead', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'res.users', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'lead_name', - 'label' => __('Lead name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web Lead', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'priority', - 'label' => __('Priority', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - 'max' => 3, - 'default' => 1, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'expected_revenue', - 'label' => __('Expected revenue', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - 'default' => 0, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tag_ids', - 'label' => __('Lead tags', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'crm.tag', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - 'is_multi' => true, - ], - ], - 'bridge' => [ - 'endpoint' => 'crm.lead', - 'mutations' => [ - [ - [ - 'from' => '?user_id', - 'to' => 'user_id', - 'cast' => 'integer', - ], - [ - 'from' => 'contact_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?priority', - 'to' => 'priority', - 'cast' => 'string', - ], - [ - 'from' => '?expected_revenue', - 'to' => 'expected_revenue', - 'cast' => 'number', - ], - ], - [ - [ - 'from' => 'lead_name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - ], - 'workflow' => ['crm-contact'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - ], - [ - 'label' => __('Comments', 'forms-bridge'), - 'name' => 'description', - 'type' => 'textarea', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/crm-team-leads.php b/addons/odoo/templates/crm-team-leads.php deleted file mode 100644 index 43facef6..00000000 --- a/addons/odoo/templates/crm-team-leads.php +++ /dev/null @@ -1,144 +0,0 @@ - __('CRM Team Leads', 'forms-bridge'), - 'description' => __( - 'Team lead form template. The resulting bridge will convert form submissions into leads linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('CRM Team Leads', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'crm.lead', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'team_id', - 'label' => __('Owner team', 'forms-bridge'), - 'description' => __( - 'Name of the owner team of the lead', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'crm.team', - 'finger' => [ - 'value' => 'result[].id', - 'label' => 'result[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'lead_name', - 'label' => __('Lead name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web Lead', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'priority', - 'label' => __('Priority', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - 'max' => 3, - 'default' => 1, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'expected_revenue', - 'label' => __('Expected revenue', 'forms-bridge'), - 'type' => 'number', - 'min' => 0, - 'default' => 0, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'tag_ids', - 'label' => __('Lead tags', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'crm.tag', - 'finger' => [ - 'value' => 'result[].id', - 'label' => 'result[].name', - ], - ], - 'is_multi' => true, - ], - ], - 'bridge' => [ - 'endpoint' => 'crm.lead', - 'mutations' => [ - [ - [ - 'from' => 'team_id', - 'to' => 'team_id', - 'cast' => 'integer', - ], - [ - 'from' => 'contact_name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => '?priority', - 'to' => 'priority', - 'cast' => 'string', - ], - [ - 'from' => '?expected_revenue', - 'to' => 'expected_revenue', - 'cast' => 'number', - ], - ], - [ - [ - 'from' => 'lead_name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - ], - 'workflow' => ['crm-contact'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'contact_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - ], - [ - 'label' => __('Comments', 'forms-bridge'), - 'name' => 'description', - 'type' => 'textarea', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/helpdesk-tickets.php b/addons/odoo/templates/helpdesk-tickets.php deleted file mode 100644 index 19a7b9ce..00000000 --- a/addons/odoo/templates/helpdesk-tickets.php +++ /dev/null @@ -1,129 +0,0 @@ - __('Helpdesk ticket', 'forms-bridge'), - 'description' => __( - 'Convert form submissions to helpdesk tickets', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Helpdesk', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'helpdesk.ticket', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'team_id', - 'label' => __('Owner team', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'helpdesk.ticket.team', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'channel_id', - 'label' => __('Incoming channel', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'helpdesk.ticket.channel', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'user_id', - 'label' => __('Owner user', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'res.user', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => 'helpdesk.ticket', - 'mutations' => [ - [ - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'partner_name', - 'cast' => 'copy', - ], - [ - 'from' => 'your-email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'partner_email', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'ticket-name', - 'to' => 'name', - 'cast' => 'string', - ], - ], - ], - 'workflow' => ['contact'], - ], - 'form' => [ - 'title' => __('Helpdesk', 'forms-bridge'), - 'fields' => [ - [ - 'name' => 'your-name', - 'label' => __('Your name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'your-email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'ticket-name', - 'label' => __('Subject', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'description', - 'label' => __('Message', 'forms-bridge'), - 'type' => 'textarea', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/job-applicants.php b/addons/odoo/templates/job-applicants.php deleted file mode 100644 index 812916e5..00000000 --- a/addons/odoo/templates/job-applicants.php +++ /dev/null @@ -1,148 +0,0 @@ - __('Job position', 'forms-bridge'), - 'description' => __( - 'Job application form. The resulting bridge will convert form submissions into applications to a job from the Human Resources module', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Job position', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'hr.applicant', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'user_id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Name of the owner user of the application', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'res.users', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'job_id', - 'label' => __('Job position', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'hr.job', - 'finger' => [ - 'value' => 'result.[].id', - 'label' => 'result.[].name', - ], - ], - ], - ], - 'bridge' => [ - 'endpoint' => 'hr.applicant', - 'mutations' => [ - [ - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'partner_name', - 'cast' => 'copy', - ], - [ - 'from' => 'your-email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => 'email', - 'to' => 'email_from', - 'cast' => 'copy', - ], - [ - 'from' => 'your-phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'phone', - 'to' => 'partner_phone', - 'cast' => 'copy', - ], - [ - 'from' => 'user_id', - 'to' => 'user_id', - 'cast' => 'integer', - ], - [ - 'from' => 'job_id', - 'to' => 'job_id', - 'cast' => 'integer', - ], - ], - [], - [], - [ - [ - 'from' => 'curriculum', - 'to' => 'curriculum', - 'cast' => 'null', - ], - [ - 'from' => 'curriculum_filename', - 'to' => 'curriculum_filename', - 'cast' => 'null', - ], - ], - ], - 'workflow' => ['contact', 'candidate', 'attachments'], - ], - 'form' => [ - 'title' => 'Job position', - 'fields' => [ - [ - 'name' => 'your-name', - 'label' => __('Your name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'your-email', - 'label' => __('Your email', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'your-phone', - 'label' => __('Your phone', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'applicant_notes', - 'label' => __('Description', 'forms-bridge'), - 'type' => 'textarea', - 'required' => true, - ], - [ - 'name' => 'curriculum', - 'label' => __('Curriculum', 'forms-brirdge'), - 'type' => 'file', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/mailing-lists.php b/addons/odoo/templates/mailing-lists.php deleted file mode 100644 index 712b5323..00000000 --- a/addons/odoo/templates/mailing-lists.php +++ /dev/null @@ -1,85 +0,0 @@ - __('Mailing Lists', 'forms-bridge'), - 'description' => __( - 'Subscription form template. The resulting bridge will convert form submissions into subscriptions to mailing lists.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Mailing Lists', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'mailing.contact', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'list_ids', - 'label' => __('Mailing lists', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'mailing.list', - 'finger' => [ - 'value' => 'result[].id', - 'label' => 'result[].name', - ], - ], - 'is_multi' => true, - 'required' => true, - ], - ], - 'bridge' => [ - 'endpoint' => 'mailing.contact', - 'workflow' => ['mailing-contact'], - 'mutations' => [ - [ - [ - 'from' => 'first_name', - 'to' => 'name[0]', - 'cast' => 'copy', - ], - [ - 'from' => 'last_name', - 'to' => 'name[1]', - 'cast' => 'copy', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - ], - ], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('First name', 'forms-bridge'), - 'name' => 'first_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Last name', 'forms-bridge'), - 'name' => 'last_name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/quotations.php b/addons/odoo/templates/quotations.php deleted file mode 100644 index 68262433..00000000 --- a/addons/odoo/templates/quotations.php +++ /dev/null @@ -1,155 +0,0 @@ - __('Quotations', 'forms-bridge'), - 'description' => __( - 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Quotations', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'sale.order', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'product_id', - 'label' => __('Product', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - 'endpoint' => 'product.product', - 'finger' => [ - 'value' => 'result[].id', - 'label' => 'result[].name', - ], - ], - 'required' => true, - ], - ], - 'bridge' => [ - 'endpoint' => 'sale.order', - 'custom_fields' => [ - [ - 'name' => 'state', - 'value' => 'draft', - ], - [ - 'name' => 'order_line[0][0]', - 'value' => '0', - ], - [ - 'name' => 'order_line[0][1]', - 'value' => '0', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'your-name', - 'to' => 'name', - 'cast' => 'string', - ], - [ - 'from' => 'order_line[0][0]', - 'to' => 'order_line[0][0]', - 'cast' => 'integer', - ], - [ - 'from' => 'order_line[0][1]', - 'to' => 'order_line[0][1]', - 'cast' => 'integer', - ], - [ - 'from' => 'quantity', - 'to' => 'order_line[0][2].product_uom_qty', - 'cast' => 'integer', - ], - [ - 'from' => 'product_id', - 'to' => 'order_line[0][2].product_id', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => ['iso2-country-code', 'vat-id', 'country-id', 'contact'], - ], - 'form' => [ - 'fields' => [ - [ - 'label' => __('Quantity', 'forms-bridge'), - 'name' => 'quantity', - 'type' => 'number', - 'required' => true, - 'default' => 1, - 'min' => 1, - ], - [ - 'label' => __('Your name', 'forms-bridge'), - 'name' => 'your-name', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Vat ID', 'forms-bridge'), - 'name' => 'vat', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Your email', 'forms-bridge'), - 'name' => 'email', - 'type' => 'email', - 'required' => true, - ], - [ - 'label' => __('Your phone', 'forms-bridge'), - 'name' => 'phone', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Address', 'forms-bridge'), - 'name' => 'street', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('City', 'forms-bridge'), - 'name' => 'city', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Zip code', 'forms-bridge'), - 'name' => 'zip', - 'type' => 'text', - 'required' => true, - ], - [ - 'label' => __('Country', 'forms-bridge'), - 'name' => 'country', - 'type' => 'select', - 'options' => array_map(function ($country_code) { - global $forms_bridge_iso2_countries; - return [ - 'value' => $country_code, - 'label' => $forms_bridge_iso2_countries[$country_code], - ]; - }, array_keys($forms_bridge_iso2_countries)), - 'required' => true, - ], - ], - ], -]; diff --git a/addons/odoo/templates/woo-delivered-orders.php b/addons/odoo/templates/woo-delivered-orders.php deleted file mode 100644 index d360cfd0..00000000 --- a/addons/odoo/templates/woo-delivered-orders.php +++ /dev/null @@ -1,401 +0,0 @@ - __('Delivered Orders', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product delivered sale orders linked to new contacts. To work properly, the bridge needs that you use your Odoo\'s product internal refrence as the WooCommerce product sku values.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'sale.order', - ], - ], - 'bridge' => [ - 'endpoint' => 'sale.order', - 'custom_fields' => [ - [ - 'name' => 'state', - 'value' => 'sale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'shipping.comment', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][0]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][0]', - 'to' => 'order_line[][0]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][1]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][1]', - 'to' => 'order_line[][1]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'order_line[][2].product_uom_qty', - 'cast' => 'integer', - ], - [ - 'from' => 'order_line[][2].product_uom_qty', - 'to' => 'order_line[][2].qty_delivered', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'order_line[][2].default_code', - 'cast' => 'string', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'order_line[][2].price_unit', - 'cast' => 'number', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'partner_id', - 'to' => 'order_partner_id', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.comment', - 'to' => 'comment', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'order_line[][2].default_code', - 'to' => 'internal_refs', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'order_partner_id', - 'to' => 'partner_id', - 'cast' => 'integer', - ], - [ - 'from' => 'product_ids[]', - 'to' => 'order_line[][2].product_id', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => ['contact', 'delivery-address', 'products-by-ref'], - ], -]; diff --git a/addons/odoo/templates/woo-delivered-sync-orders.php b/addons/odoo/templates/woo-delivered-sync-orders.php deleted file mode 100644 index 06f46c86..00000000 --- a/addons/odoo/templates/woo-delivered-sync-orders.php +++ /dev/null @@ -1,408 +0,0 @@ - __('Delivered Orders + Sync', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product delivered sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Odoo by product refs.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'sale.order', - ], - ], - 'bridge' => [ - 'endpoint' => 'sale.order', - 'custom_fields' => [ - [ - 'name' => 'state', - 'value' => 'sale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'shipping.comment', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][0]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][0]', - 'to' => 'order_line[][0]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][1]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][1]', - 'to' => 'order_line[][1]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'order_line[][2].product_uom_qty', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][2].product_uom_qty', - 'to' => 'order_line[][2].qty_delivered', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'order_line[][2].default_code', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'order_line[][2].price_unit', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'partner_id', - 'to' => 'order_partner_id', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.comment', - 'to' => 'comment', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'order_line[][2].default_code', - 'to' => 'internal_refs', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'order_partner_id', - 'to' => 'partner_id', - 'cast' => 'integer', - ], - [ - 'from' => 'product_ids[]', - 'to' => 'order_line[][2].product_id', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'sync-products-by-ref', - 'contact', - 'delivery-address', - 'products-by-ref', - ], - ], -]; diff --git a/addons/odoo/templates/woo-sale-orders.php b/addons/odoo/templates/woo-sale-orders.php deleted file mode 100644 index 639dbc61..00000000 --- a/addons/odoo/templates/woo-sale-orders.php +++ /dev/null @@ -1,396 +0,0 @@ - __('Sale Orders', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. To work properly, the bridge needs that you use your Odoo\'s product internal refrence as the WooCommerce product sku values.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'sale.order', - ], - ], - 'bridge' => [ - 'endpoint' => 'sale.order', - 'custom_fields' => [ - [ - 'name' => 'state', - 'value' => 'sale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'shipping.comment', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][0]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][0]', - 'to' => 'order_line[][0]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][1]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][1]', - 'to' => 'order_line[][1]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'order_line[][2].product_uom_qty', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'order_line[][2].default_code', - 'cast' => 'string', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'order_line[][2].price_unit', - 'cast' => 'number', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'partner_id', - 'to' => 'order_partner_id', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.comment', - 'to' => 'comment', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'order_line[][2].default_code', - 'to' => 'internal_refs', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'order_partner_id', - 'to' => 'partner_id', - 'cast' => 'integer', - ], - [ - 'from' => 'product_ids[]', - 'to' => 'order_line[][2].product_id', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => ['contact', 'delivery-address', 'products-by-ref'], - ], -]; diff --git a/addons/odoo/templates/woo-sync-orders.php b/addons/odoo/templates/woo-sync-orders.php deleted file mode 100644 index b6479dc0..00000000 --- a/addons/odoo/templates/woo-sync-orders.php +++ /dev/null @@ -1,403 +0,0 @@ - __('Sale Orders + Sync', 'forms-bridge'), - 'description' => __( - 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Odoo by product refs.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => 'sale.order', - ], - ], - 'bridge' => [ - 'endpoint' => 'sale.order', - 'custom_fields' => [ - [ - 'name' => 'state', - 'value' => 'sale', - ], - ], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => 'billing.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?billing.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?billing.email', - 'to' => 'email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'customer_ip_address', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => '?customer_note', - 'to' => 'shipping.comment', - 'cast' => 'string', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][0]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][0]', - 'to' => 'order_line[][0]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].product.name', - 'to' => 'order_line[][1]', - 'cast' => 'copy', - ], - [ - 'from' => 'order_line[][1]', - 'to' => 'order_line[][1]', - 'cast' => 'integer', - ], - [ - 'from' => 'line_items[].quantity', - 'to' => 'order_line[][2].product_uom_qty', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.sku', - 'to' => 'order_line[][2].default_code', - 'cast' => 'copy', - ], - [ - 'from' => 'line_items[].product.price', - 'to' => 'order_line[][2].price_unit', - 'cast' => 'copy', - ], - ], - [ - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'partner_id', - 'to' => 'order_partner_id', - 'cast' => 'copy', - ], - [ - 'from' => '?shipping.first_name', - 'to' => 'name[0]', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.last_name', - 'to' => 'name[1]', - 'cast' => 'string', - ], - [ - 'from' => 'name', - 'to' => 'name', - 'cast' => 'concat', - ], - [ - 'from' => '?shipping.phone', - 'to' => 'phone', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_1', - 'to' => 'street', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.address_2', - 'to' => 'street2', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.city', - 'to' => 'city', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.postcode', - 'to' => 'zip', - 'cast' => 'string', - ], - [ - 'from' => '?shipping.comment', - 'to' => 'comment', - 'cast' => 'string', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'order_line[][2].default_code', - 'to' => 'internal_refs', - 'cast' => 'inherit', - ], - ], - [ - [ - 'from' => 'order_partner_id', - 'to' => 'partner_id', - 'cast' => 'integer', - ], - [ - 'from' => 'product_ids[]', - 'to' => 'order_line[][2].product_id', - 'cast' => 'integer', - ], - ], - ], - 'workflow' => [ - 'sync-products-by-ref', - 'contact', - 'delivery-address', - 'products-by-ref', - ], - ], -]; diff --git a/addons/rest/class-rest-addon.php b/addons/rest/class-rest-addon.php deleted file mode 100644 index 5582777c..00000000 --- a/addons/rest/class-rest-addon.php +++ /dev/null @@ -1,29 +0,0 @@ - $payload['Last_Name'], - ]; - - $lead_fields = [ - 'Owner', - 'Company', - 'First_Name', - 'Full_Name', - 'Email', - 'Phone', - 'Fax', - 'Mobile', - 'Website', - 'Lead_Source', - 'Lead_Status', - 'Industry', - 'No_of_Employees', - 'Annual_Revenue', - 'Street', - 'City', - 'Zip_Code', - 'State', - 'Country', - 'Description', - 'Tag', - ]; - - foreach ($lead_fields as $field) { - if (isset($payload[$field])) { - $lead[$field] = $payload[$field]; - } - } - - $response = $bridge - ->patch([ - 'name' => 'zoho-crm-create-lead', - 'scope' => 'ZohoCRM.modules.leads.CREATE', - 'endpoint' => '/crm/v7/Leads/upsert', - 'template' => null, - ]) - ->submit($lead); - - if (is_wp_error($response)) { - return $response; - } - - $code = $response['data']['data'][0]['code'] ?? null; - if ($code === 'DUPLICATE_DATA') { - return $response['data']['data'][0]['details']['duplicate_record']; - } else { - return $response['data']['data'][0]['details']; - } -} - -function forms_bridge_zoho_crm_create_contact($payload, $bridge) -{ - $contact = [ - 'Last_Name' => $payload['Last_Name'], - ]; - - $contact_fields = [ - 'Owner', - 'Lead_Source', - 'First_Name', - 'Full_Name', - 'Email', - 'Fax', - 'Mobile', - 'Phone', - 'Title', - 'Department', - 'Account_Name', - 'Mailing_Street', - 'Mailing_City', - 'Mailing_Zip', - 'Mailing_State', - 'Mailing_Country', - 'Description', - 'Tag', - ]; - - foreach ($contact_fields as $field) { - if (isset($payload[$field])) { - $contact[$field] = $payload[$field]; - } - } - - if ( - isset($payload['Account_Name']) && - is_string($payload['Account_Name']) - ) { - $account = forms_bridge_zoho_crm_create_account($payload, $bridge); - - if (is_wp_error($account)) { - return $account; - } - - $payload['Account_Name'] = ['id' => $account['id']]; - } - - $response = $bridge - ->patch([ - 'name' => 'zoho-crm-create-contact', - 'scope' => 'ZohoCRM.modules.contacts.CREATE', - 'endpoint' => '/crm/v7/Contacts/upsert', - 'template' => null, - ]) - ->submit($contact); - - if (is_wp_error($response)) { - return $response; - } - - $code = $response['data']['data'][0]['code']; - if ($code === 'DUPLICATE_DATA') { - return $response['data']['data'][0]['details']['duplicate_record']; - } else { - return $response['data']['data'][0]['details']; - } -} - -function forms_bridge_zoho_crm_create_account($payload, $bridge) -{ - $company = [ - 'Account_Name' => $payload['Account_Name'], - ]; - - $company_fields = [ - 'Billing_Street', - 'Billing_Code', - 'Billing_City', - 'Billing_State', - 'Billing_Country', - 'Phone', - 'Fax', - 'Mobile', - 'Website', - 'Owner', - 'Industry', - 'Ownership', - 'Employees', - 'Description', - 'Tag', - ]; - - foreach ($company_fields as $field) { - if (isset($payload[$field])) { - $company[$field] = $payload[$field]; - } - } - - $response = $bridge - ->patch([ - 'name' => 'zoho-crm-create-account', - 'scope' => 'ZohoCRM.modules.accounts.CREATE', - 'endpoint' => '/crm/v7/Accounts/upsert', - 'template' => null, - ]) - ->submit($company); - - if (is_wp_error($response)) { - return $response; - } - - $code = $response['data']['data'][0]['code'] ?? null; - if ($code === 'DUPLICATE_DATA') { - return $response['data']['data'][0]['details']['duplicate_record']; - } else { - return $response['data']['data'][0]['details']; - } -} diff --git a/addons/zoho/class-zoho-addon.php b/addons/zoho/class-zoho-addon.php deleted file mode 100644 index 51966f95..00000000 --- a/addons/zoho/class-zoho-addon.php +++ /dev/null @@ -1,177 +0,0 @@ - '__zoho-' . time(), - 'backend' => $backend, - 'endpoint' => '/crm/v7/users', - 'method' => 'GET', - ]); - - $backend = $bridge->backend; - if (!$backend) { - return false; - } - - $credential = $backend->credential; - if (!$credential) { - return false; - } - - $parsed = wp_parse_url($backend->base_url); - $host = $parsed['host'] ?? ''; - - if ( - !preg_match( - '/www\.zohoapis\.(\w{2,3}(\.\w{2})?)$/', - $host, - $matches - ) - ) { - return false; - } - - // $region = $matches[1]; - // if (!preg_match('/' . $region . '$/', $credential->region)) { - // return false; - // } - - $response = $bridge->submit(['type' => 'CurrentUser']); - return !is_wp_error($response); - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - $bridge_class = static::bridge_class; - $bridge = new $bridge_class([ - 'name' => '__zoho-' . time(), - 'backend' => $backend, - 'endpoint' => $endpoint, - 'method' => 'GET', - ]); - - return $bridge->submit(); - } - - /** - * Performs an introspection of the backend endpoint and returns API fields. - * - * @param string $endpoint API endpoint. - * @param string $backend Backend name. - * - * @return array List of fields and content type of the endpoint. - */ - public function get_endpoint_schema($endpoint, $backend) - { - if ( - !preg_match( - '/\/(([A-Z][a-z]+(_[A-Z][a-z])?)(?:\/upsert)?$)/', - $endpoint, - $matches - ) - ) { - return []; - } - - $module = $matches[2]; - - $bridge_class = static::bridge_class; - $bridge = new $bridge_class([ - 'name' => '__zoho-' . time(), - 'backend' => $backend, - 'endpoint' => '/crm/v7/settings/layouts', - 'method' => 'GET', - ]); - - $response = $bridge->submit(['module' => $module]); - - if (is_wp_error($response)) { - return []; - } - - $fields = []; - foreach ($response['data']['layouts'] as $layout) { - foreach ($layout['sections'] as $section) { - foreach ($section['fields'] as $field) { - $type = $field['json_type']; - if ($type === 'jsonobject') { - $type = 'object'; - } elseif ($type === 'jsonarray') { - $type = 'array'; - } elseif ($type === 'double') { - $type = 'number'; - } - - $fields[] = [ - 'name' => $field['api_name'], - 'schema' => ['type' => $type], - ]; - } - } - } - - return $fields; - } -} - -Zoho_Addon::setup(); diff --git a/addons/zoho/class-zoho-form-bridge.php b/addons/zoho/class-zoho-form-bridge.php deleted file mode 100644 index 5134abb3..00000000 --- a/addons/zoho/class-zoho-form-bridge.php +++ /dev/null @@ -1,84 +0,0 @@ -is_valid) { - return new WP_Error('invalid_bridge'); - } - - $method = $this->method; - if ($method === 'POST' || $method === 'PUT') { - $payload = wp_is_numeric_array($payload) ? $payload : [$payload]; - $payload = ['data' => $payload]; - } - - add_filter( - 'http_bridge_backend_headers', - function ($headers, $backend) { - if ($backend->name === $this->data['backend']) { - if (isset($headers['Authorization'])) { - $headers['Authorization'] = str_replace( - 'Bearer', - 'Zoho-oauthtoken', - $headers['Authorization'] - ); - } - } - - return $headers; - }, - 9, - 2 - ); - - $response = $this->backend()->$method( - $this->endpoint, - $payload, - [], - $attachments - ); - - if (is_wp_error($response)) { - $data = json_decode( - $response->get_error_data()['response']['body'], - true - ); - - $code = $data['data'][0]['code'] ?? null; - if ($code !== 'DUPLICATE_DATA') { - return $response; - } - - $response = $response->get_error_data()['response']; - $response['data'] = json_decode($response['body'], true); - } - - return $response; - } -} diff --git a/addons/zoho/hooks.php b/addons/zoho/hooks.php deleted file mode 100644 index 183893cd..00000000 --- a/addons/zoho/hooks.php +++ /dev/null @@ -1,262 +0,0 @@ - [ - [ - 'ref' => '#credential', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'schema', - 'type' => 'text', - 'value' => 'Bearer', - ], - [ - 'ref' => '#credential', - 'name' => 'oauth_url', - 'label' => __('Authorization URL', 'forms-bridge'), - 'type' => 'text', - 'value' => 'https://accounts.{region}/oauth/v2', - ], - [ - 'ref' => '#credential', - 'name' => 'region', - 'label' => __('Datacenter', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'value' => 'zoho.com', - 'label' => 'zoho.com', - ], - [ - 'value' => 'zoho.eu', - 'label' => 'zoho.eu', - ], - [ - 'value' => 'zoho.in', - 'label' => 'zoho.in', - ], - [ - 'value' => 'zoho.com.cn', - 'label' => 'zoho.com.cn', - ], - [ - 'value' => 'zoho.com.au', - 'label' => 'zoho.com.au', - ], - [ - 'value' => 'zoho.jp', - 'label' => 'zoho.jp', - ], - [ - 'label' => 'zoho.sa', - 'value' => 'zoho.sa', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_id', - 'label' => __('Client ID', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'client_secret', - 'label' => __('Client secret', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#credential', - 'name' => 'scope', - 'label' => __('Scope', 'forms-bridge'), - // 'description' => __( - // 'See the documentation for more information', - // 'forms-bridge' - // ), - 'type' => 'text', - 'value' => - 'ZohoCRM.modules.ALL,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ', - 'required' => true, - ], - [ - 'ref' => '#backend', - 'name' => 'name', - 'default' => 'Zoho API', - ], - [ - 'ref' => '#backend', - 'name' => 'base_url', - 'type' => 'select', - 'options' => [ - [ - 'label' => 'www.zohoapis.com', - 'value' => 'https://www.zohoapis.com', - ], - [ - 'label' => 'www.zohoapis.eu', - 'value' => 'https://www.zohoapis.eu', - ], - [ - 'label' => 'www.zohoapis.com.au', - 'value' => 'https://www.zohoapis.com.au', - ], - [ - 'label' => 'www.zohoapis.in', - 'value' => 'https://www.zohoapis.in', - ], - [ - 'label' => 'www.zohoapis.cn', - 'value' => 'https://www.zohoapis.cn', - ], - [ - 'label' => 'www.zohoapis.jp', - 'value' => 'https://www.zohoapis.jp', - ], - [ - 'label' => 'www.zohoapis.sa', - 'value' => 'https://www.zohoapis.sa', - ], - [ - 'label' => 'www.zohoapis.ca', - 'value' => 'https://www.zohoapis.ca', - ], - ], - 'default' => 'https://www.zohoapis.com', - 'required' => true, - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'value' => 'POST', - ], - ], - 'bridge' => [ - 'backend' => 'Zoho API', - 'endpoint' => '', - ], - 'credential' => [ - 'name' => '', - 'schema' => 'Bearer', - 'oauth_url' => 'https://accounts.{region}/oauth/v2', - 'scope' => - 'ZohoCRM.modules.ALL,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ', - 'client_id' => '', - 'client_secret' => '', - 'access_token' => '', - 'expires_at' => 0, - 'refresh_token' => '', - ], - 'backend' => [ - 'base_url' => 'https://www.zohoapis.{region}', - 'headers' => [ - [ - 'name' => 'Accept', - 'value' => 'application/json', - ], - ], - ], - ], - $defaults, - $schema - ); - }, - 10, - 3 -); - -add_filter( - 'forms_bridge_template_data', - function ($data, $template_id) { - if (strpos($template_id, 'zoho-') !== 0) { - return $data; - } - - $region = $data['credential']['region']; - $data['credential']['oauth_url'] = preg_replace( - '/{region}/', - $region, - $data['credential']['oauth_url'] - ); - unset($data['credential']['region']); - - $index = array_search( - 'Tag', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $field = &$data['bridge']['custom_fields'][$index]; - - if (!empty($field['value'])) { - $tags = array_filter( - array_map('trim', explode(',', strval($field['value']))) - ); - - for ($i = 0; $i < count($tags); $i++) { - $data['bridge']['custom_fields'][] = [ - 'name' => "Tag[{$i}].name", - 'value' => $tags[$i], - ]; - } - } - - array_splice($data['bridge']['custom_fields'], $index, 1); - } - - $index = array_search( - 'All_day', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - $data['form']['fields'] = array_filter( - $data['form']['fields'], - function ($field) { - return !in_array( - $field['name'], - [ - 'hour', - 'minute', - __('Hour', 'forms-bridge'), - __('Minute', 'forms-bridge'), - ], - true - ); - } - ); - - $index = array_search( - 'duration', - array_column($data['bridge']['custom_fields'], 'name') - ); - - if ($index !== false) { - array_splice($data['bridge']['custom_fields'], $index, 1); - } - } - - return $data; - }, - 10, - 2 -); diff --git a/addons/zoho/jobs/appointment-participant.php b/addons/zoho/jobs/appointment-participant.php deleted file mode 100644 index 3920068f..00000000 --- a/addons/zoho/jobs/appointment-participant.php +++ /dev/null @@ -1,105 +0,0 @@ - 'contact', - 'participant' => $contact['id'], - ]; - - return $payload; -} - -return [ - 'title' => __('Appointment participant', 'forms-bridge'), - 'description' => __( - 'Search for a contact or creates a new one and sets its ID as appointment participant', - 'forms-bridge' - ), - 'method' => 'forms_bridge_zoho_appointment_participant', - 'input' => [ - [ - 'name' => 'Last_Name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'First_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Full_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_Street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_City', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_Zip', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_State', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mailing_Country', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Description', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Account_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Title', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'Participants', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'type' => ['type' => 'string'], - 'participant' => ['type' => 'string'], - ], - ], - 'additionalItems' => true, - ], - ], - ], -]; diff --git a/addons/zoho/jobs/contact-account.php b/addons/zoho/jobs/contact-account.php deleted file mode 100644 index a41a252e..00000000 --- a/addons/zoho/jobs/contact-account.php +++ /dev/null @@ -1,126 +0,0 @@ - __('Contact account', 'forms-bridge'), - 'description' => __( - 'Create an account and sets its id as the Account_Name field on the payload', - 'forms-bridge' - ), - 'method' => 'forms_bridge_zoho_crm_contact_account', - 'input' => [ - [ - 'name' => 'Account_Name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'Rating', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'Billing_Street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_City', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_Code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_State', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Billing_Country', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Fax', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Website', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Owner', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - ], - 'required' => ['id'], - 'additionalProperties' => false, - ], - ], - [ - 'name' => 'Industry', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Ownership', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Employees', - 'schema' => ['type' => 'integer'], - ], - [ - 'name' => 'Description', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Tag', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => ['type' => 'string'], - ], - 'additionalProperties' => false, - 'required' => ['name'], - ], - ], - ], - ], - 'output' => [ - [ - 'name' => 'Account_Name', - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'string'], - ], - 'additionalProperties' => false, - ], - ], - ], -]; - -function forms_bridge_zoho_crm_contact_account($payload, $bridge) -{ - $account = forms_bridge_zoho_crm_create_account($payload, $bridge); - - if (is_wp_error($account)) { - return $account; - } - - $payload['Account_Name'] = ['id' => $account['id']]; - return $payload; -} diff --git a/addons/zoho/jobs/crm-meeting-participant.php b/addons/zoho/jobs/crm-meeting-participant.php deleted file mode 100644 index 1297845c..00000000 --- a/addons/zoho/jobs/crm-meeting-participant.php +++ /dev/null @@ -1,147 +0,0 @@ - 'lead', - 'participant' => $lead['id'], - ]; - - return $payload; -} - -return [ - 'title' => __('CRM meeting participant', 'forms-bridge'), - 'description' => __( - 'Search for a lead or creates a new one and sets its ID as meeting participant', - 'forms-bridge' - ), - 'method' => 'forms_bridge_crm_meeting_participant', - 'input' => [ - [ - 'name' => 'Last_Name', - 'schema' => ['type' => 'string'], - 'required' => true, - ], - [ - 'name' => 'First_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Full_Name', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Designation', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Secondary_Email', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Phone', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Mobile', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Fax', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Website', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Lead_Source', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Lead_Status', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Description', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Company', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'No_of_Employees', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Industry', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Annual_Revenue', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Street', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'City', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'State', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Zip_Code', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'Tag', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => ['type' => 'string'], - ], - 'additionalProperties' => false, - 'required' => ['name'], - ], - ], - ], - ], - 'output' => [ - [ - 'name' => 'Participants', - 'schema' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'type' => ['type' => 'string'], - 'participant' => ['type' => 'string'], - ], - ], - 'additionalItems' => true, - ], - ], - ], -]; diff --git a/addons/zoho/jobs/event-dates.php b/addons/zoho/jobs/event-dates.php deleted file mode 100644 index a7670840..00000000 --- a/addons/zoho/jobs/event-dates.php +++ /dev/null @@ -1,55 +0,0 @@ -getTimestamp(); - - $payload['Start_DateTime'] = date('c', $timestamp); - $payload['End_DateTime'] = date('c', $timestamp + 3600 * $duration); - - return $payload; -} - -return [ - 'title' => __('Meeting dates', 'forms-bridge'), - 'description' => __( - 'Sets meeting start and end time from "date" and "duration" fields', - 'forms-bridge' - ), - 'method' => 'forms_bridge_zoho_meeting_dates', - 'input' => [ - [ - 'name' => 'date', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'duration', - 'schema' => ['type' => 'number'], - ], - ], - 'output' => [ - [ - 'name' => 'Start_DateTime', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'End_DateTime', - 'schema' => ['type' => 'string'], - ], - ], -]; diff --git a/addons/zoho/templates/accounts.php b/addons/zoho/templates/accounts.php deleted file mode 100644 index b9b91c95..00000000 --- a/addons/zoho/templates/accounts.php +++ /dev/null @@ -1,119 +0,0 @@ - __('Company Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form for companies template. The resulting bridge will convert form submissions into new contacts linked to company accounts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/crm/v7/Contacts/upsert', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the account', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/crm/v7/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Contacts', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'Account_Name', - 'label' => __('Company name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Billing_Street', - 'label' => __('Address', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Billing_City', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Billing_Code', - 'label' => __('Zip code', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Billing_State', - 'label' => __('State', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Billing_Country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Title', - 'label' => __('Job position', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/crm/v7/Contacts/upsert', - 'workflow' => ['contact-account'], - ], -]; diff --git a/addons/zoho/templates/company-leads.php b/addons/zoho/templates/company-leads.php deleted file mode 100644 index 511be7c0..00000000 --- a/addons/zoho/templates/company-leads.php +++ /dev/null @@ -1,176 +0,0 @@ - __('Company Leads', 'forms-bridge'), - 'description' => __( - 'Lead form template. The resulting bridge will convert form submissions into company leads.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/crm/v7/Leads/upsert', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the deal', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/crm/v7/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Lead_Source', - 'label' => __('Lead source', 'forms-bridge'), - 'description' => __( - 'Label to identify your website sourced leads', - 'forms-bridge' - ), - 'type' => 'text', - 'default' => 'WordPress', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Lead_Status', - 'label' => __('Lead status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Not Contacted', 'forms-bridge'), - 'value' => 'Not Connected', - ], - [ - 'label' => __('Qualified', 'forms-bridge'), - 'value' => 'Qualified', - ], - [ - 'label' => __('Not qualified', 'forms-bridge'), - 'value' => 'Not Qualified', - ], - [ - 'label' => __('Pre-qualified', 'forms-bridge'), - 'value' => 'Pre-Qualified', - ], - [ - 'label' => __('Attempted to Contact', 'forms-bridge'), - 'value' => 'New Lead', - ], - [ - 'label' => __('Contact in Future', 'forms-bridge'), - 'value' => 'Connected', - ], - [ - 'label' => __('Junk Lead', 'forms-bridge'), - 'value' => 'Junk Lead', - ], - [ - 'label' => __('Lost Lead', 'forms-bridge'), - 'value' => 'Lost Lead', - ], - ], - 'default' => 'Not Contacted', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Tag', - 'label' => __('Lead tags', 'forms-bridge'), - 'description' => __( - 'Tag names separated by commas', - 'forms-bridge' - ), - 'type' => 'text', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Company Leads', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'Company', - 'label' => __('Company', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Street', - 'label' => __('Street', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Zip_Code', - 'label' => __('Postal code', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'City', - 'label' => __('City', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'State', - 'label' => __('State', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Country', - 'label' => __('Country', 'forms-bridge'), - 'type' => 'text', - ], - - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Title', - 'label' => __('Job position', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/crm/v7/Leads/upsert', - ], -]; diff --git a/addons/zoho/templates/contacts.php b/addons/zoho/templates/contacts.php deleted file mode 100644 index 164273c1..00000000 --- a/addons/zoho/templates/contacts.php +++ /dev/null @@ -1,78 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert form submissions into contacts.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/crm/v7/Contacts/upsert', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the account', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/crm/v7/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Contacts', 'forms-bridge'), - ], - ], - 'form' => [ - 'title' => 'Contacts', - 'fields' => [ - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/crm/v7/Contacts/upsert', - ], -]; diff --git a/addons/zoho/templates/leads.php b/addons/zoho/templates/leads.php deleted file mode 100644 index 25c1abea..00000000 --- a/addons/zoho/templates/leads.php +++ /dev/null @@ -1,139 +0,0 @@ - __('Leads', 'forms-bridge'), - 'description' => __( - 'Lead form template. The resulting bridge will convert form submissions into leads.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/crm/v7/Leads/upsert', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the deal', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/crm/v7/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Lead_Source', - 'label' => __('Lead source', 'forms-bridge'), - 'description' => __( - 'Label to identify your website sourced leads', - 'forms-bridge' - ), - 'type' => 'text', - 'default' => 'WordPress', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Lead_Status', - 'label' => __('Lead status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Not Contacted', 'forms-bridge'), - 'value' => 'Not Connected', - ], - [ - 'label' => __('Qualified', 'forms-bridge'), - 'value' => 'Qualified', - ], - [ - 'label' => __('Not qualified', 'forms-bridge'), - 'value' => 'Not Qualified', - ], - [ - 'label' => __('Pre-qualified', 'forms-bridge'), - 'value' => 'Pre-Qualified', - ], - [ - 'label' => __('Attempted to Contact', 'forms-bridge'), - 'value' => 'New Lead', - ], - [ - 'label' => __('Contact in Future', 'forms-bridge'), - 'value' => 'Connected', - ], - [ - 'label' => __('Junk Lead', 'forms-bridge'), - 'value' => 'Junk Lead', - ], - [ - 'label' => __('Lost Lead', 'forms-bridge'), - 'value' => 'Lost Lead', - ], - ], - 'default' => 'Not Contacted', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Tag', - 'label' => __('Lead tags', 'forms-bridge'), - 'description' => __( - 'Tag names separated by commas', - 'forms-bridge' - ), - 'type' => 'text', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Leads', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/crm/v7/Leads/upsert', - ], -]; diff --git a/addons/zoho/templates/meetings.php b/addons/zoho/templates/meetings.php deleted file mode 100644 index e40734eb..00000000 --- a/addons/zoho/templates/meetings.php +++ /dev/null @@ -1,307 +0,0 @@ - __('Meetings', 'forms-bridge'), - 'description' => __( - 'Meetings form template. The resulting bridge will convert form submissions into events on the calendar linked to new leads.', - 'forms-bridge' - ), - 'fields' => [ - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/crm/v7/Events', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the deal', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/crm/v7/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - 'required' => true, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Event_Title', - 'label' => __('Event title', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => __('Web Meetting', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Lead_Source', - 'label' => __('Lead source', 'forms-bridge'), - 'description' => __( - 'Label to identify your website sourced leads', - 'forms-bridge' - ), - 'type' => 'text', - 'required' => true, - 'default' => 'WordPress', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Lead_Status', - 'label' => __('Lead status', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('Not Contacted', 'forms-bridge'), - 'value' => 'Not Connected', - ], - [ - 'label' => __('Qualified', 'forms-bridge'), - 'value' => 'Qualified', - ], - [ - 'label' => __('Not qualified', 'forms-bridge'), - 'value' => 'Not Qualified', - ], - [ - 'label' => __('Pre-qualified', 'forms-bridge'), - 'value' => 'Pre-Qualified', - ], - [ - 'label' => __('Attempted to Contact', 'forms-bridge'), - 'value' => 'New Lead', - ], - [ - 'label' => __('Contact in Future', 'forms-bridge'), - 'value' => 'Connected', - ], - [ - 'label' => __('Junk Lead', 'forms-bridge'), - 'value' => 'Junk Lead', - ], - [ - 'label' => __('Lost Lead', 'forms-bridge'), - 'value' => 'Lost Lead', - ], - ], - 'required' => true, - 'default' => 'Not Contacted', - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'All_day', - 'label' => __('Is all day event?', 'forms-bridge'), - 'type' => 'boolean', - 'default' => false, - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'duration', - 'label' => __('Meeting duration', 'forms-bridge'), - 'type' => 'number', - 'default' => 1, - 'min' => 0, - 'max' => 24, - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'default' => __('Meetings', 'forms-bridge'), - ], - ], - 'form' => [ - 'fields' => [ - [ - 'name' => 'First_Name', - 'label' => __('First name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Last_Name', - 'label' => __('Last name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'Email', - 'label' => __('Email', 'forms-bridge'), - 'type' => 'email', - 'required' => true, - ], - [ - 'name' => 'Phone', - 'label' => __('Phone', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'name' => 'date', - 'label' => __('Date', 'forms-bridge'), - 'type' => 'date', - 'required' => true, - ], - [ - 'name' => 'hour', - 'label' => __('Hour', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - [ - 'label' => __('1 AM', 'forms-bridge'), - 'value' => '01', - ], - [ - 'label' => __('2 AM', 'forms-bridge'), - 'value' => '02', - ], - [ - 'label' => __('3 AM', 'forms-bridge'), - 'value' => '03', - ], - [ - 'label' => __('4 AM', 'forms-bridge'), - 'value' => '04', - ], - [ - 'label' => __('5 AM', 'forms-bridge'), - 'value' => '05', - ], - [ - 'label' => __('6 AM', 'forms-bridge'), - 'value' => '06', - ], - [ - 'label' => __('7 AM', 'forms-bridge'), - 'value' => '07', - ], - [ - 'label' => __('8 AM', 'forms-bridge'), - 'value' => '08', - ], - [ - 'label' => __('9 AM', 'forms-bridge'), - 'value' => '09', - ], - [ - 'label' => __('10 AM', 'forms-bridge'), - 'value' => '10', - ], - [ - 'label' => __('11 AM', 'forms-bridge'), - 'value' => '11', - ], - [ - 'label' => __('12 AM', 'forms-bridge'), - 'value' => '12', - ], - [ - 'label' => __('1 PM', 'forms-bridge'), - 'value' => '13', - ], - [ - 'label' => __('2 PM', 'forms-bridge'), - 'value' => '14', - ], - [ - 'label' => __('3 PM', 'forms-bridge'), - 'value' => '15', - ], - [ - 'label' => __('4 PM', 'forms-bridge'), - 'value' => '16', - ], - [ - 'label' => __('5 PM', 'forms-bridge'), - 'value' => '17', - ], - [ - 'label' => __('6 PM', 'forms-bridge'), - 'value' => '18', - ], - [ - 'label' => __('7 PM', 'forms-bridge'), - 'value' => '19', - ], - [ - 'label' => __('8 PM', 'forms-bridge'), - 'value' => '20', - ], - [ - 'label' => __('9 PM', 'forms-bridge'), - 'value' => '21', - ], - [ - 'label' => __('10 PM', 'forms-bridge'), - 'value' => '22', - ], - [ - 'label' => __('11 PM', 'forms-bridge'), - 'value' => '23', - ], - [ - 'label' => __('12 PM', 'forms-bridge'), - 'value' => '24', - ], - ], - 'required' => true, - ], - [ - 'name' => 'minute', - 'label' => __('Minute', 'forms-bridge'), - 'type' => 'select', - 'options' => [ - ['label' => '00', 'value' => '00.0'], - ['label' => '05', 'value' => '05'], - ['label' => '10', 'value' => '10'], - ['label' => '15', 'value' => '15'], - ['label' => '20', 'value' => '20'], - ['label' => '25', 'value' => '25'], - ['label' => '30', 'value' => '30'], - ['label' => '35', 'value' => '35'], - ['label' => '40', 'value' => '40'], - ['label' => '45', 'value' => '45'], - ['label' => '50', 'value' => '50'], - ['label' => '55', 'value' => '55'], - ], - 'required' => true, - ], - [ - 'name' => 'Description', - 'label' => __('Comments', 'forms-bridge'), - 'type' => 'textarea', - ], - ], - ], - 'bridge' => [ - 'endpoint' => '/crm/v7/Events', - 'workflow' => [ - 'date-fields-to-date', - 'event-dates', - 'crm-meeting-participant', - ], - 'mutations' => [ - [ - [ - 'from' => 'All_day', - 'to' => 'All_day', - 'cast' => 'boolean', - ], - ], - [ - [ - 'from' => 'datetime', - 'to' => 'date', - 'cast' => 'string', - ], - ], - ], - ], -]; diff --git a/addons/zoho/templates/woo-contacts.php b/addons/zoho/templates/woo-contacts.php deleted file mode 100644 index e19912be..00000000 --- a/addons/zoho/templates/woo-contacts.php +++ /dev/null @@ -1,348 +0,0 @@ - __('Contacts', 'forms-bridge'), - 'description' => __( - 'Contact form template. The resulting bridge will convert woocommerce customers into contacts.', - 'forms-bridge' - ), - 'integrations' => ['woo'], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'title', - 'value' => __('Woo Checkout', 'forms-bridge'), - ], - [ - 'ref' => '#bridge/custom_fields[]', - 'name' => 'Owner.id', - 'label' => __('Owner', 'forms-bridge'), - 'description' => __( - 'Email of the owner user of the account', - 'forms-bridge' - ), - 'type' => 'select', - 'options' => [ - 'endpoint' => '/crm/v7/users', - 'finger' => [ - 'value' => 'users[].id', - 'label' => 'users[].full_name', - ], - ], - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'value' => '/crm/v7/Contacts/upsert', - ], - ], - 'bridge' => [ - 'endpoint' => '/crm/v7/Contacts/upsert', - 'workflow' => ['contact-account'], - 'mutations' => [ - [ - [ - 'from' => 'id', - 'to' => 'id', - 'cast' => 'null', - ], - [ - 'from' => 'parent_id', - 'to' => 'parent_id', - 'cast' => 'null', - ], - [ - 'from' => 'status', - 'to' => 'status', - 'cast' => 'null', - ], - [ - 'from' => 'version', - 'to' => 'version', - 'cast' => 'null', - ], - [ - 'from' => 'prices_include_tax', - 'to' => 'prices_include_tax', - 'cast' => 'null', - ], - [ - 'from' => 'date_created', - 'to' => 'date_created', - 'cast' => 'null', - ], - [ - 'from' => 'date_modified', - 'to' => 'date_modified', - 'cast' => 'null', - ], - [ - 'from' => 'discount_total', - 'to' => 'discount_total', - 'cast' => 'null', - ], - [ - 'from' => 'discount_tax', - 'to' => 'discount_tax', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_total', - 'to' => 'shipping_total', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_tax', - 'to' => 'shipping_tax', - 'cast' => 'null', - ], - [ - 'from' => 'cart_total', - 'to' => 'cart_total', - 'cast' => 'null', - ], - [ - 'from' => 'cart_tax', - 'to' => 'cart_tax', - 'cast' => 'null', - ], - [ - 'from' => 'total', - 'to' => 'total', - 'cast' => 'null', - ], - [ - 'from' => 'total_tax', - 'to' => 'total_tax', - 'cast' => 'null', - ], - [ - 'from' => 'customer_id', - 'to' => 'customer_id', - 'cast' => 'null', - ], - [ - 'from' => 'order_key', - 'to' => 'order_key', - 'cast' => 'null', - ], - [ - 'from' => '?Owner', - 'to' => 'Contact_Owner', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.company', - 'to' => 'Account_Name', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'Billing_Street', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.city', - 'to' => 'Billing_City', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.state', - 'to' => 'Billing_State', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.country', - 'to' => 'Billing_Country', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'Billing_Code', - 'cast' => 'copy', - ], - [ - 'from' => '?billing.first_name', - 'to' => 'First_Name', - 'cast' => 'string', - ], - [ - 'from' => 'billing.last_name', - 'to' => 'Last_Name', - 'cast' => 'string', - ], - [ - 'from' => 'billing.email', - 'to' => 'Email', - 'cast' => 'string', - ], - [ - 'from' => '?billing.phone', - 'to' => 'Phone', - 'cast' => 'string', - ], - [ - 'from' => '?billing.address_1', - 'to' => 'Mailing_Street', - 'cast' => 'string', - ], - [ - 'from' => '?billing.city', - 'to' => 'Mailing_City', - 'cast' => 'string', - ], - [ - 'from' => '?billing.state', - 'to' => 'Mailing_State', - 'cast' => 'string', - ], - [ - 'from' => '?billing.country', - 'to' => 'Mailing_Country', - 'cast' => 'string', - ], - [ - 'from' => '?billing.postcode', - 'to' => 'Mailing_Zip', - 'cast' => 'string', - ], - [ - 'from' => 'billing', - 'to' => 'billing', - 'cast' => 'null', - ], - [ - 'from' => 'shipping', - 'to' => 'shipping', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method', - 'to' => 'payment_method', - 'cast' => 'null', - ], - [ - 'from' => 'payment_method_title', - 'to' => 'payment_method_title', - 'cast' => 'null', - ], - [ - 'from' => 'transaction_id', - 'to' => 'transaction_id', - 'cast' => 'null', - ], - [ - 'from' => 'customer_ip_address', - 'to' => 'ip_signup', - 'cast' => 'null', - ], - [ - 'from' => 'customer_user_agent', - 'to' => 'customer_user_agent', - 'cast' => 'null', - ], - [ - 'from' => 'created_via', - 'to' => 'created_via', - 'cast' => 'null', - ], - [ - 'from' => 'customer_note', - 'to' => 'notes', - 'cast' => 'null', - ], - [ - 'from' => 'date_completed', - 'to' => 'date_completed', - 'cast' => 'null', - ], - [ - 'from' => 'date_paid', - 'to' => 'date_paid', - 'cast' => 'null', - ], - [ - 'from' => 'cart_hash', - 'to' => 'cart_hash', - 'cast' => 'null', - ], - [ - 'from' => 'order_stock_reduced', - 'to' => 'order_stock_reduced', - 'cast' => 'null', - ], - [ - 'from' => 'download_permissions_granted', - 'to' => 'download_permissions_granted', - 'cast' => 'null', - ], - [ - 'from' => 'new_order_email_sent', - 'to' => 'new_order_email_sent', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_sales', - 'to' => 'recorded_sales', - 'cast' => 'null', - ], - [ - 'from' => 'recorded_coupon_usage_counts', - 'to' => 'recorded_coupon_usage_counts', - 'cast' => 'null', - ], - [ - 'from' => 'number', - 'to' => 'number', - 'cast' => 'null', - ], - [ - 'from' => 'tax_lines', - 'to' => 'tax_lines', - 'cast' => 'null', - ], - [ - 'from' => 'shipping_lines', - 'to' => 'shipping_lines', - 'cast' => 'null', - ], - [ - 'from' => 'fee_lines', - 'to' => 'fee_lines', - 'cast' => 'null', - ], - [ - 'from' => 'coupon_lines', - 'to' => 'coupon_lines', - 'cast' => 'null', - ], - [ - 'from' => 'line_items', - 'to' => 'line_items', - 'cast' => 'null', - ], - [ - 'from' => 'currency', - 'to' => 'currency', - 'cast' => 'null', - ], - ], - [ - [ - 'from' => 'Contact_Owner', - 'to' => 'Owner', - 'cast' => 'inherit', - ], - ], - ], - ], -]; diff --git a/assets/logo.png b/assets/logo.png deleted file mode 100644 index 4e0bd762..00000000 Binary files a/assets/logo.png and /dev/null differ diff --git a/assets/rest.png b/assets/rest.png deleted file mode 100644 index c4a15347..00000000 Binary files a/assets/rest.png and /dev/null differ diff --git a/bin/download-test-deps.sh b/bin/download-test-deps.sh new file mode 100644 index 00000000..61f477fc --- /dev/null +++ b/bin/download-test-deps.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +TMPDIR=${TMPDIR-/tmp} +if [ ! -d "$TMPDIR" ]; then + mkdir -p "$TMPDIR" +fi + +WORDPRESS_DIR="$TMPDIR/wordpress" +if [ ! -d "$WORDPRESS_DIR" ]; then + echo 'Distination path is not a directory' + exit 1 +else + mkdir -p "$WORDPRESS_DIR/wp-content/mu-plugins" +fi + +URLS=('https://downloads.wordpress.org/plugin/contact-form-7.6.1.3.zip' + 'https://downloads.wordpress.org/plugin/ninja-forms.3.13.0.zip' + 'https://www.codeccoop.org/formsbridge/plugins/wpforms.zip' + 'https://www.codeccoop.org/formsbridge/plugins/gravityforms.zip' + 'https://downloads.wordpress.org/plugin/woocommerce.10.3.4.zip') + +PLUGINS=('contact-form-7' 'gravityforms' 'ninja-forms' 'wpforms', 'woocommerce') + +COUNT=${#PLUGINS[@]} + +i=0 +while [ $i -lt $COUNT ]; do + URL=${URLS[$i]} + PLUGIN=${PLUGINS[$i]} + + ZIP="$TMPDIR/$PLUGIN.zip" + if [ ! -f "$ZIP" ]; then + curl -sL --connect-timeout 5 --max-time 30 "$URL" >"$ZIP" + + if [ $? -gt 0 ]; then + test -f "$ZIP" && rm -rf "$ZIP" + echo "Download of $PLUGIN has failed" + exit 1 + fi + fi + + unzip -oqq "$ZIP" -d "$WORDPRESS_DIR/wp-content/mu-plugins" + + i=$((i + 1)) +done diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh index ee05775c..f6b23631 100755 --- a/bin/install-wp-tests.sh +++ b/bin/install-wp-tests.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash -if [ $# -lt 3 ]; then +if [ $# == 1 ] && [ $1 == '--help' ]; then echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" exit 1 fi -DB_NAME=$1 -DB_USER=$2 -DB_PASS=$3 -DB_HOST=${4-localhost} +DB_NAME=${1-wpdb} +DB_USER=${2-wpusr} +DB_PASS=${3-wppwd} +DB_HOST=${4-127.0.0.1} WP_VERSION=${5-latest} SKIP_DB_CREATE=${6-false} @@ -18,11 +18,11 @@ WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress} download() { - if [ `which curl` ]; then - curl -s "$1" > "$2"; - elif [ `which wget` ]; then - wget -nv -O "$2" "$1" - fi + if [ $(which curl) ]; then + curl -s "$1" >"$2" + elif [ $(which wget) ]; then + wget -nv -O "$2" "$1" + fi } if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then @@ -56,7 +56,7 @@ set -ex install_wp() { if [ -d $WP_CORE_DIR ]; then - return; + return fi mkdir -p $WP_CORE_DIR @@ -77,7 +77,7 @@ install_wp() { LATEST_VERSION=${WP_VERSION%??} else # otherwise, scan the releases and get the most up to date minor version of the major release - local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + local VERSION_ESCAPED=$(echo $WP_VERSION | sed 's/\./\\./g') LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) fi if [[ -z "$LATEST_VERSION" ]]; then @@ -88,7 +88,7 @@ install_wp() { else local ARCHIVE_NAME="wordpress-$WP_VERSION" fi - download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR fi @@ -128,9 +128,8 @@ install_test_suite() { recreate_db() { shopt -s nocasematch - if [[ $1 =~ ^(y|yes)$ ]] - then - mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA + if [[ $1 =~ ^(y|yes)$ ]]; then + mariadb-admin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA create_db echo "Recreated the database ($DB_NAME)." else @@ -140,7 +139,7 @@ recreate_db() { } create_db() { - mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA + mariadb-admin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA } install_db() { @@ -151,25 +150,24 @@ install_db() { # parse DB_HOST for port or socket references local PARTS=(${DB_HOST//\:/ }) - local DB_HOSTNAME=${PARTS[0]}; - local DB_SOCK_OR_PORT=${PARTS[1]}; + local DB_HOSTNAME=${PARTS[0]} + local DB_SOCK_OR_PORT=${PARTS[1]} local EXTRA="" - if ! [ -z $DB_HOSTNAME ] ; then + if ! [ -z $DB_HOSTNAME ]; then if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" - elif ! [ -z $DB_SOCK_OR_PORT ] ; then + elif ! [ -z $DB_SOCK_OR_PORT ]; then EXTRA=" --socket=$DB_SOCK_OR_PORT" - elif ! [ -z $DB_HOSTNAME ] ; then + elif ! [ -z $DB_HOSTNAME ]; then EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" fi fi # create database - if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ] - then + if [ $(mariadb --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ]; then echo "Reinstalling will delete the existing test database ($DB_NAME)" - read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB + # read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB recreate_db $DELETE_EXISTING_DB else create_db diff --git a/bin/test-on-docker.sh b/bin/test-on-docker.sh new file mode 100644 index 00000000..84f2e0f8 --- /dev/null +++ b/bin/test-on-docker.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +docker run --rm \ + -v .:/forms-bridge \ + -v /tmp:/tmp \ + -w /forms-bridge \ + --add-host=host.docker.internal:host-gateway \ + --name forms-bridge-tests \ + codeccoop/wp-test \ + sh -c " +nohup docker-entrypoint.sh mariadbd >/dev/null 2>&1 & +echo -n 'Install composer dependencies: ' +composer -q install +echo '✅' +echo -n 'Wait for mariadb to start for three seconds: ' +sleep 5 +echo '✅' +echo -n 'Install wordpress test suite: ' +TMPDIR=/opt bin/install-wp-tests.sh >/dev/null 2>&1 +echo '✅' +echo -n 'Download test dependencies: ' +bash bin/download-test-deps.sh +echo '✅' + +echo 'Run tests! 🚀' +echo + +vendor/bin/phpunit +" diff --git a/common b/common deleted file mode 160000 index 81b3b95d..00000000 --- a/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 81b3b95d038255a877e95eb1af597684207e14a9 diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..a752b45d --- /dev/null +++ b/composer.json @@ -0,0 +1,20 @@ +{ + "name": "codeccoop/forms-bridge", + "scripts": { + "test": "vendor/bin/phpunit -c phpunit.xml.dist", + "lint": "vendor/bin/phpcs", + "format": "vendor/bin/phpcbf" + }, + "require-dev": { + "yoast/phpunit-polyfills": "^4.0", + "phpunit/phpunit": "^9.0", + "squizlabs/php_codesniffer": "^3.13", + "wp-coding-standards/wpcs": "^3.0", + "phpcompatibility/phpcompatibility-wp": "*" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..f397fa16 --- /dev/null +++ b/composer.lock @@ -0,0 +1,2513 @@ +{ + "_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": "5241c59da0bad77a7ab9cc15d08915f5", + "packages": [], + "packages-dev": [ + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", + "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.2", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "^2.2", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-07-17T20:45:56+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.6.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + }, + "time": "2025-10-21T19:32:17+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "paragonie", + "phpcs", + "polyfill", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-09-19T17:43:28+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.8", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-10-18T00:05:59+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "882b8c947ada27eb002870fe77fee9ce0a454cdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/882b8c947ada27eb002870fe77fee9ce0a454cdb", + "reference": "882b8c947ada27eb002870fe77fee9ce0a454cdb", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.1.2", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-09-05T06:54:52+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", + "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.13.3 || ^4.0" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "phpcs4", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-10-16T16:39:32+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.32", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:23:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.5.0 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.9", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:29:11+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-08-10T06:51:50+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:03:27+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:10:35+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-10T06:57:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.13.4", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ad545ea9c1b7d270ce0fc9cbfb884161cd706119", + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-09-05T05:47:09+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + }, + { + "name": "wp-coding-standards/wpcs", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "d2421de7cec3274ae622c22c744de9a62c7925af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/d2421de7cec3274ae622c22c744de9a62c7925af", + "reference": "d2421de7cec3274ae622c22c744de9a62c7925af", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", + "php": ">=5.4", + "phpcsstandards/phpcsextra": "^1.4.0", + "phpcsstandards/phpcsutils": "^1.1.0", + "squizlabs/php_codesniffer": "^3.13.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "suggest": { + "ext-iconv": "For improved results", + "ext-mbstring": "For improved results" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", + "source": "https://github.com/WordPress/WordPress-Coding-Standards", + "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/php_codesniffer", + "type": "custom" + } + ], + "time": "2025-07-24T20:08:31+00:00" + }, + { + "name": "yoast/phpunit-polyfills", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", + "reference": "134921bfca9b02d8f374c48381451da1d98402f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/134921bfca9b02d8f374c48381451da1d98402f9", + "reference": "134921bfca9b02d8f374c48381451da1d98402f9", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0 || ^11.0 || ^12.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.x-dev" + } + }, + "autoload": { + "files": [ + "phpunitpolyfills-autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Team Yoast", + "email": "support@yoast.com", + "homepage": "https://yoast.com" + }, + { + "name": "Contributors", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" + } + ], + "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", + "keywords": [ + "phpunit", + "polyfill", + "testing" + ], + "support": { + "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", + "source": "https://github.com/Yoast/PHPUnit-Polyfills" + }, + "time": "2025-02-09T18:58:54+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/data/country-phone-codes.php b/data/country-phone-codes.php deleted file mode 100644 index d7b93158..00000000 --- a/data/country-phone-codes.php +++ /dev/null @@ -1,240 +0,0 @@ - __('United States', 'forms-bridge'), - '7' => __('Russia', 'forms-bridge'), - '20' => __('Egypt', 'forms-bridge'), - '27' => __('South Africa', 'forms-bridge'), - '30' => __('Greece', 'forms-bridge'), - '31' => __('Netherlands', 'forms-bridge'), - '32' => __('Belgium', 'forms-bridge'), - '33' => __('France', 'forms-bridge'), - '34' => __('Spain', 'forms-bridge'), - '36' => __('Hungary', 'forms-bridge'), - '39' => __('Italy', 'forms-bridge'), - '40' => __('Romania', 'forms-bridge'), - '41' => __('Switzerland', 'forms-bridge'), - '43' => __('Austria', 'forms-bridge'), - '44' => __('United Kingdom', 'forms-bridge'), - '45' => __('Denmark', 'forms-bridge'), - '46' => __('Sweden', 'forms-bridge'), - '47' => __('Svalbard and Jan Mayen', 'forms-bridge'), - '48' => __('Poland', 'forms-bridge'), - '49' => __('Germany', 'forms-bridge'), - '51' => __('Peru', 'forms-bridge'), - '52' => __('Mexico', 'forms-bridge'), - '53' => __('Cuba', 'forms-bridge'), - '54' => __('Argentina', 'forms-bridge'), - '55' => __('Brazil', 'forms-bridge'), - '56' => __('Chile', 'forms-bridge'), - '57' => __('Colombia', 'forms-bridge'), - '58' => __('Venezuela', 'forms-bridge'), - '60' => __('Malaysia', 'forms-bridge'), - '61' => __('Cocos Islands', 'forms-bridge'), - '62' => __('Indonesia', 'forms-bridge'), - '63' => __('Philippines', 'forms-bridge'), - '64' => __('Pitcairn', 'forms-bridge'), - '65' => __('Singapore', 'forms-bridge'), - '66' => __('Thailand', 'forms-bridge'), - '81' => __('Japan', 'forms-bridge'), - '82' => __('South Korea', 'forms-bridge'), - '84' => __('Vietnam', 'forms-bridge'), - '86' => __('China', 'forms-bridge'), - '90' => __('Turkey', 'forms-bridge'), - '91' => __('India', 'forms-bridge'), - '92' => __('Pakistan', 'forms-bridge'), - '93' => __('Afghanistan', 'forms-bridge'), - '94' => __('Sri Lanka', 'forms-bridge'), - '95' => __('Myanmar', 'forms-bridge'), - '98' => __('Iran', 'forms-bridge'), - '211' => __('South Sudan', 'forms-bridge'), - '212' => __('Western Sahara', 'forms-bridge'), - '213' => __('Algeria', 'forms-bridge'), - '216' => __('Tunisia', 'forms-bridge'), - '218' => __('Libya', 'forms-bridge'), - '220' => __('Gambia', 'forms-bridge'), - '221' => __('Senegal', 'forms-bridge'), - '222' => __('Mauritania', 'forms-bridge'), - '223' => __('Mali', 'forms-bridge'), - '224' => __('Guinea', 'forms-bridge'), - '225' => __('Ivory Coast', 'forms-bridge'), - '226' => __('Burkina Faso', 'forms-bridge'), - '227' => __('Niger', 'forms-bridge'), - '228' => __('Togo', 'forms-bridge'), - '229' => __('Benin', 'forms-bridge'), - '230' => __('Mauritius', 'forms-bridge'), - '231' => __('Liberia', 'forms-bridge'), - '232' => __('Sierra Leone', 'forms-bridge'), - '233' => __('Ghana', 'forms-bridge'), - '234' => __('Nigeria', 'forms-bridge'), - '235' => __('Chad', 'forms-bridge'), - '236' => __('Central African Republic', 'forms-bridge'), - '237' => __('Cameroon', 'forms-bridge'), - '238' => __('Cape Verde', 'forms-bridge'), - '239' => __('Sao Tome and Principe', 'forms-bridge'), - '240' => __('Equatorial Guinea', 'forms-bridge'), - '241' => __('Gabon', 'forms-bridge'), - '242' => __('Republic of the Congo', 'forms-bridge'), - '243' => __('Democratic Republic of the Congo', 'forms-bridge'), - '244' => __('Angola', 'forms-bridge'), - '245' => __('Guinea-Bissau', 'forms-bridge'), - '246' => __('British Indian Ocean Territory', 'forms-bridge'), - '248' => __('Seychelles', 'forms-bridge'), - '249' => __('Sudan', 'forms-bridge'), - '250' => __('Rwanda', 'forms-bridge'), - '251' => __('Ethiopia', 'forms-bridge'), - '252' => __('Somalia', 'forms-bridge'), - '253' => __('Djibouti', 'forms-bridge'), - '254' => __('Kenya', 'forms-bridge'), - '255' => __('Tanzania', 'forms-bridge'), - '256' => __('Uganda', 'forms-bridge'), - '257' => __('Burundi', 'forms-bridge'), - '258' => __('Mozambique', 'forms-bridge'), - '260' => __('Zambia', 'forms-bridge'), - '261' => __('Madagascar', 'forms-bridge'), - '262' => __('Reunion', 'forms-bridge'), - '263' => __('Zimbabwe', 'forms-bridge'), - '264' => __('Namibia', 'forms-bridge'), - '265' => __('Malawi', 'forms-bridge'), - '266' => __('Lesotho', 'forms-bridge'), - '267' => __('Botswana', 'forms-bridge'), - '268' => __('Swaziland', 'forms-bridge'), - '269' => __('Comoros', 'forms-bridge'), - '290' => __('Saint Helena', 'forms-bridge'), - '291' => __('Eritrea', 'forms-bridge'), - '297' => __('Aruba', 'forms-bridge'), - '298' => __('Faroe Islands', 'forms-bridge'), - '299' => __('Greenland', 'forms-bridge'), - '350' => __('Gibraltar', 'forms-bridge'), - '351' => __('Portugal', 'forms-bridge'), - '352' => __('Luxembourg', 'forms-bridge'), - '353' => __('Ireland', 'forms-bridge'), - '354' => __('Iceland', 'forms-bridge'), - '355' => __('Albania', 'forms-bridge'), - '356' => __('Malta', 'forms-bridge'), - '357' => __('Cyprus', 'forms-bridge'), - '358' => __('Finland', 'forms-bridge'), - '359' => __('Bulgaria', 'forms-bridge'), - '370' => __('Lithuania', 'forms-bridge'), - '371' => __('Latvia', 'forms-bridge'), - '372' => __('Estonia', 'forms-bridge'), - '373' => __('Moldova', 'forms-bridge'), - '374' => __('Armenia', 'forms-bridge'), - '375' => __('Belarus', 'forms-bridge'), - '376' => __('Andorra', 'forms-bridge'), - '377' => __('Monaco', 'forms-bridge'), - '378' => __('San Marino', 'forms-bridge'), - '379' => __('Vatican', 'forms-bridge'), - '380' => __('Ukraine', 'forms-bridge'), - '381' => __('Serbia', 'forms-bridge'), - '382' => __('Montenegro', 'forms-bridge'), - '383' => __('Kosovo', 'forms-bridge'), - '385' => __('Croatia', 'forms-bridge'), - '386' => __('Slovenia', 'forms-bridge'), - '387' => __('Bosnia and Herzegovina', 'forms-bridge'), - '389' => __('Macedonia', 'forms-bridge'), - '420' => __('Czech Republic', 'forms-bridge'), - '421' => __('Slovakia', 'forms-bridge'), - '423' => __('Liechtenstein', 'forms-bridge'), - '500' => __('Falkland Islands', 'forms-bridge'), - '501' => __('Belize', 'forms-bridge'), - '502' => __('Guatemala', 'forms-bridge'), - '503' => __('El Salvador', 'forms-bridge'), - '504' => __('Honduras', 'forms-bridge'), - '505' => __('Nicaragua', 'forms-bridge'), - '506' => __('Costa Rica', 'forms-bridge'), - '507' => __('Panama', 'forms-bridge'), - '508' => __('Saint Pierre and Miquelon', 'forms-bridge'), - '509' => __('Haiti', 'forms-bridge'), - '590' => __('Saint Martin', 'forms-bridge'), - '591' => __('Bolivia', 'forms-bridge'), - '592' => __('Guyana', 'forms-bridge'), - '593' => __('Ecuador', 'forms-bridge'), - '595' => __('Paraguay', 'forms-bridge'), - '597' => __('Suriname', 'forms-bridge'), - '598' => __('Uruguay', 'forms-bridge'), - '599' => __('Netherlands Antilles', 'forms-bridge'), - '670' => __('East Timor', 'forms-bridge'), - '672' => __('Antarctica', 'forms-bridge'), - '673' => __('Brunei', 'forms-bridge'), - '674' => __('Nauru', 'forms-bridge'), - '675' => __('Papua New Guinea', 'forms-bridge'), - '676' => __('Tonga', 'forms-bridge'), - '677' => __('Solomon Islands', 'forms-bridge'), - '678' => __('Vanuatu', 'forms-bridge'), - '679' => __('Fiji', 'forms-bridge'), - '680' => __('Palau', 'forms-bridge'), - '681' => __('Wallis and Futuna', 'forms-bridge'), - '682' => __('Cook Islands', 'forms-bridge'), - '683' => __('Niue', 'forms-bridge'), - '685' => __('Samoa', 'forms-bridge'), - '686' => __('Kiribati', 'forms-bridge'), - '687' => __('New Caledonia', 'forms-bridge'), - '688' => __('Tuvalu', 'forms-bridge'), - '689' => __('French Polynesia', 'forms-bridge'), - '690' => __('Tokelau', 'forms-bridge'), - '691' => __('Micronesia', 'forms-bridge'), - '692' => __('Marshall Islands', 'forms-bridge'), - '850' => __('North Korea', 'forms-bridge'), - '852' => __('Hong Kong', 'forms-bridge'), - '853' => __('Macau', 'forms-bridge'), - '855' => __('Cambodia', 'forms-bridge'), - '856' => __('Laos', 'forms-bridge'), - '880' => __('Bangladesh', 'forms-bridge'), - '886' => __('Taiwan', 'forms-bridge'), - '960' => __('Maldives', 'forms-bridge'), - '961' => __('Lebanon', 'forms-bridge'), - '962' => __('Jordan', 'forms-bridge'), - '963' => __('Syria', 'forms-bridge'), - '964' => __('Iraq', 'forms-bridge'), - '965' => __('Kuwait', 'forms-bridge'), - '966' => __('Saudi Arabia', 'forms-bridge'), - '967' => __('Yemen', 'forms-bridge'), - '968' => __('Oman', 'forms-bridge'), - '970' => __('Palestine', 'forms-bridge'), - '971' => __('United Arab Emirates', 'forms-bridge'), - '972' => __('Israel', 'forms-bridge'), - '973' => __('Bahrain', 'forms-bridge'), - '974' => __('Qatar', 'forms-bridge'), - '975' => __('Bhutan', 'forms-bridge'), - '976' => __('Mongolia', 'forms-bridge'), - '977' => __('Nepal', 'forms-bridge'), - '992' => __('Tajikistan', 'forms-bridge'), - '993' => __('Turkmenistan', 'forms-bridge'), - '994' => __('Azerbaijan', 'forms-bridge'), - '995' => __('Georgia', 'forms-bridge'), - '996' => __('Kyrgyzstan', 'forms-bridge'), - '998' => __('Uzbekistan', 'forms-bridge'), - '1-684' => __('American Samoa', 'forms-bridge'), - '1-264' => __('Anguilla', 'forms-bridge'), - '1-268' => __('Antigua and Barbuda', 'forms-bridge'), - '1-242' => __('Bahamas', 'forms-bridge'), - '1-246' => __('Barbados', 'forms-bridge'), - '1-441' => __('Bermuda', 'forms-bridge'), - '1-284' => __('British Virgin Islands', 'forms-bridge'), - '1-345' => __('Cayman Islands', 'forms-bridge'), - '1-767' => __('Dominica', 'forms-bridge'), - '1-809, 1-829, 1-849' => __('Dominican Republic', 'forms-bridge'), - '1-473' => __('Grenada', 'forms-bridge'), - '1-671' => __('Guam', 'forms-bridge'), - '44-1481' => __('Guernsey', 'forms-bridge'), - '44-1624' => __('Isle of Man', 'forms-bridge'), - '1-876' => __('Jamaica', 'forms-bridge'), - '44-1534' => __('Jersey', 'forms-bridge'), - '1-664' => __('Montserrat', 'forms-bridge'), - '1-670' => __('Northern Mariana Islands', 'forms-bridge'), - '1-787, 1-939' => __('Puerto Rico', 'forms-bridge'), - '1-869' => __('Saint Kitts and Nevis', 'forms-bridge'), - '1-758' => __('Saint Lucia', 'forms-bridge'), - '1-784' => __('Saint Vincent and the Grenadines', 'forms-bridge'), - '1-721' => __('Sint Maarten', 'forms-bridge'), - '1-868' => __('Trinidad and Tobago', 'forms-bridge'), - '1-649' => __('Turks and Caicos Islands', 'forms-bridge'), - '1-340' => __('U.S. Virgin Islands', 'forms-bridge'), -]; diff --git a/data/iso2-countries.php b/data/iso2-countries.php deleted file mode 100644 index 4ead4414..00000000 --- a/data/iso2-countries.php +++ /dev/null @@ -1,259 +0,0 @@ - __('Andorra', 'forms-bridge'), - 'AE' => __('United Arab Emirates', 'forms-bridge'), - 'AF' => __('Afghanistan', 'forms-bridge'), - 'AG' => __('Antigua and Barbuda', 'forms-bridge'), - 'AI' => __('Anguilla', 'forms-bridge'), - 'AL' => __('Albania', 'forms-bridge'), - 'AM' => __('Armenia', 'forms-bridge'), - 'AO' => __('Angola', 'forms-bridge'), - 'AQ' => __('Antarctica', 'forms-bridge'), - 'AR' => __('Argentina', 'forms-bridge'), - 'AS' => __('American Samoa', 'forms-bridge'), - 'AT' => __('Austria', 'forms-bridge'), - 'AU' => __('Australia', 'forms-bridge'), - 'AW' => __('Aruba', 'forms-bridge'), - 'AX' => __('Åland Islands', 'forms-bridge'), - 'AZ' => __('Azerbaijan', 'forms-bridge'), - 'BA' => __('Bosnia and Herzegovina', 'forms-bridge'), - 'BB' => __('Barbados', 'forms-bridge'), - 'BD' => __('Bangladesh', 'forms-bridge'), - 'BE' => __('Belgium', 'forms-bridge'), - 'BF' => __('Burkina Faso', 'forms-bridge'), - 'BG' => __('Bulgaria', 'forms-bridge'), - 'BH' => __('Bahrain', 'forms-bridge'), - 'BI' => __('Burundi', 'forms-bridge'), - 'BJ' => __('Benin', 'forms-bridge'), - 'BL' => __('Saint Barthélémy', 'forms-bridge'), - 'BM' => __('Bermuda', 'forms-bridge'), - 'BN' => __('Brunei Darussalam', 'forms-bridge'), - 'BO' => __('Bolivia', 'forms-bridge'), - 'BQ' => __('Bonaire, Sint Eustatius and Saba', 'forms-bridge'), - 'BR' => __('Brazil', 'forms-bridge'), - 'BS' => __('Bahamas', 'forms-bridge'), - 'BT' => __('Bhutan', 'forms-bridge'), - 'BV' => __('Bouvet Island', 'forms-bridge'), - 'BW' => __('Botswana', 'forms-bridge'), - 'BY' => __('Belarus', 'forms-bridge'), - 'BZ' => __('Belize', 'forms-bridge'), - 'CA' => __('Canada', 'forms-bridge'), - 'CC' => __('Cocos (Keeling) Islands', 'forms-bridge'), - 'CF' => __('Central African Republic', 'forms-bridge'), - 'CD' => __('Democratic Republic of the Congo', 'forms-bridge'), - 'CG' => __('Congo', 'forms-bridge'), - 'CH' => __('Switzerland', 'forms-bridge'), - 'CI' => __('Côte d\'Ivoire', 'forms-bridge'), - 'CK' => __('Cook Islands', 'forms-bridge'), - 'CL' => __('Chile', 'forms-bridge'), - 'CM' => __('Cameroon', 'forms-bridge'), - 'CN' => __('China', 'forms-bridge'), - 'CO' => __('Colombia', 'forms-bridge'), - 'CR' => __('Costa Rica', 'forms-bridge'), - 'CU' => __('Cuba', 'forms-bridge'), - 'CV' => __('Cape Verde', 'forms-bridge'), - 'CW' => __('Curaçao', 'forms-bridge'), - 'CX' => __('Christmas Island', 'forms-bridge'), - 'CY' => __('Cyprus', 'forms-bridge'), - 'CZ' => __('Czech Republic', 'forms-bridge'), - 'DE' => __('Germany', 'forms-bridge'), - 'DJ' => __('Djibouti', 'forms-bridge'), - 'DK' => __('Denmark', 'forms-bridge'), - 'DM' => __('Dominica', 'forms-bridge'), - 'DO' => __('Dominican Republic', 'forms-bridge'), - 'DZ' => __('Algeria', 'forms-bridge'), - 'EC' => __('Ecuador', 'forms-bridge'), - 'EE' => __('Estonia', 'forms-bridge'), - 'EG' => __('Egypt', 'forms-bridge'), - 'EH' => __('Western Sahara', 'forms-bridge'), - 'ER' => __('Eritrea', 'forms-bridge'), - 'ES' => __('Spain', 'forms-bridge'), - 'ET' => __('Ethiopia', 'forms-bridge'), - 'FI' => __('Finland', 'forms-bridge'), - 'FJ' => __('Fiji', 'forms-bridge'), - 'FK' => __('Falkland Islands', 'forms-bridge'), - 'FM' => __('Micronesia', 'forms-bridge'), - 'FO' => __('Faroe Islands', 'forms-bridge'), - 'FR' => __('France', 'forms-bridge'), - 'GA' => __('Gabon', 'forms-bridge'), - 'GD' => __('Grenada', 'forms-bridge'), - 'GE' => __('Georgia', 'forms-bridge'), - 'GF' => __('French Guiana', 'forms-bridge'), - 'GH' => __('Ghana', 'forms-bridge'), - 'GI' => __('Gibraltar', 'forms-bridge'), - 'GG' => __('Guernsey', 'forms-bridge'), - 'GL' => __('Greenland', 'forms-bridge'), - 'GM' => __('Gambia', 'forms-bridge'), - 'GN' => __('Guinea', 'forms-bridge'), - 'GP' => __('Guadeloupe', 'forms-bridge'), - 'GQ' => __('Equatorial Guinea', 'forms-bridge'), - 'GR' => __('Greece', 'forms-bridge'), - 'GS' => __('South Georgia and the South Sandwich Islands', 'forms-bridge'), - 'GT' => __('Guatemala', 'forms-bridge'), - 'GU' => __('Guam', 'forms-bridge'), - 'GW' => __('Guinea-Bissau', 'forms-bridge'), - 'GY' => __('Guyana', 'forms-bridge'), - 'HK' => __('Hong Kong', 'forms-bridge'), - 'HM' => __('Heard Island and McDonald Islands', 'forms-bridge'), - 'HN' => __('Honduras', 'forms-bridge'), - 'HR' => __('Croatia', 'forms-bridge'), - 'HT' => __('Haiti', 'forms-bridge'), - 'HU' => __('Hungary', 'forms-bridge'), - 'ID' => __('Indonesia', 'forms-bridge'), - 'IE' => __('Ireland', 'forms-bridge'), - 'IL' => __('Israel', 'forms-bridge'), - 'IM' => __('Isle of Man', 'forms-bridge'), - 'IN' => __('India', 'forms-bridge'), - 'IO' => __('British Indian Ocean Territory', 'forms-bridge'), - 'IQ' => __('Iraq', 'forms-bridge'), - 'IR' => __('Iran', 'forms-bridge'), - 'IS' => __('Iceland', 'forms-bridge'), - 'IT' => __('Italy', 'forms-bridge'), - 'JE' => __('Jersey', 'forms-bridge'), - 'JM' => __('Jamaica', 'forms-bridge'), - 'JO' => __('Jordan', 'forms-bridge'), - 'JP' => __('Japan', 'forms-bridge'), - 'KE' => __('Kenya', 'forms-bridge'), - 'KG' => __('Kyrgyzstan', 'forms-bridge'), - 'KH' => __('Cambodia', 'forms-bridge'), - 'KI' => __('Kiribati', 'forms-bridge'), - 'KM' => __('Comoros', 'forms-bridge'), - 'KN' => __('Saint Kitts and Nevis', 'forms-bridge'), - 'KP' => __('North Korea', 'forms-bridge'), - 'KR' => __('South Korea', 'forms-bridge'), - 'KW' => __('Kuwait', 'forms-bridge'), - 'KY' => __('Cayman Islands', 'forms-bridge'), - 'KZ' => __('Kazakhstan', 'forms-bridge'), - 'LA' => __('Laos', 'forms-bridge'), - 'LB' => __('Lebanon', 'forms-bridge'), - 'LC' => __('Saint Lucia', 'forms-bridge'), - 'LI' => __('Liechtenstein', 'forms-bridge'), - 'LK' => __('Sri Lanka', 'forms-bridge'), - 'LR' => __('Liberia', 'forms-bridge'), - 'LS' => __('Lesotho', 'forms-bridge'), - 'LT' => __('Lithuania', 'forms-bridge'), - 'LU' => __('Luxembourg', 'forms-bridge'), - 'LV' => __('Latvia', 'forms-bridge'), - 'LY' => __('Libya', 'forms-bridge'), - 'MA' => __('Morocco', 'forms-bridge'), - 'MC' => __('Monaco', 'forms-bridge'), - 'MD' => __('Moldova', 'forms-bridge'), - 'ME' => __('Montenegro', 'forms-bridge'), - 'MF' => __('Saint Martin (French part)', 'forms-bridge'), - 'MG' => __('Madagascar', 'forms-bridge'), - 'MH' => __('Marshall Islands', 'forms-bridge'), - 'MK' => __('Macedonia, the former Yugoslav Republic of', 'forms-bridge'), - 'ML' => __('Mali', 'forms-bridge'), - 'MM' => __('Myanmar', 'forms-bridge'), - 'MN' => __('Mongolia', 'forms-bridge'), - 'MO' => __('Macau', 'forms-bridge'), - 'MP' => __('Northern Mariana Islands', 'forms-bridge'), - 'MQ' => __('Martinique', 'forms-bridge'), - 'MR' => __('Mauritania', 'forms-bridge'), - 'MS' => __('Montserrat', 'forms-bridge'), - 'MT' => __('Malta', 'forms-bridge'), - 'MU' => __('Mauritius', 'forms-bridge'), - 'MV' => __('Maldives', 'forms-bridge'), - 'MW' => __('Malawi', 'forms-bridge'), - 'MX' => __('Mexico', 'forms-bridge'), - 'MY' => __('Malaysia', 'forms-bridge'), - 'MZ' => __('Mozambique', 'forms-bridge'), - 'NA' => __('Namibia', 'forms-bridge'), - 'NC' => __('New Caledonia', 'forms-bridge'), - 'NE' => __('Niger', 'forms-bridge'), - 'NF' => __('Norfolk Island', 'forms-bridge'), - 'NG' => __('Nigeria', 'forms-bridge'), - 'NI' => __('Nicaragua', 'forms-bridge'), - 'NL' => __('Netherlands', 'forms-bridge'), - 'NO' => __('Norway', 'forms-bridge'), - 'NP' => __('Nepal', 'forms-bridge'), - 'NR' => __('Nauru', 'forms-bridge'), - 'NU' => __('Niue', 'forms-bridge'), - 'NZ' => __('New Zealand', 'forms-bridge'), - 'OM' => __('Oman', 'forms-bridge'), - 'PA' => __('Panama', 'forms-bridge'), - 'PE' => __('Peru', 'forms-bridge'), - 'PF' => __('French Polynesia', 'forms-bridge'), - 'PG' => __('Papua New Guinea', 'forms-bridge'), - 'PH' => __('Philippines', 'forms-bridge'), - 'PK' => __('Pakistan', 'forms-bridge'), - 'PL' => __('Poland', 'forms-bridge'), - 'PM' => __('Saint Pierre and Miquelon', 'forms-bridge'), - 'PN' => __('Pitcairn Islands', 'forms-bridge'), - 'PR' => __('Puerto Rico', 'forms-bridge'), - 'PS' => __('State of Palestine', 'forms-bridge'), - 'PT' => __('Portugal', 'forms-bridge'), - 'PW' => __('Palau', 'forms-bridge'), - 'PY' => __('Paraguay', 'forms-bridge'), - 'QA' => __('Qatar', 'forms-bridge'), - 'RE' => __('Réunion', 'forms-bridge'), - 'RO' => __('Romania', 'forms-bridge'), - 'RS' => __('Serbia', 'forms-bridge'), - 'RU' => __('Russian Federation', 'forms-bridge'), - 'RW' => __('Rwanda', 'forms-bridge'), - 'SA' => __('Saudi Arabia', 'forms-bridge'), - 'SB' => __('Solomon Islands', 'forms-bridge'), - 'SC' => __('Seychelles', 'forms-bridge'), - 'SD' => __('Sudan', 'forms-bridge'), - 'SE' => __('Sweden', 'forms-bridge'), - 'SG' => __('Singapore', 'forms-bridge'), - 'SH' => __('Saint Helena, Ascension and Tristan da Cunha', 'forms-bridge'), - 'SI' => __('Slovenia', 'forms-bridge'), - 'SJ' => __('Svalbard and Jan Mayen', 'forms-bridge'), - 'SK' => __('Slovakia', 'forms-bridge'), - 'SL' => __('Sierra Leone', 'forms-bridge'), - 'SM' => __('San Marino', 'forms-bridge'), - 'SN' => __('Senegal', 'forms-bridge'), - 'SO' => __('Somalia', 'forms-bridge'), - 'SR' => __('Suriname', 'forms-bridge'), - 'SS' => __('South Sudan', 'forms-bridge'), - 'ST' => __('São Tomé and Príncipe', 'forms-bridge'), - 'SV' => __('El Salvador', 'forms-bridge'), - 'SX' => __('Sint Maarten (Dutch part)', 'forms-bridge'), - 'SY' => __('Syria', 'forms-bridge'), - 'SZ' => __('Swaziland', 'forms-bridge'), - 'TC' => __('Turks and Caicos Islands', 'forms-bridge'), - 'TD' => __('Chad', 'forms-bridge'), - 'TF' => __('French Southern Territories', 'forms-bridge'), - 'TG' => __('Togo', 'forms-bridge'), - 'TH' => __('Thailand', 'forms-bridge'), - 'TJ' => __('Tajikistan', 'forms-bridge'), - 'TK' => __('Tokelau', 'forms-bridge'), - 'TM' => __('Turkmenistan', 'forms-bridge'), - 'TN' => __('Tunisia', 'forms-bridge'), - 'TO' => __('Tonga', 'forms-bridge'), - 'TL' => __('Timor-Leste', 'forms-bridge'), - 'TR' => __('Turkey', 'forms-bridge'), - 'TT' => __('Trinidad and Tobago', 'forms-bridge'), - 'TV' => __('Tuvalu', 'forms-bridge'), - 'TW' => __('Taiwan', 'forms-bridge'), - 'TZ' => __('Tanzania', 'forms-bridge'), - 'UA' => __('Ukraine', 'forms-bridge'), - 'UG' => __('Uganda', 'forms-bridge'), - 'GB' => __('United Kingdom', 'forms-bridge'), - 'UM' => __('USA Minor Outlying Islands', 'forms-bridge'), - 'US' => __('United States', 'forms-bridge'), - 'UY' => __('Uruguay', 'forms-bridge'), - 'UZ' => __('Uzbekistan', 'forms-bridge'), - 'VA' => __('Holy See (Vatican City State)', 'forms-bridge'), - 'VC' => __('Saint Vincent and the Grenadines', 'forms-bridge'), - 'VE' => __('Venezuela', 'forms-bridge'), - 'VG' => __('Virgin Islands (British)', 'forms-bridge'), - 'VI' => __('Virgin Islands (USA)', 'forms-bridge'), - 'VN' => __('Vietnam', 'forms-bridge'), - 'VU' => __('Vanuatu', 'forms-bridge'), - 'WF' => __('Wallis and Futuna', 'forms-bridge'), - 'WS' => __('Samoa', 'forms-bridge'), - 'YE' => __('Yemen', 'forms-bridge'), - 'YT' => __('Mayotte', 'forms-bridge'), - 'ZA' => __('South Africa', 'forms-bridge'), - 'ZM' => __('Zambia', 'forms-bridge'), - 'ZW' => __('Zimbabwe', 'forms-bridge'), - 'XK' => __('Kosovo', 'forms-bridge'), -]; diff --git a/data/iso3-countries.php b/data/iso3-countries.php deleted file mode 100644 index 22d14adc..00000000 --- a/data/iso3-countries.php +++ /dev/null @@ -1,249 +0,0 @@ - __('Afghanistan', 'forms-bridge'), - 'ALB' => __('Albania', 'forms-bridge'), - 'DZA' => __('Algeria', 'forms-bridge'), - 'ASM' => __('American Samoa', 'forms-bridge'), - 'AND' => __('Andorra', 'forms-bridge'), - 'AGO' => __('Angola', 'forms-bridge'), - 'AIA' => __('Anguilla', 'forms-bridge'), - 'ATA' => __('Antarctica', 'forms-bridge'), - 'ATG' => __('Antigua and Barbuda', 'forms-bridge'), - 'ARG' => __('Argentina', 'forms-bridge'), - 'ARM' => __('Armenia', 'forms-bridge'), - 'ABW' => __('Aruba', 'forms-bridge'), - 'AUS' => __('Australia', 'forms-bridge'), - 'AUT' => __('Austria', 'forms-bridge'), - 'AZE' => __('Azerbaijan', 'forms-bridge'), - 'BHS' => __('Bahamas', 'forms-bridge'), - 'BHR' => __('Bahrain', 'forms-bridge'), - 'BGD' => __('Bangladesh', 'forms-bridge'), - 'BRB' => __('Barbados', 'forms-bridge'), - 'BLR' => __('Belarus', 'forms-bridge'), - 'BEL' => __('Belgium', 'forms-bridge'), - 'BLZ' => __('Belize', 'forms-bridge'), - 'BEN' => __('Benin', 'forms-bridge'), - 'BMU' => __('Bermuda', 'forms-bridge'), - 'BTN' => __('Bhutan', 'forms-bridge'), - 'BOL' => __('Bolivia', 'forms-bridge'), - 'BIH' => __('Bosnia and Herzegovina', 'forms-bridge'), - 'BWA' => __('Botswana', 'forms-bridge'), - 'BRA' => __('Brazil', 'forms-bridge'), - 'IOT' => __('British Indian Ocean Territory', 'forms-bridge'), - 'VGB' => __('British Virgin Islands', 'forms-bridge'), - 'BRN' => __('Brunei', 'forms-bridge'), - 'BGR' => __('Bulgaria', 'forms-bridge'), - 'BFA' => __('Burkina Faso', 'forms-bridge'), - 'BDI' => __('Burundi', 'forms-bridge'), - 'KHM' => __('Cambodia', 'forms-bridge'), - 'CMR' => __('Cameroon', 'forms-bridge'), - 'CAN' => __('Canada', 'forms-bridge'), - 'CPV' => __('Cape Verde', 'forms-bridge'), - 'CYM' => __('Cayman Islands', 'forms-bridge'), - 'CAF' => __('Central African Republic', 'forms-bridge'), - 'TCD' => __('Chad', 'forms-bridge'), - 'CHL' => __('Chile', 'forms-bridge'), - 'CHN' => __('China', 'forms-bridge'), - 'CXR' => __('Christmas Island', 'forms-bridge'), - 'CCK' => __('Cocos Islands', 'forms-bridge'), - 'COL' => __('Colombia', 'forms-bridge'), - 'COM' => __('Comoros', 'forms-bridge'), - 'COK' => __('Cook Islands', 'forms-bridge'), - 'CRI' => __('Costa Rica', 'forms-bridge'), - 'HRV' => __('Croatia', 'forms-bridge'), - 'CUB' => __('Cuba', 'forms-bridge'), - 'CUW' => __('Curacao', 'forms-bridge'), - 'CYP' => __('Cyprus', 'forms-bridge'), - 'CZE' => __('Czech Republic', 'forms-bridge'), - 'COD' => __('Democratic Republic of the Congo', 'forms-bridge'), - 'DNK' => __('Denmark', 'forms-bridge'), - 'DJI' => __('Djibouti', 'forms-bridge'), - 'DMA' => __('Dominica', 'forms-bridge'), - 'DOM' => __('Dominican Republic', 'forms-bridge'), - 'TLS' => __('East Timor', 'forms-bridge'), - 'ECU' => __('Ecuador', 'forms-bridge'), - 'EGY' => __('Egypt', 'forms-bridge'), - 'SLV' => __('El Salvador', 'forms-bridge'), - 'GNQ' => __('Equatorial Guinea', 'forms-bridge'), - 'ERI' => __('Eritrea', 'forms-bridge'), - 'EST' => __('Estonia', 'forms-bridge'), - 'ETH' => __('Ethiopia', 'forms-bridge'), - 'FLK' => __('Falkland Islands', 'forms-bridge'), - 'FRO' => __('Faroe Islands', 'forms-bridge'), - 'FJI' => __('Fiji', 'forms-bridge'), - 'FIN' => __('Finland', 'forms-bridge'), - 'FRA' => __('France', 'forms-bridge'), - 'PYF' => __('French Polynesia', 'forms-bridge'), - 'GAB' => __('Gabon', 'forms-bridge'), - 'GMB' => __('Gambia', 'forms-bridge'), - 'GEO' => __('Georgia', 'forms-bridge'), - 'DEU' => __('Germany', 'forms-bridge'), - 'GHA' => __('Ghana', 'forms-bridge'), - 'GIB' => __('Gibraltar', 'forms-bridge'), - 'GRC' => __('Greece', 'forms-bridge'), - 'GRL' => __('Greenland', 'forms-bridge'), - 'GRD' => __('Grenada', 'forms-bridge'), - 'GUM' => __('Guam', 'forms-bridge'), - 'GTM' => __('Guatemala', 'forms-bridge'), - 'GGY' => __('Guernsey', 'forms-bridge'), - 'GIN' => __('Guinea', 'forms-bridge'), - 'GNB' => __('Guinea-Bissau', 'forms-bridge'), - 'GUY' => __('Guyana', 'forms-bridge'), - 'HTI' => __('Haiti', 'forms-bridge'), - 'HND' => __('Honduras', 'forms-bridge'), - 'HKG' => __('Hong Kong', 'forms-bridge'), - 'HUN' => __('Hungary', 'forms-bridge'), - 'ISL' => __('Iceland', 'forms-bridge'), - 'IND' => __('India', 'forms-bridge'), - 'IDN' => __('Indonesia', 'forms-bridge'), - 'IRN' => __('Iran', 'forms-bridge'), - 'IRQ' => __('Iraq', 'forms-bridge'), - 'IRL' => __('Ireland', 'forms-bridge'), - 'IMN' => __('Isle of Man', 'forms-bridge'), - 'ISR' => __('Israel', 'forms-bridge'), - 'ITA' => __('Italy', 'forms-bridge'), - 'CIV' => __('Ivory Coast', 'forms-bridge'), - 'JAM' => __('Jamaica', 'forms-bridge'), - 'JPN' => __('Japan', 'forms-bridge'), - 'JEY' => __('Jersey', 'forms-bridge'), - 'JOR' => __('Jordan', 'forms-bridge'), - 'KAZ' => __('Kazakhstan', 'forms-bridge'), - 'KEN' => __('Kenya', 'forms-bridge'), - 'KIR' => __('Kiribati', 'forms-bridge'), - 'XKX' => __('Kosovo', 'forms-bridge'), - 'KWT' => __('Kuwait', 'forms-bridge'), - 'KGZ' => __('Kyrgyzstan', 'forms-bridge'), - 'LAO' => __('Laos', 'forms-bridge'), - 'LVA' => __('Latvia', 'forms-bridge'), - 'LBN' => __('Lebanon', 'forms-bridge'), - 'LSO' => __('Lesotho', 'forms-bridge'), - 'LBR' => __('Liberia', 'forms-bridge'), - 'LBY' => __('Libya', 'forms-bridge'), - 'LIE' => __('Liechtenstein', 'forms-bridge'), - 'LTU' => __('Lithuania', 'forms-bridge'), - 'LUX' => __('Luxembourg', 'forms-bridge'), - 'MAC' => __('Macau', 'forms-bridge'), - 'MKD' => __('Macedonia', 'forms-bridge'), - 'MDG' => __('Madagascar', 'forms-bridge'), - 'MWI' => __('Malawi', 'forms-bridge'), - 'MYS' => __('Malaysia', 'forms-bridge'), - 'MDV' => __('Maldives', 'forms-bridge'), - 'MLI' => __('Mali', 'forms-bridge'), - 'MLT' => __('Malta', 'forms-bridge'), - 'MHL' => __('Marshall Islands', 'forms-bridge'), - 'MRT' => __('Mauritania', 'forms-bridge'), - 'MUS' => __('Mauritius', 'forms-bridge'), - 'MYT' => __('Mayotte', 'forms-bridge'), - 'MEX' => __('Mexico', 'forms-bridge'), - 'FSM' => __('Micronesia', 'forms-bridge'), - 'MDA' => __('Moldova', 'forms-bridge'), - 'MCO' => __('Monaco', 'forms-bridge'), - 'MNG' => __('Mongolia', 'forms-bridge'), - 'MNE' => __('Montenegro', 'forms-bridge'), - 'MSR' => __('Montserrat', 'forms-bridge'), - 'MAR' => __('Morocco', 'forms-bridge'), - 'MOZ' => __('Mozambique', 'forms-bridge'), - 'MMR' => __('Myanmar', 'forms-bridge'), - 'NAM' => __('Namibia', 'forms-bridge'), - 'NRU' => __('Nauru', 'forms-bridge'), - 'NPL' => __('Nepal', 'forms-bridge'), - 'NLD' => __('Netherlands', 'forms-bridge'), - 'ANT' => __('Netherlands Antilles', 'forms-bridge'), - 'NCL' => __('New Caledonia', 'forms-bridge'), - 'NZL' => __('New Zealand', 'forms-bridge'), - 'NIC' => __('Nicaragua', 'forms-bridge'), - 'NER' => __('Niger', 'forms-bridge'), - 'NGA' => __('Nigeria', 'forms-bridge'), - 'NIU' => __('Niue', 'forms-bridge'), - 'PRK' => __('North Korea', 'forms-bridge'), - 'MNP' => __('Northern Mariana Islands', 'forms-bridge'), - 'NOR' => __('Norway', 'forms-bridge'), - 'OMN' => __('Oman', 'forms-bridge'), - 'PAK' => __('Pakistan', 'forms-bridge'), - 'PLW' => __('Palau', 'forms-bridge'), - 'PSE' => __('Palestine', 'forms-bridge'), - 'PAN' => __('Panama', 'forms-bridge'), - 'PNG' => __('Papua New Guinea', 'forms-bridge'), - 'PRY' => __('Paraguay', 'forms-bridge'), - 'PER' => __('Peru', 'forms-bridge'), - 'PHL' => __('Philippines', 'forms-bridge'), - 'PCN' => __('Pitcairn', 'forms-bridge'), - 'POL' => __('Poland', 'forms-bridge'), - 'PRT' => __('Portugal', 'forms-bridge'), - 'PRI' => __('Puerto Rico', 'forms-bridge'), - 'QAT' => __('Qatar', 'forms-bridge'), - 'COG' => __('Republic of the Congo', 'forms-bridge'), - 'REU' => __('Reunion', 'forms-bridge'), - 'ROU' => __('Romania', 'forms-bridge'), - 'RUS' => __('Russia', 'forms-bridge'), - 'RWA' => __('Rwanda', 'forms-bridge'), - 'BLM' => __('Saint Barthelemy', 'forms-bridge'), - 'SHN' => __('Saint Helena', 'forms-bridge'), - 'KNA' => __('Saint Kitts and Nevis', 'forms-bridge'), - 'LCA' => __('Saint Lucia', 'forms-bridge'), - 'MAF' => __('Saint Martin', 'forms-bridge'), - 'SPM' => __('Saint Pierre and Miquelon', 'forms-bridge'), - 'VCT' => __('Saint Vincent and the Grenadines', 'forms-bridge'), - 'WSM' => __('Samoa', 'forms-bridge'), - 'SMR' => __('San Marino', 'forms-bridge'), - 'STP' => __('Sao Tome and Principe', 'forms-bridge'), - 'SAU' => __('Saudi Arabia', 'forms-bridge'), - 'SEN' => __('Senegal', 'forms-bridge'), - 'SRB' => __('Serbia', 'forms-bridge'), - 'SYC' => __('Seychelles', 'forms-bridge'), - 'SLE' => __('Sierra Leone', 'forms-bridge'), - 'SGP' => __('Singapore', 'forms-bridge'), - 'SXM' => __('Sint Maarten', 'forms-bridge'), - 'SVK' => __('Slovakia', 'forms-bridge'), - 'SVN' => __('Slovenia', 'forms-bridge'), - 'SLB' => __('Solomon Islands', 'forms-bridge'), - 'SOM' => __('Somalia', 'forms-bridge'), - 'ZAF' => __('South Africa', 'forms-bridge'), - 'KOR' => __('South Korea', 'forms-bridge'), - 'SSD' => __('South Sudan', 'forms-bridge'), - 'ESP' => __('Spain', 'forms-bridge'), - 'LKA' => __('Sri Lanka', 'forms-bridge'), - 'SDN' => __('Sudan', 'forms-bridge'), - 'SUR' => __('Suriname', 'forms-bridge'), - 'SJM' => __('Svalbard and Jan Mayen', 'forms-bridge'), - 'SWZ' => __('Swaziland', 'forms-bridge'), - 'SWE' => __('Sweden', 'forms-bridge'), - 'CHE' => __('Switzerland', 'forms-bridge'), - 'SYR' => __('Syria', 'forms-bridge'), - 'TWN' => __('Taiwan', 'forms-bridge'), - 'TJK' => __('Tajikistan', 'forms-bridge'), - 'TZA' => __('Tanzania', 'forms-bridge'), - 'THA' => __('Thailand', 'forms-bridge'), - 'TGO' => __('Togo', 'forms-bridge'), - 'TKL' => __('Tokelau', 'forms-bridge'), - 'TON' => __('Tonga', 'forms-bridge'), - 'TTO' => __('Trinidad and Tobago', 'forms-bridge'), - 'TUN' => __('Tunisia', 'forms-bridge'), - 'TUR' => __('Turkey', 'forms-bridge'), - 'TKM' => __('Turkmenistan', 'forms-bridge'), - 'TCA' => __('Turks and Caicos Islands', 'forms-bridge'), - 'TUV' => __('Tuvalu', 'forms-bridge'), - 'VIR' => __('U.S. Virgin Islands', 'forms-bridge'), - 'UGA' => __('Uganda', 'forms-bridge'), - 'UKR' => __('Ukraine', 'forms-bridge'), - 'ARE' => __('United Arab Emirates', 'forms-bridge'), - 'GBR' => __('United Kingdom', 'forms-bridge'), - 'USA' => __('United States', 'forms-bridge'), - 'URY' => __('Uruguay', 'forms-bridge'), - 'UZB' => __('Uzbekistan', 'forms-bridge'), - 'VUT' => __('Vanuatu', 'forms-bridge'), - 'VAT' => __('Vatican', 'forms-bridge'), - 'VEN' => __('Venezuela', 'forms-bridge'), - 'VNM' => __('Vietnam', 'forms-bridge'), - 'WLF' => __('Wallis and Futuna', 'forms-bridge'), - 'ESH' => __('Western Sahara', 'forms-bridge'), - 'YEM' => __('Yemen', 'forms-bridge'), - 'ZMB' => __('Zambia', 'forms-bridge'), - 'ZWE' => __('Zimbabwe', 'forms-bridge'), -]; diff --git a/deps/http b/deps/http deleted file mode 160000 index 531a79e9..00000000 --- a/deps/http +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 531a79e9c6e83a7b854bb8fbdc3fa60c436c8f7a diff --git a/deps/i18n b/deps/i18n deleted file mode 160000 index 3c26d2de..00000000 --- a/deps/i18n +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c26d2de650ff7b758b36f96fa174aa7bf9d351b diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index 3b50f366..00000000 --- a/docs/API.md +++ /dev/null @@ -1,244 +0,0 @@ -# API - -## Table of Contents - -1. [Getters](#getters) -2. [Filters](#filters) -3. [Actions](#actions) - -## Getters - -### `forms_bridge_form` - -Get the current form. - -#### Arguments - -1. `any $default`: Default value. -2. `integer $form_id`: If declared, try to return form by ID instead of the current form. - -#### Returns - -1. `array|null $form_data`: Form data. - -#### Example - -```php -$form_data = apply_filters('forms_bridge_form', null); -if (!empty($form)) { - // do something -} -``` - -### `forms_bridge_forms` - -Get available forms. - -#### Arguments - -1. `mixed $default`: Fallback value. - -#### Returns - -1. `array $forms_data`: Available forms as list of form data. - -#### Example - -```php -$forms_data = apply_filters('forms_bridge_forms', []); -foreach ($forms_data as $form_data) { - // do something -} -``` - -### `forms_bridge_bridges` - -Get available bridges for the current form. - -#### Arguments - -1. `array $bridges`: Initial value. -2. `string|integer $form_id`: If declared, try to return bridges from the form with this id. This id should includes the integration prefix if there is more than one active integration. -2. `string $api`: If declared, filters bridges by api name. - -#### Returns - -1. `array $bridges`: List of given form available bridges instances. - -#### Example - -```php -$bridges = apply_filters('forms_bridge_bridges', [], 'wpcf7:12'); -foreach ($bridges as $bridge) { - // do something -} -``` - -### `forms_bridge_submission` - -Gets the current form submission. - -#### Arguments - -1. `mixed $default`: Fallback value. - -#### Returns - -1. `array|null $submission`: Current form's submission data. - -#### Example - -```php -$submission = apply_filters('forms_bridge_submission', null); -if ($submission) { - // do something -} -``` - -### `forms_bridge_uploads` - -Gets the current form's submission uploaded files. - -#### Arguments - -1. `mixed $default`: Fallback value. - -#### Returns - -1. `array|null`: Current form's submission uploaded files. - -#### Example - -```php -$uploads = apply_filters('forms_bridge_uploads', []); -foreach ($uploads as $uplad) { - // do something -} -``` - -## Filters - -### `forms_bridge_payload` - -Filters the submission data to be sent to the backend. - -#### Arguments - -1. `array $payload`: Submission payload. -4. `Form_Bridge $bridge`: Bridge instance. - -#### Example - -```php -add_filter('forms_bridge_payload', function ($payload, $bridge) { - return $payload; -}, 10, 4); -``` - -### `forms_bridge_attachments` - -Filters attached files to be sent to the backend. - -#### Arguments - -1. `array $uploads`: Submission attached files. -3. `Form_Bridge $bridge`: Bridge instance. - -#### Example - -```php -add_filter('forms_bridge_attachments', function ($attachments, $bridge) { - return $attachments; -}, 10, 3); -``` - -### `forms_bridge_prune_empties` - -Control if Forms Bridge should clean up the submission data and prune its empty fields. - -#### Arguments - -1. `boolean $prune`: False by default. -2. `Form_Bridge $bridge`: Bridge instance. - -#### Example - -```php -add_filter('forms_bridge_prune_empties', '__return_true'); -``` - -### `forms_bridge_skip_submission` - -Control if Forms Bridge should skip the form submission. - -#### Arguments - -1. `boolean $prune`: False by default. -2. `Form_Bridge $bridge`: Bridge instance. -3. `array $payload`: Payload data. -4. `array $attachments`: Attachments list. - -#### Example - -```php -add_filter('forms_bridge_skip_submission', '__return_true'); -``` - - -## Actions - -### `forms_bridge_before_submission` - -Action to do just before submission has been sent to the backend. - -#### Arguments - -1. `Form_Bridge $bridge`: Bridge instance. -2. `array $payload`: Payload data. -3. `array $attachments`: Attachments list. - -#### Example - -```php -add_action('forms_bridge_before_submission', function ($bridge, $payload, $attachments) { - // do something -}, 10, 3); -``` - -### `forms_bridge_after_submission` - -Action to do after the submission has been succesfuly sent to the backend. - -#### Arguments - -1. `Form_Bridge $bridge`: Bridge instance. -2. `array $payload`: Submission payload. -3. `array $attachments`: Attachments list. -4. `array $response`: HTTP response data. - -#### Example - -```php -add_action('forms_bridge_after_submission', function ($bridge, $payload, $attachments, $response) { - // do something -}, 10, 1); -``` - -### `forms_bridge_on_failure` - -Action to do after a request connexion error with the backend. - -#### Arguments - -1. `Form_Bridge $bridge`: Bridge instance. -2. `WP_Error $error`: HTTP response error. -3. `array $payload`: Submission payload. -4. `array $attachments`: Attachments list. - -#### Example - -```php -add_action('forms_bridge_on_failure', function ($bridge, $error, $payload, $attachments) { - // do something -}, 10, 4); -``` diff --git a/docs/Addons.md b/docs/Addons.md deleted file mode 100644 index bc872310..00000000 --- a/docs/Addons.md +++ /dev/null @@ -1,15 +0,0 @@ -# Addons - -## REST API - -## Odoo - -Odoo addon allows Forms Bridge to hook forms with Odoo's models. Let WordPress do the work is intended for and place your business logic on an ERP system, and bridge them on a unique leads pipeline that connects backend and frontend over the JSON-RPC API protocol. - -To setup form hooks to bridge your forms and your Odoo, you will have to enable this addon. Once activated, Odoo will be shown as a new tab on the Forms Bridge settings page. Before you start to hook your forms, its worth to mention that to configure this addon's form hooks, the settings page will ask you for a database instead of a backend. This addon establishes connections with Odoo over the JSON-RPC API. This API requires some credentials to authenticate the requests. The addon handles this credentials as database access data, that you can configure in the databases panel. For each database access you will need to select a backend, a database name, a username and a password (or API-KEY). The addon requires, at least, one database access before you can start hooking your forms. - -## Google Sheets - -The WordPress' admin page is a powerful and versatil backoffice for manage your CMS, but is not the place where your team has to work if you are tailoring a marketing campaign or dispatching sell orders. Google Sheets addon allow you to synchronize your website form submission with spreadsheets from google and decouple your webpage and your data sources and focus on what really matters. - -## FinanCoop diff --git a/esbuild/build.cjs b/esbuild/build.cjs index 1776a589..7e1b32bd 100644 --- a/esbuild/build.cjs +++ b/esbuild/build.cjs @@ -1,26 +1,26 @@ const esbuild = require("esbuild"); (async () => { - await esbuild.build({ - entryPoints: ["src/index.jsx"], - bundle: true, - sourcemap: true, - minify: true, - outfile: "assets/plugin.bundle.js", - loader: { ".png": "base64" }, - plugins: [ - { - name: "rebuild-log", - setup({ onStart, onEnd }) { - var t; - onStart(() => { - t = Date.now(); - }); - onEnd(() => { - console.log("build finished in", Date.now() - t, "ms"); - }); - }, - }, - ], - }); + await esbuild.build({ + entryPoints: ["src/index.jsx"], + bundle: true, + sourcemap: true, + minify: true, + outfile: "forms-bridge/assets/plugin.bundle.js", + loader: { ".png": "base64" }, + plugins: [ + { + name: "rebuild-log", + setup({ onStart, onEnd }) { + var t; + onStart(() => { + t = Date.now(); + }); + onEnd(() => { + console.log("build finished in", Date.now() - t, "ms"); + }); + }, + }, + ], + }); })(); diff --git a/esbuild/dev.cjs b/esbuild/dev.cjs index a6239011..042e37e1 100644 --- a/esbuild/dev.cjs +++ b/esbuild/dev.cjs @@ -1,28 +1,28 @@ const esbuild = require("esbuild"); (async () => { - const ctx = await esbuild.context({ - entryPoints: ["src/index.jsx"], - bundle: true, - sourcemap: true, - outfile: "assets/plugin.bundle.js", - loader: { ".png": "base64" }, - plugins: [ - { - name: "rebuild-log", - setup({ onStart, onEnd }) { - var t; - onStart(() => { - t = Date.now(); - }); - onEnd(() => { - console.log("build finished in", Date.now() - t, "ms"); - }); - }, - }, - ], - }); + const ctx = await esbuild.context({ + entryPoints: ["src/index.jsx"], + bundle: true, + sourcemap: true, + outfile: "forms-bridge/assets/plugin.bundle.js", + loader: { ".png": "base64" }, + plugins: [ + { + name: "rebuild-log", + setup({ onStart, onEnd }) { + var t; + onStart(() => { + t = Date.now(); + }); + onEnd(() => { + console.log("build finished in", Date.now() - t, "ms"); + }); + }, + }, + ], + }); - await ctx.watch(); - console.log("watching..."); + await ctx.watch(); + console.log("watching..."); })(); diff --git a/eslint.config.js b/eslint.config.js index 2af9efed..5a3fac48 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,18 +1,44 @@ +const { defineConfig } = require("eslint/config"); const globals = require("globals"); -const pluginJs = require("@eslint/js"); -const prettier = require("prettier"); +const js = require("@eslint/js"); +const react = require("eslint-plugin-react"); +const eslintConfigPrettier = require("eslint-config-prettier/flat"); -module.exports = [ +const languageOptions = { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + globals: { + ...globals.browser, + wp: "readonly", + } +}; + +module.exports = defineConfig([ + { + ignores: ["vendor/", "node_modules/", "*.config.js", "composer.*", "package*.json"], + }, { - languageOptions: { globals: globals.browser }, - ignores: [ - "*.config.js", - ".lintstagedrc.js", - ".prettierrc", - ".prettierrc-php", - "package*.json", + settings: { + react: { + version: "18", + } + }, + files: ["forms-bridge/src/**/*.js", "forms-bridge/src/**/*.jsx"], + plugins: { js, react }, + extends: [ + "js/recommended", + react.configs.flat.recommended, + react.configs.flat["jsx-runtime"] ], + languageOptions, + rules: { + "no-case-declarations": 0, + "react/prop-types": 0, + "react/jsx-no-target-blank": 0, + } }, - pluginJs.configs.recommended, - prettier, -]; + eslintConfigPrettier, +]); diff --git a/forms-bridge.php b/forms-bridge.php deleted file mode 100644 index b683c4c7..00000000 --- a/forms-bridge.php +++ /dev/null @@ -1,671 +0,0 @@ -%s', - esc_url($url), - esc_html($label) - ); - array_push($links, $link); - - return $links; - }, - 15, - 2 - ); - - add_action( - 'forms_bridge_on_failure', - static function ($bridge, $error, $payload, $attachments = []) { - self::notify_error($bridge, $error, $payload, $attachments); - }, - 99, - 4 - ); - - add_action('init', [self::class, 'load_data'], 0, 0); - - add_action( - 'in_plugin_update_message-forms-bridge/forms-bridge.php', - function ($plugin_data, $response) { - if ($response->slug !== 'forms-bridge') { - return; - } - - if ( - !preg_match( - '/^(\d+)\.\d+\.\d+$/', - $response->new_version, - $matches - ) - ) { - return; - } - - $new_version = $matches[1]; - $db_version = get_option(self::db_version, '1.0.0'); - - if (!preg_match('/^(\d+)\.\d+\.\d+$/', $db_version, $matches)) { - return; - } - - $from_version = $matches[1]; - - if ($new_version > $from_version) { - echo '
' . - ' ' . - __( - 'This is a major release and while tested thoroughly you might experience conflicts or lost data. We recommend you back up your data before updating and check your configuration after updating.', - 'forms-bridge' - ) . - ''; - } - }, - 10, - 2 - ); - } - - /** - * Plugin activation callback. Stores the plugin version on the database - * if it doesn't exists. - */ - public static function activate() - { - $version = get_option(self::db_version); - if ($version === false) { - update_option(self::db_version, self::version(), true); - } - } - - /** - * Init hook callabck. Checks if the stored db version mismatch the current plugin version - * and, if it is, performs db migrations. - */ - protected static function init() - { - $db_version = get_option(self::db_version); - if ($db_version !== self::version()) { - self::do_migrations(); - } - } - - /** - * Data loader. - */ - public static function load_data() - { - $data_dir = self::path() . 'data'; - foreach (array_diff(scandir($data_dir), ['.', '..']) as $file) { - $filepath = "{$data_dir}/{$file}"; - if (is_file($filepath) && is_readable($filepath)) { - require_once $filepath; - } - } - } - - /** - * Enqueue admin client scripts - */ - private static function admin_enqueue_scripts() - { - $version = self::version(); - - wp_enqueue_script( - 'forms-bridge', - plugins_url('assets/plugin.bundle.js', __FILE__), - [ - 'react', - 'react-jsx-runtime', - 'wp-api-fetch', - 'wp-components', - 'wp-dom-ready', - 'wp-element', - 'wp-i18n', - 'wp-api', - ], - $version, - ['in_footer' => true] - ); - - wp_set_script_translations( - 'forms-bridge', - 'forms-bridge', - self::path() . 'languages' - ); - - wp_enqueue_style('wp-components'); - - wp_enqueue_style( - 'highlight-js', - 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css', - [], - '11.11.1' - ); - - wp_enqueue_script( - 'highlight-js', - 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js', - [], - '11.11.1' - ); - } - - public static function current_bridge() - { - return self::$current_bridge; - } - - /** - * Proceed with the submission sub-routine. - */ - public static function do_submission() - { - $form_data = FBAPI::get_current_form(); - - if (!$form_data) { - return; - } - - if (empty($form_data['bridges'])) { - return; - } - - Logger::log('Form data'); - Logger::log([ - 'id' => $form_data['id'], - 'title' => $form_data['title'], - 'fields' => array_map(function ($field) { - return $field['name']; - }, $form_data['fields']), - ]); - - $bridges = $form_data['bridges']; - - $submission = FBAPI::get_submission(); - Logger::log('Form submission'); - Logger::log($submission); - - $uploads = FBAPI::get_uploads(); - Logger::log('Submission uploads'); - Logger::log($uploads); - - if (empty($submission) && empty($uploads)) { - return; - } - - foreach ($bridges as $bridge) { - if (!$bridge->enabled) { - Logger::log( - 'Skip submission for disabled bridge ' . $bridge->name - ); - continue; - } - - self::$current_bridge = $bridge; - - try { - $attachments = apply_filters( - 'forms_bridge_attachments', - self::attachments($uploads), - $bridge - ); - - if (!empty($attachments)) { - $content_type = $bridge->content_type; - if ( - in_array($content_type, [ - 'application/json', - 'application/x-www-form-urlencoded', - ]) - ) { - $attachments = self::stringify_attachments( - $attachments - ); - foreach ($attachments as $name => $value) { - $submission[$name] = $value; - } - $attachments = []; - Logger::log('Submission after attachments stringify'); - Logger::log($submission); - } - } - - $payload = $bridge->add_custom_fields($submission); - Logger::log('Submission payload with bridge custom fields'); - Logger::log($payload); - - $bridge->prepare_mappers($form_data); - $payload = $bridge->apply_mutation($payload); - Logger::log('Submission payload after mutation'); - Logger::log($payload); - - $prune_empties = apply_filters( - 'forms_bridge_prune_empties', - true, - $bridge - ); - - if ($prune_empties) { - $payload = self::prune_empties($payload); - Logger::log('Submission payload after prune empties'); - Logger::log($payload); - } - - if ($job = $bridge->workflow) { - $payload = $job->run($payload, $bridge); - - if (empty($payload)) { - Logger::log('Skip empty payload after bridge workflow'); - continue; - } - - Logger::log('Payload after workflow'); - Logger::log($payload); - } - - $payload = apply_filters( - 'forms_bridge_payload', - $payload, - $bridge - ); - - if (empty($payload)) { - Logger::log('Skip empty payload after user filter'); - continue; - } - - Logger::log('Bridge payload'); - Logger::log($payload); - - $skip = apply_filters( - 'forms_bridge_skip_submission', - false, - $bridge, - $payload, - $attachments - ); - - if ($skip) { - Logger::log('Skip submission'); - continue; - } - - do_action( - 'forms_bridge_before_submission', - $bridge, - $payload, - $attachments - ); - - $response = $bridge->submit($payload, $attachments); - - if ($error = is_wp_error($response) ? $response : null) { - do_action( - 'forms_bridge_on_failure', - $bridge, - $error, - $payload, - $attachments - ); - } else { - Logger::log('Submission response'); - Logger::log($response); - - do_action( - 'forms_bridge_after_submission', - $bridge, - $response, - $payload, - $attachments - ); - } - } catch (Error | Exception $e) { - $message = $e->getMessage(); - if ($message === 'notification_error') { - throw $e; - } - - $error = new WP_Error( - 'internal_server_error', - $e->getMessage(), - [ - 'file' => $e->getFile(), - 'line' => $e->getLine(), - ] - ); - - do_action( - 'forms_bridge_on_failure', - $bridge, - $error, - $payload ?? $submission, - $attachments ?? [] - ); - } finally { - self::$current_bridge = null; - } - } - } - - /** - * Clean up submission empty fields. - * - * @param array $submission_data Submission data. - * - * @return array Submission data without empty fields. - */ - private static function prune_empties($submission_data) - { - foreach ($submission_data as $key => $val) { - if ($val === '' || $val === null) { - unset($submission_data[$key]); - } - } - - return $submission_data; - } - - /** - * Transform collection of uploads to an attachments map. - * - * @param array $uploads Collection of uploaded files. - * - * @return array Map of uploaded files. - */ - private static function attachments($uploads) - { - $attachments = []; - - foreach ($uploads as $name => $upload) { - if ($upload['is_multi']) { - for ($i = 1; $i <= count($uploads[$name]['path']); $i++) { - $attachments[$name . '_' . $i] = $upload['path'][$i - 1]; - } - } else { - $attachments[$name] = $upload['path']; - } - } - - return $attachments; - } - - /** - * Returns the attachments array with each attachment path replaced with its - * content as a base64 encoded string. For each file on the list, adds an - * additonal field with the file name on the response. - * - * @param array $attachments Submission attachments data. - * - * @return array Array with base64 encoded file contents and file names. - */ - private static function stringify_attachments($attachments) - { - foreach ($attachments as $name => $path) { - if (!is_file($path) || !is_readable($path)) { - continue; - } - - $suffix = ''; - if (preg_match('/_\d+$/', $name, $matches)) { - $suffix = $matches[0]; - $name = substr($name, 0, -strlen($suffix)); - } - - $filename = basename($path); - $content = file_get_contents($path); - $attachments[$name . $suffix] = base64_encode($content); - $attachments[$name . '_filename' . $suffix] = $filename; - } - - return $attachments; - } - - /** - * Sends error notifications to the email receiver. - * - * @param Form_Bridge $bridge Bridge instance. - * @param WP_Error $error Error instance. - * @param array $payload Submission data. - * @param array $attachments Submission attachments. - */ - private static function notify_error( - $bridge, - $error, - $payload, - $attachments = [] - ) { - $email = Settings_Store::setting('general')->notification_receiver; - - if (empty($email)) { - return; - } - - $skip = apply_filters( - 'forms_bridge_skip_error_notification', - false, - $error, - $bridge, - $payload, - $attachments - ); - - if ($skip) { - Logger::log('Skip error notification'); - return; - } - - $form_data = $bridge->form; - $payload = json_encode($payload, JSON_PRETTY_PRINT); - $error = json_encode( - [ - 'error' => $error->get_error_message(), - 'context' => $error->get_error_data(), - ], - JSON_PRETTY_PRINT - ); - - Logger::log('Bridge submission error', Logger::ERROR); - Logger::log($error, Logger::ERROR); - - $to = $email; - $subject = 'Forms Bridge Error'; - $body = "Form ID: {$form_data['id']}\n"; - $body .= "Form title: {$form_data['title']}\n"; - $body .= "Bridge name: {$bridge->name}\n"; - $body .= "Payload: {$payload}\n"; - $body .= "Error: {$error}\n"; - - $from_email = get_option('admin_email'); - $headers = ["From: Forms Bridge <{$from_email}>"]; - - Logger::log('Error notification'); - Logger::log($body); - - $success = wp_mail($to, $subject, $body, $headers, $attachments); - if (!$success) { - throw new Exception('notification_error'); - } - } - - /** - * Apply db migrations on plugin upgrades. - */ - private static function do_migrations() - { - $from = get_option(self::db_version, '1.0.0'); - - if (!preg_match('/^\d+\.\d+\.\d+$/', $from)) { - Logger::log('Invalid db plugin version', Logger::ERROR); - return; - } - - $to = self::version(); - - $migrations = []; - $migrations_path = self::path() . 'migrations'; - - $as_int = fn($version) => (int) str_replace('.', '', $version); - - foreach ( - array_diff(scandir($migrations_path), ['.', '..']) - as $migration - ) { - $version = pathinfo($migrations_path . '/' . $migration)[ - 'filename' - ]; - - if ($as_int($version) > $as_int($to)) { - break; - } - - if (!empty($migrations)) { - $migrations[] = $migration; - continue; - } - - if ( - $as_int($version) > $as_int($from) && - $as_int($version) <= $as_int($to) - ) { - $migrations[] = $migration; - } - } - - sort($migrations); - foreach ($migrations as $migration) { - include $migrations_path . '/' . $migration; - } - - update_option(self::db_version, $to); - } - - public static function upload_dir() - { - $dir = wp_upload_dir()['basedir'] . '/forms-bridge'; - - if (!is_dir($dir)) { - if (!mkdir($dir, 755)) { - return; - } - } - - return $dir; - } -} - -// Start the plugin -Forms_Bridge::setup(); diff --git a/forms-bridge/addons/bigin/api.php b/forms-bridge/addons/bigin/api.php new file mode 100644 index 00000000..f2429d33 --- /dev/null +++ b/forms-bridge/addons/bigin/api.php @@ -0,0 +1,114 @@ + $payload['Last_Name'], + ); + + $contact_fields = array( + 'Owner', + 'Full_Name', + 'First_Name', + 'Email', + 'Phone', + 'Mobile', + 'Title', + 'Account_Name', + 'Description', + 'Mailing_Street', + 'Mailing_City', + 'Mailing_Zip', + 'Mailing_State', + 'Mailing_Country', + ); + + foreach ( $contact_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $contact[ $field ] = $payload[ $field ]; + } + } + + if ( + isset( $payload['Account_Name'] ) && + is_string( $payload['Account_Name'] ) + ) { + $account = forms_bridge_bigin_create_account( $payload, $bridge ); + + if ( is_wp_error( $account ) ) { + return $account; + } + + $payload['Account_Name'] = array( 'id' => $account['id'] ); + } + + $response = $bridge + ->patch( + array( + 'name' => 'zoho-bigin-create-contact', + 'scope' => 'ZohoBigin.modules.contacts.CREATE', + 'endpoint' => '/bigin/v2/Contacts/upsert', + 'template' => null, + ) + ) + ->submit( $contact ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $code = $response['data']['data'][0]['code'] ?? null; + if ( $code === 'DUPLICATE_DATA' ) { + return $response['data']['data'][0]['details']['duplicate_record']; + } else { + return $response['data']['data'][0]['details']; + } +} + +function forms_bridge_bigin_create_account( $payload, $bridge ) { + $company = array( + 'Account_Name' => $payload['Account_Name'], + ); + + $company_fields = array( + 'Owner', + 'Phone', + 'Website', + 'Billing_Street', + 'Billing_Code', + 'Billing_City', + 'Billing_State', + 'Billing_Country', + 'Description', + ); + + foreach ( $company_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $company[ $field ] = $payload[ $field ]; + } + } + + $response = $bridge + ->patch( + array( + 'name' => 'zoho-bigin-create-account', + 'scope' => 'ZohoBigin.modules.accounts.CREATE', + 'endpoint' => '/bigin/v2/Accounts/upsert', + ) + ) + ->submit( $company ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $code = $response['data']['data'][0]['code'] ?? null; + if ( $code === 'DUPLICATE_DATA' ) { + return $response['data']['data'][0]['details']['duplicate_record']; + } else { + return $response['data']['data'][0]['details']; + } +} diff --git a/addons/bigin/assets/logo.png b/forms-bridge/addons/bigin/assets/logo.png similarity index 100% rename from addons/bigin/assets/logo.png rename to forms-bridge/addons/bigin/assets/logo.png diff --git a/forms-bridge/addons/bigin/class-bigin-addon.php b/forms-bridge/addons/bigin/class-bigin-addon.php new file mode 100644 index 00000000..1ee53d68 --- /dev/null +++ b/forms-bridge/addons/bigin/class-bigin-addon.php @@ -0,0 +1,153 @@ + '__bigin-' . time(), + 'backend' => $backend, + 'endpoint' => '/bigin/v2/users', + 'method' => 'GET', + ) + ); + + $backend = $bridge->backend; + if ( ! $backend ) { + return false; + } + + $credential = $backend->credential; + if ( ! $credential ) { + return false; + } + + $parsed = wp_parse_url( $backend->base_url ); + $host = $parsed['host'] ?? ''; + + if ( + ! preg_match( + '/www\.zohoapis\.(\w{2,3}(\.\w{2})?)$/', + $host, + $matches + ) + ) { + return false; + } + + $region = $matches[1]; + if ( ! preg_match( '/' . $region . '$/', $credential->region ) ) { + return false; + } + + $response = $bridge->submit( array( 'type' => 'CurrentUser' ) ); + return ! is_wp_error( $response ); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array List of fields and content type of the endpoint. + */ + public function get_endpoint_schema( $endpoint, $backend ) { + if ( + ! preg_match( + '/\/(([A-Z][a-z]+(_[A-Z][a-z])?)(?:\/upsert)?$)/', + $endpoint, + $matches + ) + ) { + return array(); + } + + $module = $matches[2]; + + $bridge_class = self::bridge_class; + $bridge = new $bridge_class( + array( + 'name' => '__bigin-' . time(), + 'backend' => $backend, + 'endpoint' => '/bigin/v2/settings/layouts', + 'method' => 'GET', + ) + ); + + $response = $bridge->submit( array( 'module' => $module ) ); + + if ( is_wp_error( $response ) ) { + return array(); + } + + $fields = array(); + foreach ( $response['data']['layouts'] as $layout ) { + foreach ( $layout['sections'] as $section ) { + foreach ( $section['fields'] as $field ) { + $type = $field['json_type']; + if ( $type === 'jsonobject' ) { + $type = 'object'; + } elseif ( $type === 'jsonarray' ) { + $type = 'array'; + } elseif ( $type === 'double' ) { + $type = 'number'; + } + + $fields[] = array( + 'name' => $field['api_name'], + 'schema' => array( 'type' => $type ), + ); + } + } + } + + return $fields; + } +} + +Bigin_Addon::setup(); diff --git a/forms-bridge/addons/bigin/class-bigin-form-bridge.php b/forms-bridge/addons/bigin/class-bigin-form-bridge.php new file mode 100644 index 00000000..d7639fa2 --- /dev/null +++ b/forms-bridge/addons/bigin/class-bigin-form-bridge.php @@ -0,0 +1,17 @@ + array( + array( + 'ref' => '#credential', + 'name' => 'scope', + 'value' => + 'ZohoBigin.modules.ALL,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ', + ), + ), + 'credential' => array( + 'scope' => + 'ZohoBigin.modules.ALL,ZohoBigin.settings.layouts.READ,ZohoBigin.users.READ', + ), + ), + $defaults, + $schema + ); + }, + 20, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'bigin-' ) !== 0 ) { + return $data; + } + + return apply_filters( + 'forms_bridge_template_data', + $data, + 'zoho-' . $template_id + ); + }, + 10, + 2 +); diff --git a/forms-bridge/addons/bigin/jobs/account-name.php b/forms-bridge/addons/bigin/jobs/account-name.php new file mode 100644 index 00000000..cb7862a4 --- /dev/null +++ b/forms-bridge/addons/bigin/jobs/account-name.php @@ -0,0 +1,90 @@ + $account['id'], + ); + + return $payload; +} + +return array( + 'title' => __( 'Account name', 'forms-bridge' ), + 'description' => __( + 'Search for an account by name or creates a new if it does\'t exists and replace the name by the ID on the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_zoho_bigin_account_name', + 'input' => array( + array( + 'name' => 'Account_Name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'Owner', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + 'required' => array( 'id' ), + ), + ), + array( + 'name' => 'Phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Website', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_Street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_Code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_City', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_State', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_Country', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Description', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'Account_Name', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + ), +); diff --git a/forms-bridge/addons/bigin/jobs/appointment-participant.php b/forms-bridge/addons/bigin/jobs/appointment-participant.php new file mode 100644 index 00000000..31829f53 --- /dev/null +++ b/forms-bridge/addons/bigin/jobs/appointment-participant.php @@ -0,0 +1,104 @@ + 'contact', + 'participant' => $contact['id'], + ); + + return $payload; +} + +return array( + 'title' => __( 'Appointment participant', 'forms-bridge' ), + 'description' => __( + 'Search for a contact or creates a new one and sets its ID as appointment participant', + 'forms-bridge' + ), + 'method' => 'forms_bridge_bigin_appointment_participant', + 'input' => array( + array( + 'name' => 'Last_Name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'First_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Full_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_Street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_City', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_Zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_State', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_Country', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Description', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Account_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Title', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'Participants', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'type' => array( 'type' => 'string' ), + 'participant' => array( 'type' => 'string' ), + ), + ), + 'additionalItems' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/bigin/jobs/contact-name.php b/forms-bridge/addons/bigin/jobs/contact-name.php new file mode 100644 index 00000000..88de0f7d --- /dev/null +++ b/forms-bridge/addons/bigin/jobs/contact-name.php @@ -0,0 +1,89 @@ + $contact['id'], + ); + + return $payload; +} + +return array( + 'title' => __( 'Contact name', 'forms-bridge' ), + 'description' => __( + 'Search for a contact by email or creates a new if it does\'t exists and replace the name by the ID on the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_bigin_contact_name', + 'input' => array( + array( + 'name' => 'Email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'First_Name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'Phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Title', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Account_Name', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + array( + 'name' => 'Description', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'Contact_Name', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + array( + 'name' => 'Account_Name', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + ), +); diff --git a/forms-bridge/addons/bigin/jobs/event-dates.php b/forms-bridge/addons/bigin/jobs/event-dates.php new file mode 100644 index 00000000..290a28e4 --- /dev/null +++ b/forms-bridge/addons/bigin/jobs/event-dates.php @@ -0,0 +1,54 @@ +getTimestamp(); + + $payload['Start_DateTime'] = date( 'c', $timestamp ); + $payload['End_DateTime'] = date( 'c', $timestamp + 3600 * $duration ); + + return $payload; +} + +return array( + 'title' => __( 'Appointment dates', 'forms-bridge' ), + 'description' => __( + 'Sets appointment start and end time from "date" and "duration" fields', + 'forms-bridge' + ), + 'method' => 'forms_bridge_bigin_appointment_dates', + 'input' => array( + array( + 'name' => 'date', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'duration', + 'schema' => array( 'type' => 'number' ), + ), + ), + 'output' => array( + array( + 'name' => 'Start_DateTime', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'End_DateTime', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/bigin/templates/companies.php b/forms-bridge/addons/bigin/templates/companies.php new file mode 100644 index 00000000..c8fa078d --- /dev/null +++ b/forms-bridge/addons/bigin/templates/companies.php @@ -0,0 +1,115 @@ + __( 'Company Contact', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into contacts linked to company accounts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/bigin/v2/Contacts/upsert', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the account', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/bigin/v2/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Companies', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'Account_Name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Billing_Street', + 'label' => __( 'Street', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_Code', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_City', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_State', + 'label' => __( 'State', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_Country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Title', + 'label' => __( 'Title', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/bigin/v2/Contacts/upsert', + 'workflow' => array( 'account-name' ), + ), +); diff --git a/forms-bridge/addons/bigin/templates/contacts.php b/forms-bridge/addons/bigin/templates/contacts.php new file mode 100644 index 00000000..71efabd2 --- /dev/null +++ b/forms-bridge/addons/bigin/templates/contacts.php @@ -0,0 +1,77 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/bigin/v2/Contacts/upsert', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner ID', 'forms-bridge' ), + 'description' => __( + 'ID of the owner user of the contact', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/bigin/v2/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Contacts', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/bigin/v2/Contacts/upsert', + ), +); diff --git a/forms-bridge/addons/bigin/templates/deals.php b/forms-bridge/addons/bigin/templates/deals.php new file mode 100644 index 00000000..32c3da31 --- /dev/null +++ b/forms-bridge/addons/bigin/templates/deals.php @@ -0,0 +1,188 @@ + __( 'Deals', 'forms-bridge' ), + 'description' => __( + 'Leads form templates. The resulting bridge will convert form submissions into deals on the sales pipeline linked new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/bigin/v2/Pipelines', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'descritpion' => __( + 'Email of the owner user of the deal', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/bigin/v2/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Deal_Name', + 'label' => __( 'Deal name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Stage', + 'label' => __( 'Deal stage', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'value' => 'Qualification', + 'label' => __( 'Qualification', 'forms-bridge' ), + ), + array( + 'value' => 'Needs Analysis', + 'label' => __( 'Needs Analysis', 'forms-bridge' ), + ), + array( + 'value' => 'Proposal/Price Quote', + 'label' => __( 'Proposal/Price Quote', 'forms-bridge' ), + ), + array( + 'value' => 'Negotation/Review', + 'label' => __( 'Negotiation/Review', 'forms-bridge' ), + ), + array( + 'value' => 'Closed Won', + 'label' => __( 'Closed Won', 'forms-bridge' ), + ), + array( + 'value' => 'Closed Lost', + 'label' => __( 'Closed Lost', 'forms-bridge' ), + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Sub_Pipeline', + 'label' => __( 'Pipeline name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Amount', + 'label' => __( 'Deal amount', 'forms-bridge' ), + 'type' => 'number', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Tag', + 'label' => __( 'Deal tags', 'forms-bridge' ), + 'description' => __( + 'Tag names separated by commas', + 'forms-bridge' + ), + 'type' => 'text', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Deals', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'Account_Name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Billing_Street', + 'label' => __( 'Street', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_Code', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_City', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_State', + 'label' => __( 'State', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Billing_Country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Title', + 'label' => __( 'Title', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/bigin/v2/Pipelines', + 'workflow' => array( 'account-name', 'contact-name' ), + 'mutations' => array( + array( + array( + 'from' => 'Amount', + 'to' => 'Amount', + 'cast' => 'number', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/bigin/templates/meetings.php b/forms-bridge/addons/bigin/templates/meetings.php new file mode 100644 index 00000000..734940a3 --- /dev/null +++ b/forms-bridge/addons/bigin/templates/meetings.php @@ -0,0 +1,290 @@ + __( 'Meetings', 'forms-bridge' ), + 'description' => __( + 'Meetings form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/bigin/v2/Events', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'descritpion' => __( + 'Email of the owner user of the event', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/bigin/v2/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Event_Title', + 'label' => __( 'Event title', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web Meeting', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'All_day', + 'label' => __( 'Is all day event?', 'forms-bridge' ), + 'type' => 'boolean', + 'default' => false, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'duration', + 'label' => __( 'Duration', 'forms-bridge' ), + 'description' => __( 'Duration in hours', 'forms-bridge' ), + 'type' => 'number', + 'default' => '1', + 'min' => 0, + 'max' => 24, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Meetings', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'date', + 'label' => __( 'Date', 'forms-bridge' ), + 'type' => 'date', + 'required' => true, + ), + array( + 'name' => 'hour', + 'label' => __( 'Hour', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( '1 AM', 'forms-bridge' ), + 'value' => '01', + ), + array( + 'label' => __( '2 AM', 'forms-bridge' ), + 'value' => '02', + ), + array( + 'label' => __( '3 AM', 'forms-bridge' ), + 'value' => '03', + ), + array( + 'label' => __( '4 AM', 'forms-bridge' ), + 'value' => '04', + ), + array( + 'label' => __( '5 AM', 'forms-bridge' ), + 'value' => '05', + ), + array( + 'label' => __( '6 AM', 'forms-bridge' ), + 'value' => '06', + ), + array( + 'label' => __( '7 AM', 'forms-bridge' ), + 'value' => '07', + ), + array( + 'label' => __( '8 AM', 'forms-bridge' ), + 'value' => '08', + ), + array( + 'label' => __( '9 AM', 'forms-bridge' ), + 'value' => '09', + ), + array( + 'label' => __( '10 AM', 'forms-bridge' ), + 'value' => '10', + ), + array( + 'label' => __( '11 AM', 'forms-bridge' ), + 'value' => '11', + ), + array( + 'label' => __( '12 AM', 'forms-bridge' ), + 'value' => '12', + ), + array( + 'label' => __( '1 PM', 'forms-bridge' ), + 'value' => '13', + ), + array( + 'label' => __( '2 PM', 'forms-bridge' ), + 'value' => '14', + ), + array( + 'label' => __( '3 PM', 'forms-bridge' ), + 'value' => '15', + ), + array( + 'label' => __( '4 PM', 'forms-bridge' ), + 'value' => '16', + ), + array( + 'label' => __( '5 PM', 'forms-bridge' ), + 'value' => '17', + ), + array( + 'label' => __( '6 PM', 'forms-bridge' ), + 'value' => '18', + ), + array( + 'label' => __( '7 PM', 'forms-bridge' ), + 'value' => '19', + ), + array( + 'label' => __( '8 PM', 'forms-bridge' ), + 'value' => '20', + ), + array( + 'label' => __( '9 PM', 'forms-bridge' ), + 'value' => '21', + ), + array( + 'label' => __( '10 PM', 'forms-bridge' ), + 'value' => '22', + ), + array( + 'label' => __( '11 PM', 'forms-bridge' ), + 'value' => '23', + ), + array( + 'label' => __( '12 PM', 'forms-bridge' ), + 'value' => '24', + ), + ), + 'required' => 'true', + ), + array( + 'name' => 'minute', + 'label' => __( 'Minute', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => '00', + 'value' => '00.0', + ), + array( + 'label' => '05', + 'value' => '05', + ), + array( + 'label' => '10', + 'value' => '10', + ), + array( + 'label' => '15', + 'value' => '15', + ), + array( + 'label' => '20', + 'value' => '20', + ), + array( + 'label' => '25', + 'value' => '25', + ), + array( + 'label' => '30', + 'value' => '30', + ), + array( + 'label' => '35', + 'value' => '35', + ), + array( + 'label' => '40', + 'value' => '40', + ), + array( + 'label' => '45', + 'value' => '45', + ), + array( + 'label' => '50', + 'value' => '50', + ), + array( + 'label' => '55', + 'value' => '55', + ), + ), + 'required' => true, + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/bigin/v2/Events', + 'mutations' => array( + array( + array( + 'from' => '?All_day', + 'to' => 'All_day', + 'cast' => 'boolean', + ), + ), + array( + array( + 'from' => 'datetime', + 'to' => 'date', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( + 'date-fields-to-date', + 'event-dates', + 'appointment-participant', + ), + ), +); diff --git a/forms-bridge/addons/bigin/templates/woo-contacts.php b/forms-bridge/addons/bigin/templates/woo-contacts.php new file mode 100644 index 00000000..0f4eccfd --- /dev/null +++ b/forms-bridge/addons/bigin/templates/woo-contacts.php @@ -0,0 +1,349 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert woocommerce customers into contacts.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/bigin/v2/Contacts/upsert', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner ID', 'forms-bridge' ), + 'description' => __( + 'ID of the owner user of the contact', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/bigin/v2/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/bigin/v2/Contacts/upsert', + 'workflow' => array( 'account-name' ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => '?Owner', + 'to' => 'Contact_Owner', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.company', + 'to' => 'Account_Name', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'Billing_Street', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.city', + 'to' => 'Billing_City', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.state', + 'to' => 'Billing_State', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.country', + 'to' => 'Billing_Country', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'Billing_Code', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.first_name', + 'to' => 'First_Name', + 'cast' => 'string', + ), + array( + 'from' => 'billing.last_name', + 'to' => 'Last_Name', + 'cast' => 'string', + ), + array( + 'from' => 'billing.email', + 'to' => 'Email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'Phone', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'Mailing_Street', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'Mailing_City', + 'cast' => 'string', + ), + array( + 'from' => '?billing.state', + 'to' => 'Mailing_State', + 'cast' => 'string', + ), + array( + 'from' => '?billing.country', + 'to' => 'Mailing_Country', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'Mailing_Zip', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'Contact_Owner', + 'to' => 'Owner', + 'cast' => 'inherit', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/brevo/api.php b/forms-bridge/addons/brevo/api.php new file mode 100644 index 00000000..ca93def5 --- /dev/null +++ b/forms-bridge/addons/brevo/api.php @@ -0,0 +1,78 @@ + $payload['name'], + ); + + $company_fields = array( + 'attributes', + 'countryCode', + 'linkedContactsIds', + 'linkedDealsIds', + ); + + foreach ( $company_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $company[ $field ] = $payload[ $field ]; + } + } + + $response = $bridge + ->patch( + array( + 'name' => 'brevo-create-company', + 'endpoint' => '/v3/companies', + 'method' => 'POST', + ) + ) + ->submit( $company ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response['data']; +} + +function forms_bridge_brevo_create_contact( $payload, $bridge ) { + $contact = array( + 'email' => $payload['email'], + ); + + $contact_fields = array( + 'ext_id', + 'attributes', + 'emailBlacklisted', + 'smsBlacklisted', + 'listIds', + 'updateEnabled', + 'smtpBlacklistSender', + ); + + foreach ( $contact_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $contact[ $field ] = $payload[ $field ]; + } + } + + $response = $bridge + ->patch( + array( + 'name' => 'brevo-create-contact', + 'endpoint' => '/v3/contacts', + 'method' => 'POST', + ) + ) + ->submit( $contact ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response['data']; +} diff --git a/addons/brevo/assets/logo.png b/forms-bridge/addons/brevo/assets/logo.png similarity index 100% rename from addons/brevo/assets/logo.png rename to forms-bridge/addons/brevo/assets/logo.png diff --git a/forms-bridge/addons/brevo/class-brevo-addon.php b/forms-bridge/addons/brevo/class-brevo-addon.php new file mode 100644 index 00000000..06f6057c --- /dev/null +++ b/forms-bridge/addons/brevo/class-brevo-addon.php @@ -0,0 +1,320 @@ + '__brevo-' . time(), + 'endpoint' => '/v3/contacts/lists', + 'method' => 'GET', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit( array( 'limit' => 1 ) ); + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge = new Brevo_Form_Bridge( + array( + 'name' => '__brevo-' . time(), + 'endpoint' => $endpoint, + 'backend' => $backend, + 'method' => 'GET', + ) + ); + + return $bridge->submit(); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array + */ + public function get_endpoint_schema( $endpoint, $backend ) { + $bridge = new Brevo_Form_Bridge( + array( + 'name' => '__brevo-' . time(), + 'endpoint' => $endpoint, + 'backend' => $backend, + 'method' => 'GET', + ) + ); + + if ( strstr( $bridge->endpoint, 'contacts' ) ) { + $response = $bridge + ->patch( + array( + 'name' => 'brevo-contacts-attributes', + 'endpoint' => '/v3/contacts/attributes', + 'method' => 'GET', + ) + ) + ->submit(); + + if ( is_wp_error( $response ) ) { + return array(); + } + + if ( $bridge->endpoint === '/v3/contacts/doubleOptinConfirmation' ) { + $fields = array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'includeListIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + ), + 'required' => true, + ), + array( + 'name' => 'excludeListIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + ), + ), + array( + 'name' => 'templateId', + 'schema' => array( 'type' => 'integer' ), + 'required' => true, + ), + array( + 'name' => 'redirectionUrl', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'attributes', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + ), + ); + } else { + $fields = array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'ext_id', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'emailBlacklisted', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'smsBlacklisted', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'listIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + ), + ), + array( + 'name' => 'updateEnabled', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'smtpBlacklistSender', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'attributes', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + ), + ); + } + + foreach ( $response['data']['attributes'] as $attribute ) { + $fields[] = array( + 'name' => 'attributes.' . $attribute['name'], + 'schema' => array( 'type' => 'string' ), + ); + } + + return $fields; + } else { + if ( ! preg_match( '/\/([a-z]+)$/', $bridge->endpoint, $matches ) ) { + return array(); + } + + $module = $matches[1]; + $response = $bridge + ->patch( + array( + 'name' => "brevo-{$module}-attributes", + 'endpoint' => "/v3/crm/attributes/{$module}", + 'method' => 'GET', + ) + ) + ->submit(); + + if ( is_wp_error( $response ) ) { + return array(); + } + + if ( $module === 'companies' ) { + $fields = array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'countryCode', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'linkedContactsIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + ), + ), + array( + 'name' => 'linkedDealsIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + ), + ), + array( + 'name' => 'attributes', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + ), + ); + } elseif ( $module === 'deals' ) { + $fields = array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'linkedDealsIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + ), + ), + array( + 'name' => 'linkedCompaniesIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + ), + ), + array( + 'name' => 'attributes', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + ), + ); + } + + foreach ( $response['data'] as $attribute ) { + switch ( $attribute['attributeTypeName'] ) { + case 'number': + $type = 'number'; + break; + case 'text': + $type = 'string'; + break; + case 'user': + $type = 'email'; + break; + case 'date': + $type = 'date'; + break; + default: + $type = 'string'; + } + + $fields[] = array( + 'name' => 'attributes.' . $attribute['internalName'], + 'schema' => array( 'type' => $type ), + ); + } + + return $fields; + } + } +} + +Brevo_Addon::setup(); diff --git a/forms-bridge/addons/brevo/class-brevo-form-bridge.php b/forms-bridge/addons/brevo/class-brevo-form-bridge.php new file mode 100644 index 00000000..87907adc --- /dev/null +++ b/forms-bridge/addons/brevo/class-brevo-form-bridge.php @@ -0,0 +1,73 @@ +get_error_data()['response']; + if ( + $error_response['response']['code'] !== 425 && + $error_response['response']['code'] !== 400 + ) { + return $response; + } + + $data = json_decode( $error_response['body'], true ); + if ( $data['code'] !== 'duplicate_parameter' ) { + return $response; + } + + if ( + ! isset( $payload['email'] ) || + strstr( $this->endpoint, '/v3/contacts' ) === false + ) { + return $response; + } + + $update_response = $this->patch( + array( + 'name' => 'brevo-update-contact-by-email', + 'endpoint' => "/v3/contacts/{$payload['email']}?identifierType=email_id", + 'method' => 'PUT', + ) + )->submit( $payload ); + + if ( is_wp_error( $update_response ) ) { + return $update_response; + } + + return $this->patch( + array( + 'name' => 'brevo-search-contact-by-email', + 'endpoint' => "/v3/contacts/{$payload['email']}", + 'method' => 'GET', + ) + )->submit( array( 'identifierType' => 'email_id' ) ); + } + + return $response; + } +} diff --git a/forms-bridge/addons/brevo/hooks.php b/forms-bridge/addons/brevo/hooks.php new file mode 100644 index 00000000..91263c60 --- /dev/null +++ b/forms-bridge/addons/brevo/hooks.php @@ -0,0 +1,166 @@ + array( + array( + 'ref' => '#backend', + 'name' => 'name', + 'description' => __( + 'Label of the Brevo API backend connection', + 'forms-bridge' + ), + 'default' => 'Brevo API', + ), + array( + 'ref' => '#backend', + 'name' => 'base_url', + 'value' => 'https://api.brevo.com', + ), + array( + 'ref' => '#backend/headers[]', + 'name' => 'api-key', + 'label' => __( 'API Key', 'forms-bridge' ), + 'description' => __( + 'Get it from your account', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + ), + 'bridge' => array( + 'method' => 'POST', + ), + 'backend' => array( + 'base_url' => 'https://api.brevo.com', + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'brevo-' ) !== 0 ) { + return $data; + } + + $get_index = fn( $name ) => array_search( + $name, + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + $index = array_search( + 'listIds', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = $data['bridge']['custom_fields'][ $index ]; + + for ( $i = 0; $i < count( $field['value'] ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "listIds[{$i}]", + 'value' => $field['value'][ $i ], + ); + + $data['bridge']['mutations'][0][] = array( + 'from' => "listIds[{$i}]", + 'to' => "listIds[{$i}]", + 'cast' => 'integer', + ); + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'includeListIds', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = $data['bridge']['custom_fields'][ $index ]; + + for ( $i = 0; $i < count( $field['value'] ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "includeListIds[{$i}]", + 'value' => $field['value'][ $i ], + ); + + $data['bridge']['mutations'][0][] = array( + 'from' => "includeListIds[{$i}]", + 'to' => "includeListIds[{$i}]", + 'cast' => 'integer', + ); + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'redirectionUrl', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = &$data['bridge']['custom_fields'][ $index ]; + + $field['value'] = (string) filter_var( + (string) $field['value'], + FILTER_SANITIZE_URL + ); + + $parsed = parse_url( $field['value'] ); + + if ( ! isset( $parsed['host'] ) ) { + $site_url = get_site_url(); + + $field['value'] = + $site_url . + '/' . + preg_replace( '/^\/+/', '', $field['value'] ); + } + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/brevo/jobs/country-phone-code.php b/forms-bridge/addons/brevo/jobs/country-phone-code.php new file mode 100644 index 00000000..6b16f366 --- /dev/null +++ b/forms-bridge/addons/brevo/jobs/country-phone-code.php @@ -0,0 +1,58 @@ + __( 'Country phone code', 'forms-bridge' ), + 'description' => __( + 'Get a country by name and adds its phone prefix as the "countryCode" field on the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_brevo_country_phone_prefix', + 'input' => array( + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'countryCode', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); + +function forms_bridge_brevo_country_phone_prefix( $payload ) { + global $forms_bridge_country_phone_codes; + global $forms_bridge_iso2_countries; + global $forms_bridge_iso3_countries; + + $countries = array(); + foreach ( $forms_bridge_country_phone_codes as $phone_code => $country ) { + $countries[ $country ] = $phone_code; + } + + $country = $payload['country']; + + if ( isset( $forms_bridge_iso2_countries[ $country ] ) ) { + $country = $forms_bridge_iso2_countries[ $country ]; + } elseif ( isset( $forms_bridge_iso3_countries[ $country ] ) ) { + $country = $forms_bridge_iso3_countries[ $country ]; + } + + if ( isset( $countries[ $country ] ) ) { + $payload['countryCode'] = (int) $countries[ $country ]; + } else { + $payload['countryCode'] = null; + } + + return $payload; +} diff --git a/forms-bridge/addons/brevo/jobs/linked-company.php b/forms-bridge/addons/brevo/jobs/linked-company.php new file mode 100644 index 00000000..dff3ac03 --- /dev/null +++ b/forms-bridge/addons/brevo/jobs/linked-company.php @@ -0,0 +1,63 @@ + __( 'Linked company', 'forms-bridge' ), + 'description' => __( + 'Creates a new company and inserts its ID in the linkedCompaniesIds array field of the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_brevo_linked_company', + 'input' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'attributes', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + 'additionalProperties' => true, + ), + ), + array( + 'name' => 'countryCode', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'linkedContactsIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + 'additionalItems' => true, + ), + ), + ), + 'output' => array( + array( + 'name' => 'linkedCompaniesIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_brevo_linked_company( $payload, $bridge ) { + $company = forms_bridge_brevo_create_company( $payload, $bridge ); + + if ( is_wp_error( $company ) ) { + return $company; + } + + $payload['linkedCompaniesIds'][] = $company['id']; + + return $payload; +} diff --git a/forms-bridge/addons/brevo/jobs/linked-contact.php b/forms-bridge/addons/brevo/jobs/linked-contact.php new file mode 100644 index 00000000..933fc279 --- /dev/null +++ b/forms-bridge/addons/brevo/jobs/linked-contact.php @@ -0,0 +1,79 @@ + __( 'Linked contact', 'forms-bridge' ), + 'description' => __( + 'Creates a new contact and inserts its ID in the linkedContactsIds array field of the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_brevo_linked_contact', + 'input' => array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'ext_id', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'attributes', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + 'additionalProperties' => true, + ), + ), + array( + 'name' => 'emailBlacklisted', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'smsBlacklisted', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'listIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + 'additionalItems' => true, + ), + ), + array( + 'name' => 'updateEnabled', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'smtpBlacklistSender', + 'schema' => array( 'type' => 'boolean' ), + ), + ), + 'output' => array( + array( + 'name' => 'linkedContactsIds', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_brevo_linked_contact( $payload, $bridge ) { + $contact = forms_bridge_brevo_create_contact( $payload, $bridge ); + + if ( is_wp_error( $contact ) ) { + return $contact; + } + + $payload['linkedContactsIds'][] = $contact['id']; + + return $payload; +} diff --git a/forms-bridge/addons/brevo/jobs/skip-subscription.php b/forms-bridge/addons/brevo/jobs/skip-subscription.php new file mode 100644 index 00000000..95ec3b13 --- /dev/null +++ b/forms-bridge/addons/brevo/jobs/skip-subscription.php @@ -0,0 +1,30 @@ + __( 'Skip subscription', 'forms-bridge' ), + 'description' => __( + 'Skip subscription if the brevo field is not true', + 'forms-bridge' + ), + 'method' => 'forms_bridge_brevo_skip_subscription', + 'input' => array( + array( + 'name' => 'brevo', + 'schema' => array( 'type' => 'boolean' ), + 'required' => true, + ), + ), + 'output' => array(), +); + +function forms_bridge_brevo_skip_subscription( $payload ) { + if ( $payload['brevo'] != true ) { + return; + } + + return $payload; +} diff --git a/forms-bridge/addons/brevo/jobs/sync-woo-products.php b/forms-bridge/addons/brevo/jobs/sync-woo-products.php new file mode 100644 index 00000000..c125e616 --- /dev/null +++ b/forms-bridge/addons/brevo/jobs/sync-woo-products.php @@ -0,0 +1,143 @@ + __( 'Sync woo products', 'forms-bridge' ), + 'description' => __( + 'Synchronize WooCommerce orders products with the eCommerce module of Brevo', + 'forms-bridge' + ), + 'method' => 'forms_bridge_brevo_sync_woo_products', + 'input' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + 'required' => array( 'id', 'name', 'price' ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_brevo_sync_woo_products( $payload, $bridge ) { + $response = $bridge + ->patch( + array( + 'name' => 'brevo-search-products', + 'endpoint' => '/v3/products', + 'method' => 'GET', + ) + ) + ->submit( + array( + 'offset' => 0, + 'order' => 'desc', + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + foreach ( $payload['line_items'] as $line_item ) { + $product = null; + foreach ( $response['data']['products'] as $candidate ) { + if ( $candidate['id'] == $line_item['product_id'] ) { + $product = $candidate; + break; + } + } + + if ( ! $product ) { + $product = array( + 'updateEnabled' => false, + 'id' => (string) $line_item['product_id'], + 'name' => $line_item['product']['name'], + 'price' => $line_item['product']['price'], + 'stock' => $line_item['product']['stock_quantity'], + ); + + if ( ! empty( $line_item['product']['parent_id'] ) ) { + $product['parent_id'] = $line_item['product']['parent_id']; + } + + if ( ! empty( $line_item['product']['sku'] ) ) { + $product['sku'] = $line_item['product']['sku']; + } + + $product_response = $bridge + ->patch( + array( + 'name' => 'brevo-sync-woo-product', + 'method' => 'POST', + 'endpoint' => '/v3/products', + ) + ) + ->submit( $product ); + + if ( is_wp_error( $product_response ) ) { + return $product_response; + } + } + } + + return $payload; +} diff --git a/forms-bridge/addons/brevo/templates/companies.php b/forms-bridge/addons/brevo/templates/companies.php new file mode 100644 index 00000000..af46e814 --- /dev/null +++ b/forms-bridge/addons/brevo/templates/companies.php @@ -0,0 +1,166 @@ + __( 'Companies', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into companies linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/companies', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'owner', + 'label' => __( 'Owner email', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the company contact', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/organization/invited/users', + 'finger' => 'users[].email', + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Companies', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Companies', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'company_name', + 'label' => __( 'Company', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country ) { + return array( + 'value' => $country, + 'label' => $country, + ); + }, + array_values( $forms_bridge_country_phone_codes ) + ), + 'required' => true, + ), + array( + 'name' => 'phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'website', + 'label' => __( 'Website', 'forms-bridge' ), + 'type' => 'url', + ), + array( + 'name' => 'industry', + 'label' => __( 'Industry', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'fname', + 'label' => __( 'Your first name', 'forms-bridge' ), + 'type' => 'text', + 'required' => false, + ), + array( + 'name' => 'lname', + 'label' => __( 'Your last name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'backend' => array( + 'base_url' => 'https://api.brevo.com', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/companies', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'fname', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => 'lname', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'country', + 'cast' => 'null', + ), + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'phone', + 'to' => 'attributes.phone_number', + 'cast' => 'string', + ), + array( + 'from' => 'website', + 'to' => 'attributes.domain', + 'cast' => 'string', + ), + array( + 'from' => 'industry', + 'to' => 'attributes.industry', + 'cast' => 'string', + ), + array( + 'from' => '?owner', + 'to' => 'attributes.owner', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( 'linked-contact', 'country-phone-code' ), + ), +); diff --git a/forms-bridge/addons/brevo/templates/company-deals.php b/forms-bridge/addons/brevo/templates/company-deals.php new file mode 100644 index 00000000..3dec9e56 --- /dev/null +++ b/forms-bridge/addons/brevo/templates/company-deals.php @@ -0,0 +1,220 @@ + __( 'Company deals', 'forms-bridge' ), + 'description' => __( + 'Quotation form templates. The resulting bridge will convert form submissions into deals on the sales pipeline linked new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/crm/deals', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'deal_name', + 'label' => __( 'Deal name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'deal_owner', + 'label' => __( 'Owner email', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the deal', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/organization/invited/users', + 'finger' => 'users[].email', + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'pipeline', + 'label' => __( 'Pipeline', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/crm/pipeline/details/all', + 'finger' => array( + 'value' => '[].pipeline', + 'label' => '[].pipeline_name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'amount', + 'label' => __( 'Deal amount', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company deals', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Company deals', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'company_name', + 'label' => __( 'Company', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country ) { + return array( + 'value' => $country, + 'label' => $country, + ); + }, + array_values( $forms_bridge_country_phone_codes ) + ), + 'required' => true, + ), + array( + 'name' => 'phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'website', + 'label' => __( 'Website', 'forms-bridge' ), + 'type' => 'url', + ), + array( + 'name' => 'industry', + 'label' => __( 'Industry', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'fname', + 'label' => __( 'Your first name', 'forms-bridge' ), + 'type' => 'text', + 'required' => false, + ), + array( + 'name' => 'lname', + 'label' => __( 'Your last name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/crm/deals', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'fname', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => 'lname', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'country', + 'cast' => 'null', + ), + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'phone', + 'to' => 'attributes.phone_number', + 'cast' => 'string', + ), + array( + 'from' => 'website', + 'to' => 'attributes.domain', + 'cast' => 'string', + ), + array( + 'from' => 'industry', + 'to' => 'attributes.industry', + 'cast' => 'string', + ), + array( + 'from' => 'deal_owner', + 'to' => 'attributes.owner', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'deal_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?pipeline', + 'to' => 'attributes.pipeline', + 'cast' => 'string', + ), + array( + 'from' => 'deal_owner', + 'to' => 'attributes.deal_owner', + 'cast' => 'string', + ), + array( + 'from' => '?amount', + 'to' => 'attributes.amount', + 'cast' => 'number', + ), + ), + ), + 'workflow' => array( + 'linked-contact', + 'country-phone-code', + 'linked-company', + ), + ), + 'backend' => array( + 'base_url' => 'https://api.brevo.com', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), +); diff --git a/forms-bridge/addons/brevo/templates/contacts-doi.php b/forms-bridge/addons/brevo/templates/contacts-doi.php new file mode 100644 index 00000000..4b82bf1b --- /dev/null +++ b/forms-bridge/addons/brevo/templates/contacts-doi.php @@ -0,0 +1,125 @@ + __( 'Contacts DOI', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions with a double opt-in confirmation check.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/contacts/doubleOptinConfirmation', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'includeListIds', + 'label' => __( 'Segments', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/contacts/lists', + 'finger' => array( + 'value' => 'lists[].id', + 'label' => 'lists[].name', + ), + ), + 'is_multi' => true, + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'templateId', + 'label' => __( 'Double opt-in template', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/smtp/templates', + 'finger' => array( + 'value' => 'templates[].id', + 'label' => 'templates[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'redirectionUrl', + 'label' => __( 'Redirection URL', 'forms-bridge' ), + 'type' => 'text', + 'description' => __( + 'URL of the web page that user will be redirected to after clicking on the double opt in URL', + 'forms-bridge' + ), + 'required' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Contacts DOI', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Contacts DOI', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'fname', + 'label' => __( 'Your first name', 'forms-bridge' ), + 'type' => 'text', + 'required' => false, + ), + array( + 'name' => 'lname', + 'label' => __( 'Your last name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'backend' => array( + 'base_url' => 'https://api.brevo.com', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/contacts/doubleOptinConfirmation', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'templateId', + 'to' => 'templateId', + 'cast' => 'integer', + ), + array( + 'from' => 'fname', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => 'lname', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/brevo/templates/contacts.php b/forms-bridge/addons/brevo/templates/contacts.php new file mode 100644 index 00000000..a52c4550 --- /dev/null +++ b/forms-bridge/addons/brevo/templates/contacts.php @@ -0,0 +1,96 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/contacts', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'listIds', + 'endpoint' => '/v3/contacts/lists', + 'label' => __( 'Segments', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/contacts/lists', + 'finger' => array( + 'value' => 'lists[].id', + 'label' => 'lists[].name', + ), + ), + 'is_multi' => true, + 'required' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Contacts', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Contacts', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'fname', + 'label' => __( 'Your first name', 'forms-bridge' ), + 'type' => 'text', + 'required' => false, + ), + array( + 'name' => 'lname', + 'label' => __( 'Your last name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'backend' => array( + 'base_url' => 'https://api.brevo.com', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/contacts', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'fname', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => 'lname', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/brevo/templates/deals.php b/forms-bridge/addons/brevo/templates/deals.php new file mode 100644 index 00000000..c31a3eb2 --- /dev/null +++ b/forms-bridge/addons/brevo/templates/deals.php @@ -0,0 +1,147 @@ + __( 'Deals', 'forms-bridge' ), + 'description' => __( + 'Leads form templates. The resulting bridge will convert form submissions into deals on the sales pipeline linked new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/crm/deals', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'deal_name', + 'label' => __( 'Deal name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'deal_owner', + 'label' => __( 'Owner email', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the deal', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/organization/invited/users', + 'finger' => 'users[].email', + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'pipeline', + 'label' => __( 'Pipeline', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/crm/pipeline/details/all', + 'finger' => array( + 'value' => '[].pipeline', + 'label' => '[].pipeline_name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'amount', + 'label' => __( 'Deal amount', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Deals', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Deals', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'fname', + 'label' => __( 'Your first name', 'forms-bridge' ), + 'type' => 'text', + 'required' => false, + ), + array( + 'name' => 'lname', + 'label' => __( 'Your last name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/crm/deals', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'fname', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => 'lname', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'deal_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?pipeline', + 'to' => 'attributes.pipeline', + 'cast' => 'string', + ), + array( + 'from' => 'deal_owner', + 'to' => 'attributes.deal_owner', + 'cast' => 'string', + ), + array( + 'from' => '?amount', + 'to' => 'attributes.amount', + 'cast' => 'number', + ), + ), + ), + 'workflow' => array( 'linked-contact' ), + ), + 'backend' => array( + 'base_url' => 'https://api.brevo.com', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), +); diff --git a/forms-bridge/addons/brevo/templates/woo-contacts-doi.php b/forms-bridge/addons/brevo/templates/woo-contacts-doi.php new file mode 100644 index 00000000..938dff9b --- /dev/null +++ b/forms-bridge/addons/brevo/templates/woo-contacts-doi.php @@ -0,0 +1,305 @@ + __( 'Subscription DOI', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list with a double opt in check.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/contacts/doubleOptinConfirmation', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'includeListIds', + 'label' => __( 'Segments', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/contacts/lists', + 'finger' => array( + 'value' => 'lists[].id', + 'label' => 'lists[].name', + ), + ), + 'is_multi' => true, + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'templateId', + 'label' => __( 'Double opt-in template', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/smtp/templates', + 'finger' => array( + 'value' => 'templates[].id', + 'label' => 'templates[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'redirectionUrl', + 'label' => __( 'Redirection URL', 'forms-bridge' ), + 'type' => 'text', + 'description' => __( + 'URL of the web page that user will be redirected to after clicking on the double opt in URL', + 'forms-bridge' + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/contacts/doubleOptinConfirmation', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'templateId', + 'to' => 'templateId', + 'cast' => 'integer', + ), + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'ext_id', + 'cast' => 'string', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + array( + 'from' => 'billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/brevo/templates/woo-contacts.php b/forms-bridge/addons/brevo/templates/woo-contacts.php new file mode 100644 index 00000000..43952429 --- /dev/null +++ b/forms-bridge/addons/brevo/templates/woo-contacts.php @@ -0,0 +1,275 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/contacts', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'listIds', + 'label' => __( 'Segments', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/contacts/lists', + 'finger' => array( + 'value' => 'lists[].id', + 'label' => 'lists[].name', + ), + ), + 'is_multi' => true, + 'required' => true, + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/contacts', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'ext_id', + 'cast' => 'string', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + array( + 'from' => 'billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/brevo/templates/woo-orders.php b/forms-bridge/addons/brevo/templates/woo-orders.php new file mode 100644 index 00000000..0ef96699 --- /dev/null +++ b/forms-bridge/addons/brevo/templates/woo-orders.php @@ -0,0 +1,303 @@ + __( 'Woo Orders', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will synchronize WooCommerce with the Brevo eCommerce module.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/v3/orders/status', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'listIds', + 'label' => __( 'Segments', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/v3/contacts/lists', + 'finger' => array( + 'value' => 'lists[].id', + 'label' => 'lists[].name', + ), + ), + 'is_multi' => true, + 'required' => true, + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/v3/orders/status', + 'custom_fields' => array( + array( + 'name' => 'attributes.LANGUAGE', + 'value' => '$locale', + ), + array( + 'name' => 'createdAt', + 'value' => '$utc_date', + ), + array( + 'name' => 'updatedAt', + 'value' => '$utc_date', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'order_id', + 'cast' => 'string', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'amount', + 'cast' => 'number', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'ext_id', + 'cast' => 'string', + ), + array( + 'from' => 'ext_id', + 'to' => 'identifiers.ext_id', + 'cast' => 'copy', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'attributes.FNAME', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'attributes.LNAME', + 'cast' => 'string', + ), + array( + 'from' => 'billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'linkedContactsIds', + 'to' => 'linkedContactsIds', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'order_id', + 'to' => 'id', + 'cast' => 'string', + ), + array( + 'from' => 'line_items[].product_id', + 'to' => 'products[].productId', + 'cast' => 'string', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'products[].quantity', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'products[].price', + 'cast' => 'number', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + ), + 'workflow' => array( 'linked-contact', 'sync-woo-products' ), + ), +); diff --git a/forms-bridge/addons/dolibarr/api.php b/forms-bridge/addons/dolibarr/api.php new file mode 100644 index 00000000..9f5d9c30 --- /dev/null +++ b/forms-bridge/addons/dolibarr/api.php @@ -0,0 +1,416 @@ + fn( $v ) => "(t.email:=:'{$v}')", + 'firstname' => fn( $v ) => "(t.firstname:like:'{$v}')", + 'lastname' => fn( $v ) => "(t.lastname:like:'{$v}')", + // 'socid' => fn ($v) => "(t.fk_soc:=:{$v})", + ); + + foreach ( $search_fields as $field => $filter ) { + if ( isset( $payload[ $field ] ) ) { + $sqlfilters[] = $filter( $payload[ $field ] ); + } + } + + if ( empty( $sqlfilters ) ) { + return; + } + + $sqlfilters = implode( ' and ', $sqlfilters ); + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-search-contact', + 'endpoint' => '/api/index.php/contacts', + 'method' => 'GET', + ) + ) + ->submit( + array( + 'sortfield' => 't.rowid', + 'sortorder' => 'DESC', + 'limit' => '1', + 'properties' => 'id', + 'sqlfilters' => $sqlfilters, + ) + ); + + if ( is_wp_error( $response ) ) { + $error_data = $response->get_error_data(); + $response_code = $error_data['response']['response']['code']; + + if ( $response_code !== 404 ) { + return $response; + } + } + + if ( is_wp_error( $response ) ) { + return; + } + + return $response['data'][0]; +} + +function forms_bridge_dolibarr_search_thirdparty( $payload, $bridge ) { + $sqlfilters = array( "(t.nom:like:'{$payload['name']}')" ); + + $search_fields = array( + // 'typent_id' => fn ($v) => "(t.fk_typent:=:{$v})", + 'tva_intra' => fn( $v ) => "(t.tva_intra:=:'{$v}')", + 'idprof1' => fn( $v ) => "(t.siren:=:'{$v}')", + 'email' => fn( $v ) => "(t.email:=:'{$v}')", + 'code_client' => fn( $v ) => "(t.code_client:=:'{$v}')", + ); + + foreach ( $search_fields as $field => $filter ) { + if ( isset( $payload[ $field ] ) ) { + $sqlfilters[] = $filter( $payload[ $field ] ); + } + } + + if ( empty( $sqlfilters ) ) { + return; + } + + $sqlfilters = implode( ' or ', $sqlfilters ); + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-search-thirdparty', + 'endpoint' => '/api/index.php/thirdparties', + 'method' => 'GET', + ) + ) + ->submit( + array( + 'sortfield' => 't.rowid', + 'sortorder' => 'DESC', + 'limit' => '1', + 'properties' => 'id,code_client', + 'sqlfilters' => $sqlfilters, + ) + ); + + if ( is_wp_error( $response ) ) { + $error_data = $response->get_error_data(); + $response_code = $error_data['response']['response']['code'] ?? null; + + if ( $response_code !== 404 ) { + return $response; + } + } + + if ( is_wp_error( $response ) ) { + return; + } + + return $response['data'][0]; +} + +function forms_bridge_dolibarr_get_next_code_client( $payload, $bridge ) { + $required = isset( $payload['client'] ) && $payload['client'] != '0'; + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-get-next-code-client', + 'endpoint' => '/api/index.php/thirdparties', + 'method' => 'GET', + ) + ) + ->submit( + array( + 'sortfield' => 't.rowid', + 'sortorder' => 'DESC', + 'properties' => 'code_client', + 'limit' => 1, + ) + ); + + if ( is_wp_error( $response ) ) { + if ( ! $required ) { + $payload['code_client'] = ''; + return $payload; + } + + return $response; + } + + $previous_code_client = $response['data'][0]['code_client']; + + try { + [$prefix, $number] = explode( '-', $previous_code_client ); + + if ( empty( $number ) ) { + $number = $prefix; + $prefix = ''; + } + + $next = strval( $number + 1 ); + while ( strlen( $next ) < strlen( $number ) ) { + $next = '0' . $next; + } + } catch ( Error ) { + if ( ! $required ) { + $payload['code_client'] = ''; + return $payload; + } + + return new WP_Error( 'unkown_code_format' ); + } + + if ( preg_match( '/^CU[0-9]{4}$/', $prefix ) ) { + $prefix = 'CU' . date( 'y' ) . date( 'm' ); + } elseif ( preg_match( '/^CU[0-9]{2}$/', $prefix ) ) { + $prefix = 'CU' . date( 'y' ); + } + + if ( empty( $prefix ) ) { + return $next; + } + + return $prefix . '-' . $next; +} + +function forms_bridge_dolibarr_get_next_project_ref( $payload, $bridge ) { + $response = $bridge + ->patch( + array( + 'name' => 'dolibar-get-next-project-ref', + 'endpoint' => '/api/index.php/projects', + 'method' => 'GET', + ) + ) + ->submit( + array( + 'sortfield' => 't.rowid', + 'sortorder' => 'DESC', + 'properties' => 'ref', + 'limit' => 1, + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $previous_project_ref = $response['data'][0]['ref']; + + [$prefix, $number] = explode( '-', $previous_project_ref ); + + $next = strval( $number + 1 ); + while ( strlen( $next ) < strlen( $number ) ) { + $next = '0' . $next; + } + + $prefix = 'PJ' . date( 'y' ) . date( 'm' ); + return $prefix . '-' . $next; +} + +function forms_bridge_dolibarr_update_contact( $payload, $bridge ) { + return forms_bridge_dolibarr_create_contact( $payload, $bridge, true ); +} + +function forms_bridge_dolibarr_create_contact( + $payload, + $bridge, + $update = false +) { + if ( ! $update ) { + $contact = forms_bridge_dolibarr_search_contact( $payload, $bridge ); + + if ( ! is_wp_error( $contact ) && isset( $contact['id'] ) ) { + $payload['id'] = $contact['id']; + return forms_bridge_dolibarr_update_contact( $payload, $bridge ); + } + } + + $contact = array( + 'lastname' => $payload['lastname'], + ); + + $contact_fields = array( + 'email', + 'firstname', + 'civility_code', + 'socid', + 'poste', + 'status', + 'note_public', + 'note_private', + 'address', + 'zip', + 'town', + 'country_id', + 'state_id', + 'region_id', + 'url', + 'no_email', + 'phone_pro', + 'phone_perso', + 'phone_mobile', + 'fax', + 'stcomm_id', + 'default_lang', + ); + + foreach ( $contact_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $contact[ $field ] = $payload[ $field ]; + } + } + + $method = 'POST'; + $endpoint = '/api/index.php/contacts'; + if ( $update && isset( $payload['id'] ) ) { + $endpoint .= '/' . $payload['id']; + $method = 'PUT'; + } + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-create-contact', + 'endpoint' => $endpoint, + 'method' => $method, + ) + ) + ->submit( $contact ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + if ( $method === 'POST' ) { + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-get-new-contact-data', + 'endpoint' => '/api/index.php/contacts/' . $response['data'], + 'method' => 'GET', + ) + ) + ->submit( array() ); + } + + return $response['data']; +} + +function forms_bridge_dolibarr_update_thirdparty( $payload, $bridge ) { + return forms_bridge_dolibarr_create_thirdparty( $payload, $bridge, true ); +} + +function forms_bridge_dolibarr_create_thirdparty( + $payload, + $bridge, + $update = false +) { + if ( ! $update ) { + $thirdparty = forms_bridge_dolibarr_search_thirdparty( + $payload, + $bridge + ); + + if ( ! is_wp_error( $thirdparty ) && isset( $thirdparty['id'] ) ) { + $payload['id'] = $thirdparty['id']; + + if ( ! empty( $thirdparty['code_client'] ) ) { + $payload['code_client'] = $thirdparty['code_client']; + } + + return forms_bridge_dolibarr_update_thirdparty( $payload, $bridge ); + } + } + + $thirdparty = array( + 'name' => $payload['name'], + ); + + $thirdparty_fields = array( + 'email', + 'idprof1', + 'idprof2', + 'tva_intra', + 'phone', + 'fax', + 'url', + 'zip', + 'town', + 'address', + 'region_id', + 'state_id', + 'country_id', + 'no_email', + 'typent_id', + 'stcomm_id', + 'parent', + 'client', + 'fournisseur', + 'code_client', + ); + + foreach ( $thirdparty_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $thirdparty[ $field ] = $payload[ $field ]; + } + } + + if ( ! isset( $thirdparty['code_client'] ) && ! $update ) { + $code_client = forms_bridge_dolibarr_get_next_code_client( + $payload, + $bridge + ); + if ( is_wp_error( $code_client ) ) { + return $code_client; + } + + $thirdparty['code_client'] = $code_client; + } + + $endpoint = '/api/index.php/thirdparties'; + $method = 'POST'; + + if ( $update && isset( $payload['id'] ) ) { + $endpoint .= '/' . $payload['id']; + $method = 'PUT'; + } + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-create-thirdparty', + 'endpoint' => $endpoint, + 'method' => $method, + ) + ) + ->submit( $thirdparty ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + if ( $method === 'POST' ) { + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-get-new-thirdparty-data', + 'endpoint' => + '/api/index.php/thirdparties/' . $response['data'], + 'method' => 'GET', + ) + ) + ->submit(); + } + + return $response['data']; +} diff --git a/addons/dolibarr/assets/logo.png b/forms-bridge/addons/dolibarr/assets/logo.png similarity index 100% rename from addons/dolibarr/assets/logo.png rename to forms-bridge/addons/dolibarr/assets/logo.png diff --git a/forms-bridge/addons/dolibarr/class-dolibarr-addon.php b/forms-bridge/addons/dolibarr/class-dolibarr-addon.php new file mode 100644 index 00000000..94d22604 --- /dev/null +++ b/forms-bridge/addons/dolibarr/class-dolibarr-addon.php @@ -0,0 +1,133 @@ + '__dolibarr-' . time(), + 'endpoint' => '/api/index.php/status', + 'method' => 'GET', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit(); + if ( is_wp_error( $response ) ) { + return false; + } + + $code = $response['data']['success']['code'] ?? null; + return 200 === $code; + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge = new Dolibarr_Form_Bridge( + array( + 'name' => '__dolibarr-' . time(), + 'endpoint' => $endpoint, + 'backend' => $backend, + 'method' => 'GET', + ) + ); + + return $bridge->submit(); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array + */ + public function get_endpoint_schema( $endpoint, $backend ) { + $bridge = new Dolibarr_Form_Bridge( + array( + 'name' => '__dolibarr-' . time(), + 'endpoint' => $endpoint, + 'backend' => $backend, + 'method' => 'GET', + ) + ); + + $response = $bridge->submit( array( 'limit' => 1 ) ); + + if ( is_wp_error( $response ) ) { + return array(); + } + + $entry = $response['data'][0] ?? null; + if ( ! $entry ) { + return array(); + } + + $fields = array(); + foreach ( $entry as $field => $value ) { + if ( wp_is_numeric_array( $value ) ) { + $type = 'array'; + } elseif ( is_array( $value ) ) { + $type = 'object'; + } elseif ( is_double( $value ) ) { + $type = 'number'; + } elseif ( is_int( $value ) ) { + $type = 'integer'; + } else { + $type = 'string'; + } + + $fields[] = array( + 'name' => $field, + 'schema' => array( 'type' => $type ), + ); + } + + return $fields; + } +} + +Dolibarr_Addon::setup(); diff --git a/forms-bridge/addons/dolibarr/class-dolibarr-form-bridge.php b/forms-bridge/addons/dolibarr/class-dolibarr-form-bridge.php new file mode 100644 index 00000000..61ecc930 --- /dev/null +++ b/forms-bridge/addons/dolibarr/class-dolibarr-form-bridge.php @@ -0,0 +1,17 @@ + array( + array( + 'ref' => '#backend', + 'name' => 'name', + 'default' => 'Dolibarr', + ), + array( + 'ref' => '#backend/headers[]', + 'name' => 'DOLAPIKEY', + 'label' => __( 'API key', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + ), + 'backend' => array( + 'name' => 'Dolibarr', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'dolibarr-' ) !== 0 ) { + return $data; + } + + $index = array_search( + 'no_email', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = &$data['bridge']['custom_fields'][ $index ]; + $field['value'] = $field['value'] ? '0' : '1'; + } + + $index = array_search( + 'fulldayevent', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $data['form']['fields'] = array_filter( + $data['form']['fields'], + function ( $field ) { + return ! in_array( + $field['name'], + array( + 'hour', + 'minute', + __( 'Hour', 'forms-bridge' ), + __( 'Minute', 'forms-bridge' ), + ), + true + ); + } + ); + + $index = array_search( + 'duration', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/dolibarr/jobs/appointment-attendee.php b/forms-bridge/addons/dolibarr/jobs/appointment-attendee.php new file mode 100644 index 00000000..89563136 --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/appointment-attendee.php @@ -0,0 +1,142 @@ + $contact['id'], + 'mandatory' => 0, + 'answer_status' => 0, + 'transparency' => 0, + ); + + return $payload; +} + +return array( + 'title' => __( 'Appointment attendee', 'forms-bridge' ), + 'description' => __( + 'Create a contact and binds it to the appointment as an attendee', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_appointment_attendee', + 'input' => array( + array( + 'name' => 'lastname', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'firstname', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'civility_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'status', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'note_public', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'note_private', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'address', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'town', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'state_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'region_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'phone_pro', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone_perso', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone_mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'fax', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'url', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'socid', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'poste', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'stcomm_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'no_email', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'socpeopleassigned', + 'schema' => array( + 'type' => 'array', + 'items' => array( + array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'string' ), + 'mandatory' => array( 'type' => 'string' ), + 'answer_status' => array( 'type' => 'string' ), + 'transparency' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/appointment-dates.php b/forms-bridge/addons/dolibarr/jobs/appointment-dates.php new file mode 100644 index 00000000..6a6a66a3 --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/appointment-dates.php @@ -0,0 +1,56 @@ +getTimestamp(); + $payload['datep'] = $timestamp; + $payload['duration'] = floatval( $payload['duration'] ?? 1 ); + $payload['datef'] = intval( $payload['duration'] * 3600 + $timestamp ); + + return $payload; +} + +return array( + 'title' => __( 'Appointment dates', 'forms-bridge' ), + 'description' => __( + 'Sets appointment start, end time and duration from datetime and duration fields.', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_appointment_dates', + 'input' => array( + array( + 'name' => 'date', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'duration', + 'schema' => array( 'type' => 'number' ), + ), + ), + 'output' => array( + array( + 'name' => 'datep', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'datef', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'duration', + 'schema' => array( 'type' => 'number' ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/contact-id.php b/forms-bridge/addons/dolibarr/jobs/contact-id.php new file mode 100644 index 00000000..d0b1e42d --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/contact-id.php @@ -0,0 +1,126 @@ + __( 'Contact', 'forms-bridge' ), + 'description' => __( + 'Creates a contact and adds its ID to the contact_ids field of the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_contact_ids', + 'input' => array( + array( + 'name' => 'lastname', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'firstname', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'civility_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'socid', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'poste', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'status', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'note_public', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'note_private', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'address', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'town', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'state_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'region_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'url', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'no_email', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'phone_pro', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone_perso', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone_mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'fax', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'stcomm_id', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'default_lang', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'contact_ids', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + 'additionalItems' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/contact-socid.php b/forms-bridge/addons/dolibarr/jobs/contact-socid.php new file mode 100644 index 00000000..e5f88f8c --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/contact-socid.php @@ -0,0 +1,126 @@ + __( 'Third party', 'forms-bridge' ), + 'description' => __( + 'Creates a new third party and returns its ID as the socid of the payload.', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_thirdparty_socid', + 'input' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'code_client', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'idprof1', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'idprof2', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'tva_intra', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'fax', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'url', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'address', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'town', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'region_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'state_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'typent_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'status', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'client', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'fournisseur', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'stcomm_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'note_public', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'no_email', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'parent', + 'schema' => array( 'type' => 'integer' ), + ), + ), + 'output' => array( + array( + 'name' => 'socid', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/country-id.php b/forms-bridge/addons/dolibarr/jobs/country-id.php new file mode 100644 index 00000000..09c8e223 --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/country-id.php @@ -0,0 +1,63 @@ +patch( + array( + 'name' => 'dolibarr-get-country-id', + 'method' => 'GET', + 'endpoint' => + '/api/index.php/setup/dictionary/countries/byCode/' . + $payload['country'], + ) + ) + ->submit(); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $payload['country_id'] = $response['data']['id']; + return $payload; +} + +return array( + 'title' => __( 'Country ID', 'forms-bridge' ), + 'description' => __( + 'Gets country_id value from country code and replace it on the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_country_id_from_code', + 'input' => array( + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'country_id', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'country_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/next-client-code.php b/forms-bridge/addons/dolibarr/jobs/next-client-code.php new file mode 100644 index 00000000..8c215f99 --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/next-client-code.php @@ -0,0 +1,35 @@ + __( 'Next code client', 'forms-brige' ), + 'description' => __( + 'Query for the next valid thirdparty code client', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_next_code_client', + 'input' => array(), + 'output' => array( + array( + 'name' => 'code_client', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/next-project-ref.php b/forms-bridge/addons/dolibarr/jobs/next-project-ref.php new file mode 100644 index 00000000..5d3d8b4a --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/next-project-ref.php @@ -0,0 +1,32 @@ + __( 'Next project ref', 'forms-brige' ), + 'description' => __( 'Query for the next valid project ref', 'forms-bridge' ), + 'method' => 'forms_bridge_dolibarr_next_project_ref', + 'input' => array(), + 'output' => array( + array( + 'name' => 'ref', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/products-by-ref.php b/forms-bridge/addons/dolibarr/jobs/products-by-ref.php new file mode 100644 index 00000000..46e2bfb8 --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/products-by-ref.php @@ -0,0 +1,90 @@ + __( 'Products by reference', 'forms-bridge' ), + 'description' => __( + 'Search for products on Dolibarr based on a list of references and returns its IDs.', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_search_products_by_ref', + 'input' => array( + array( + 'name' => 'product_refs', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'additionalItems' => true, + ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'fk_products', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_dolibarr_search_products_by_ref( $payload, $bridge ) { + $sqlfilters = array(); + $refs = (array) $payload['product_refs']; + foreach ( $refs as $ref ) { + $ref = trim( $ref ); + $sqlfilters[] = "(t.ref:=:'{$ref}')"; + } + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-search-products-by-ref', + 'endpoint' => '/api/index.php/products', + 'method' => 'GET', + ) + ) + ->submit( + array( + 'properties' => 'id,ref', + 'sqlfilters' => implode( ' or ', $sqlfilters ), + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $fk_products = array(); + foreach ( $refs as $ref ) { + foreach ( $response['data'] as $product ) { + if ( $product['ref'] === $ref ) { + $fk_products[] = $product['id']; + break; + } + } + } + + if ( count( $fk_products ) !== count( $payload['product_refs'] ) ) { + return new WP_Error( + 'product_search_error', + __( + 'Inconsistencies between amount of found products and search references', + 'forms-bridge' + ), + array( + 'response' => $response, + 'internal_refs' => $payload['product_refs'], + ) + ); + } + + $payload['fk_products'] = $fk_products; + return $payload; +} diff --git a/forms-bridge/addons/dolibarr/jobs/skip-if-contact-exists.php b/forms-bridge/addons/dolibarr/jobs/skip-if-contact-exists.php new file mode 100644 index 00000000..d22e7bfc --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/skip-if-contact-exists.php @@ -0,0 +1,78 @@ + __( 'Skip if contact exists', 'forms-bridge' ), + 'description' => __( + 'Aborts form submission if the contact exists', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_skip_contact', + 'input' => array( + array( + 'name' => 'email', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'firstname', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'lastname', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'socid', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'email' ), + ), + array( + 'name' => 'firstname', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'firstname' ), + ), + array( + 'name' => 'lastname', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'lastname' ), + ), + array( + 'name' => 'socid', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'socid' ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/skip-if-thirdparty-exists.php b/forms-bridge/addons/dolibarr/jobs/skip-if-thirdparty-exists.php new file mode 100644 index 00000000..6471738e --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/skip-if-thirdparty-exists.php @@ -0,0 +1,73 @@ + __( 'Skip if thirdparty exists', 'forms-bridge' ), + 'description' => __( + 'Aborts form submission if a thirdparty already exists', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_skip_thirdparty', + 'input' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'idprof1', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'name' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'email' ), + ), + array( + 'name' => 'idprof1', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'idprof1' ), + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/jobs/sync-products-by-ref.php b/forms-bridge/addons/dolibarr/jobs/sync-products-by-ref.php new file mode 100644 index 00000000..a5c69c0b --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/sync-products-by-ref.php @@ -0,0 +1,152 @@ + __( 'Sync woo products', 'forms-bridge' ), + 'description' => __( + 'Search for products from the WooCommerce order by sku on Dolibarr and creates new ones if someone does not exists', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_sync_products_by_ref', + 'input' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + 'required' => array( 'sku', 'name', 'price' ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_dolibarr_sync_products_by_ref( $payload, $bridge ) { + $product_refs = array(); + foreach ( $payload['line_items'] as $line_item ) { + if ( empty( $line_item['product']['sku'] ) ) { + return new WP_Error( + "SKU is required on product {$line_item['product']['name']}" + ); + } + + $product_refs[] = $line_item['product']['sku']; + } + + $sqlfilters = array(); + foreach ( $product_refs as $ref ) { + $ref = trim( $ref ); + $sqlfilters[] = "(t.ref:=:'{$ref}')"; + } + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-search-products-by-ref', + 'endpoint' => '/api/index.php/products', + 'method' => 'GET', + ) + ) + ->submit( + array( + 'properties' => 'id,ref', + 'sqlfilters' => implode( ' or ', $sqlfilters ), + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + foreach ( $payload['line_items'] as $line_item ) { + $product = null; + foreach ( $response['data'] as $candidate ) { + if ( $candidate['ref'] === $line_item['product']['sku'] ) { + $product = $candidate; + break; + } + } + + if ( ! $product ) { + $product_response = $bridge + ->patch( + array( + 'name' => 'dolibarr-sync-product-by-ref', + 'endpoint' => '/api/index.php/products', + 'method' => 'POST', + ) + ) + ->submit( + array( + 'label' => $line_item['product']['name'], + 'ref' => $line_item['product']['sku'], + 'status' => '1', + 'type' => '0', + 'price' => $line_item['product']['price'], + ) + ); + + if ( is_wp_error( $product_response ) ) { + return $product_response; + } + } + } + + return $payload; +} diff --git a/forms-bridge/addons/dolibarr/jobs/validate-order.php b/forms-bridge/addons/dolibarr/jobs/validate-order.php new file mode 100644 index 00000000..bba706eb --- /dev/null +++ b/forms-bridge/addons/dolibarr/jobs/validate-order.php @@ -0,0 +1,67 @@ + __( 'Validate order', 'forms-bridge' ), + 'description' => __( + 'Add a callback to the bridge submission to validate the order after its creation', + 'forms-bridge' + ), + 'method' => 'forms_bridge_dolibarr_enqueue_order_validation', + 'input' => array(), + 'output' => array(), +); + +function forms_bridge_dolibarr_validate_order( + $bridge, + $response, + $payload, + $attachments +) { + remove_action( + 'forms_bridge_after_submission', + 'forms_bridge_dolibarr_validate_order', + 10, + 4 + ); + + $order_id = intval( $response['data'] ?? null ); + + if ( empty( $order_id ) ) { + return; + } + + $response = $bridge + ->patch( + array( + 'name' => 'dolibarr-validate-order', + 'method' => 'POST', + 'endpoint' => "/api/index.php/orders/{$order_id}/validate", + ) + ) + ->submit(); + + if ( is_wp_error( $response ) ) { + do_action( + 'forms_bridge_on_failure', + $response, + $bridge, + $payload, + $attachments + ); + } +} + +function forms_bridge_dolibarr_enqueue_order_validation( $payload ) { + add_action( + 'forms_bridge_after_submission', + 'forms_bridge_dolibarr_validate_order', + 10, + 4 + ); + + return $payload; +} diff --git a/forms-bridge/addons/dolibarr/state-codes.php b/forms-bridge/addons/dolibarr/state-codes.php new file mode 100644 index 00000000..fc871773 --- /dev/null +++ b/forms-bridge/addons/dolibarr/state-codes.php @@ -0,0 +1,7277 @@ + '60', + 'label' => 'AD-002 - Canillo', + ), + array( + 'value' => '61', + 'label' => 'AD-003 - Encamp', + ), + array( + 'value' => '62', + 'label' => 'AD-004 - La Massana', + ), + array( + 'value' => '63', + 'label' => 'AD-005 - Ordino', + ), + array( + 'value' => '64', + 'label' => 'AD-006 - Sant Julià de Lòria', + ), + array( + 'value' => '65', + 'label' => 'AD-007 - Andorra la Vella', + ), + array( + 'value' => '66', + 'label' => 'AD-008 - Escaldes-Engordany', + ), + array( + 'value' => '1684', + 'label' => 'AE-1 - Abu Dhabi', + ), + array( + 'value' => '1685', + 'label' => 'AE-2 - Dubai', + ), + array( + 'value' => '1686', + 'label' => 'AE-3 - Ajman', + ), + array( + 'value' => '1687', + 'label' => 'AE-4 - Fujairah', + ), + array( + 'value' => '1688', + 'label' => 'AE-5 - Ras al-Khaimah', + ), + array( + 'value' => '1689', + 'label' => 'AE-6 - Sharjah', + ), + array( + 'value' => '1690', + 'label' => 'AE-7 - Umm al-Quwain', + ), + array( + 'value' => '67', + 'label' => 'AO-ABO - Bengo', + ), + array( + 'value' => '68', + 'label' => 'AO-BGU - Benguela', + ), + array( + 'value' => '69', + 'label' => 'AO-BIE - Bié', + ), + array( + 'value' => '70', + 'label' => 'AO-CAB - Cabinda', + ), + array( + 'value' => '71', + 'label' => 'AO-CCU - Kuando Kubango', + ), + array( + 'value' => '74', + 'label' => 'AO-CNN - Cunene', + ), + array( + 'value' => '72', + 'label' => 'AO-CNO - Kwanza Norte', + ), + array( + 'value' => '73', + 'label' => 'AO-CUS - Kwanza Sul', + ), + array( + 'value' => '75', + 'label' => 'AO-HUA - Huambo', + ), + array( + 'value' => '76', + 'label' => 'AO-HUI - Huila', + ), + array( + 'value' => '78', + 'label' => 'AO-LNO - Lunda-Norte', + ), + array( + 'value' => '79', + 'label' => 'AO-LSU - Lunda-Sul', + ), + array( + 'value' => '77', + 'label' => 'AO-LUA - Luanda', + ), + array( + 'value' => '80', + 'label' => 'AO-MAL - Malange', + ), + array( + 'value' => '81', + 'label' => 'AO-MOX - Moxico', + ), + array( + 'value' => '82', + 'label' => 'AO-NAM - Namíbe', + ), + array( + 'value' => '83', + 'label' => 'AO-UIG - Uíge', + ), + array( + 'value' => '84', + 'label' => 'AO-ZAI - Zaíre', + ), + array( + 'value' => '85', + 'label' => '2301 - Catamarca', + ), + array( + 'value' => '86', + 'label' => '2302 - Jujuy', + ), + array( + 'value' => '87', + 'label' => '2303 - Tucamán', + ), + array( + 'value' => '88', + 'label' => '2304 - Santiago del Estero', + ), + array( + 'value' => '89', + 'label' => '2305 - Salta', + ), + array( + 'value' => '90', + 'label' => '2306 - Chaco', + ), + array( + 'value' => '91', + 'label' => '2307 - Corrientes', + ), + array( + 'value' => '92', + 'label' => '2308 - Entre Ríos', + ), + array( + 'value' => '93', + 'label' => '2309 - Formosa', + ), + array( + 'value' => '94', + 'label' => '2310 - Santa Fe', + ), + array( + 'value' => '95', + 'label' => '2311 - La Rioja', + ), + array( + 'value' => '96', + 'label' => '2312 - Mendoza', + ), + array( + 'value' => '97', + 'label' => '2313 - San Juan', + ), + array( + 'value' => '98', + 'label' => '2314 - San Luis', + ), + array( + 'value' => '99', + 'label' => '2315 - Córdoba', + ), + array( + 'value' => '100', + 'label' => '2316 - Buenos Aires', + ), + array( + 'value' => '101', + 'label' => '2317 - Caba', + ), + array( + 'value' => '102', + 'label' => '2318 - La Pampa', + ), + array( + 'value' => '103', + 'label' => '2319 - Neuquén', + ), + array( + 'value' => '104', + 'label' => '2320 - Río Negro', + ), + array( + 'value' => '105', + 'label' => '2321 - Chubut', + ), + array( + 'value' => '106', + 'label' => '2322 - Santa Cruz', + ), + array( + 'value' => '107', + 'label' => '2323 - Tierra del Fuego', + ), + array( + 'value' => '108', + 'label' => '2324 - Islas Malvinas', + ), + array( + 'value' => '109', + 'label' => '2325 - Antártida', + ), + array( + 'value' => '110', + 'label' => '2326 - Misiones', + ), + array( + 'value' => '119', + 'label' => 'B - Burgenland', + ), + array( + 'value' => '120', + 'label' => 'K - Kärnten', + ), + array( + 'value' => '121', + 'label' => 'N - Niederösterreich', + ), + array( + 'value' => '122', + 'label' => 'O - Oberösterreich', + ), + array( + 'value' => '123', + 'label' => 'S - Salzburg', + ), + array( + 'value' => '124', + 'label' => 'ST - Steiermark', + ), + array( + 'value' => '125', + 'label' => 'T - Tirol', + ), + array( + 'value' => '126', + 'label' => 'V - Vorarlberg', + ), + array( + 'value' => '127', + 'label' => 'W - Wien', + ), + array( + 'value' => '115', + 'label' => 'ACT - Australia Capital Territory', + ), + array( + 'value' => '111', + 'label' => 'NSW - New South Wales', + ), + array( + 'value' => '118', + 'label' => 'NT - Northern Territory', + ), + array( + 'value' => '113', + 'label' => 'QLD - Queensland', + ), + array( + 'value' => '114', + 'label' => 'SA - South Australia', + ), + array( + 'value' => '116', + 'label' => 'TAS - Tasmania', + ), + array( + 'value' => '112', + 'label' => 'VIC - Victoria', + ), + array( + 'value' => '117', + 'label' => 'WA - Western Australia', + ), + array( + 'value' => '128', + 'label' => 'CC - Christ Church', + ), + array( + 'value' => '131', + 'label' => 'JA - Saint James', + ), + array( + 'value' => '129', + 'label' => 'SA - Saint Andrew', + ), + array( + 'value' => '133', + 'label' => 'SB - Saint Joseph', + ), + array( + 'value' => '137', + 'label' => 'SC - Saint Philip', + ), + array( + 'value' => '130', + 'label' => 'SG - Saint George', + ), + array( + 'value' => '132', + 'label' => 'SJ - Saint John', + ), + array( + 'value' => '134', + 'label' => 'SL - Saint Lucy', + ), + array( + 'value' => '135', + 'label' => 'SM - Saint Michael', + ), + array( + 'value' => '136', + 'label' => 'SP - Saint Peter', + ), + array( + 'value' => '138', + 'label' => 'ST - Saint Thomas', + ), + array( + 'value' => '139', + 'label' => '01 - Anvers', + ), + array( + 'value' => '140', + 'label' => '02 - Bruxelles-Capitale', + ), + array( + 'value' => '141', + 'label' => '03 - Brabant-Wallon', + ), + array( + 'value' => '142', + 'label' => '04 - Brabant-Flamand', + ), + array( + 'value' => '143', + 'label' => '05 - Flandre-Occidentale', + ), + array( + 'value' => '144', + 'label' => '06 - Flandre-Orientale', + ), + array( + 'value' => '145', + 'label' => '07 - Hainaut', + ), + array( + 'value' => '146', + 'label' => '08 - Liège', + ), + array( + 'value' => '147', + 'label' => '09 - Limbourg', + ), + array( + 'value' => '148', + 'label' => '10 - Luxembourg', + ), + array( + 'value' => '149', + 'label' => '11 - Namur', + ), + array( + 'value' => '1565', + 'label' => 'BI0001 - Bubanza', + ), + array( + 'value' => '1566', + 'label' => 'BI0002 - Gihanga', + ), + array( + 'value' => '1567', + 'label' => 'BI0003 - Musigati', + ), + array( + 'value' => '1568', + 'label' => 'BI0004 - Mpanda', + ), + array( + 'value' => '1569', + 'label' => 'BI0005 - Rugazi', + ), + array( + 'value' => '1570', + 'label' => 'BI0006 - Muha', + ), + array( + 'value' => '1571', + 'label' => 'BI0007 - Mukaza', + ), + array( + 'value' => '1572', + 'label' => 'BI0008 - Ntahangwa', + ), + array( + 'value' => '1573', + 'label' => 'BI0009 - Isale', + ), + array( + 'value' => '1574', + 'label' => 'BI0010 - Kabezi', + ), + array( + 'value' => '1575', + 'label' => 'BI0011 - Kanyosha', + ), + array( + 'value' => '1576', + 'label' => 'BI0012 - Mubimbi', + ), + array( + 'value' => '1577', + 'label' => 'BI0013 - Mugongomanga', + ), + array( + 'value' => '1578', + 'label' => 'BI0014 - Mukike', + ), + array( + 'value' => '1579', + 'label' => 'BI0015 - Mutambu', + ), + array( + 'value' => '1580', + 'label' => 'BI0016 - Mutimbuzi', + ), + array( + 'value' => '1581', + 'label' => 'BI0017 - Nyabiraba', + ), + array( + 'value' => '1582', + 'label' => 'BI0018 - Bururi', + ), + array( + 'value' => '1583', + 'label' => 'BI0019 - Matana', + ), + array( + 'value' => '1584', + 'label' => 'BI0020 - Mugamba', + ), + array( + 'value' => '1585', + 'label' => 'BI0021 - Rutovu', + ), + array( + 'value' => '1586', + 'label' => 'BI0022 - Songa', + ), + array( + 'value' => '1587', + 'label' => 'BI0023 - Vyanda', + ), + array( + 'value' => '1588', + 'label' => 'BI0024 - Cankuzo', + ), + array( + 'value' => '1589', + 'label' => 'BI0025 - Cendajuru', + ), + array( + 'value' => '1590', + 'label' => 'BI0026 - Gisagara', + ), + array( + 'value' => '1591', + 'label' => 'BI0027 - Kigamba', + ), + array( + 'value' => '1592', + 'label' => 'BI0028 - Mishiha', + ), + array( + 'value' => '1593', + 'label' => 'BI0029 - Buganda', + ), + array( + 'value' => '1594', + 'label' => 'BI0030 - Bukinanyana', + ), + array( + 'value' => '1595', + 'label' => 'BI0031 - Mabayi', + ), + array( + 'value' => '1596', + 'label' => 'BI0032 - Mugina', + ), + array( + 'value' => '1597', + 'label' => 'BI0033 - Murwi', + ), + array( + 'value' => '1598', + 'label' => 'BI0034 - Rugombo', + ), + array( + 'value' => '1599', + 'label' => 'BI0035 - Bugendana', + ), + array( + 'value' => '1600', + 'label' => 'BI0036 - Bukirasazi', + ), + array( + 'value' => '1601', + 'label' => 'BI0037 - Buraza', + ), + array( + 'value' => '1602', + 'label' => 'BI0038 - Giheta', + ), + array( + 'value' => '1603', + 'label' => 'BI0039 - Gishubi', + ), + array( + 'value' => '1604', + 'label' => 'BI0040 - Gitega', + ), + array( + 'value' => '1605', + 'label' => 'BI0041 - Itaba', + ), + array( + 'value' => '1606', + 'label' => 'BI0042 - Makebuko', + ), + array( + 'value' => '1607', + 'label' => 'BI0043 - Mutaho', + ), + array( + 'value' => '1608', + 'label' => 'BI0044 - Nyanrusange', + ), + array( + 'value' => '1609', + 'label' => 'BI0045 - Ryansoro', + ), + array( + 'value' => '1610', + 'label' => 'BI0046 - Bugenyuzi', + ), + array( + 'value' => '1611', + 'label' => 'BI0047 - Buhiga', + ), + array( + 'value' => '1612', + 'label' => 'BI0048 - Gihogazi', + ), + array( + 'value' => '1613', + 'label' => 'BI0049 - Gitaramuka', + ), + array( + 'value' => '1614', + 'label' => 'BI0050 - Mutumba', + ), + array( + 'value' => '1615', + 'label' => 'BI0051 - Nyabikere', + ), + array( + 'value' => '1616', + 'label' => 'BI0052 - Shombo', + ), + array( + 'value' => '1617', + 'label' => 'BI0053 - Butaganzwa', + ), + array( + 'value' => '1618', + 'label' => 'BI0054 - Gahombo', + ), + array( + 'value' => '1619', + 'label' => 'BI0055 - Gatara', + ), + array( + 'value' => '1620', + 'label' => 'BI0056 - Kabarore', + ), + array( + 'value' => '1621', + 'label' => 'BI0057 - Kayanza', + ), + array( + 'value' => '1622', + 'label' => 'BI0058 - Matongo', + ), + array( + 'value' => '1623', + 'label' => 'BI0059 - Muhanga', + ), + array( + 'value' => '1624', + 'label' => 'BI0060 - Muruta', + ), + array( + 'value' => '1625', + 'label' => 'BI0061 - Rango', + ), + array( + 'value' => '1626', + 'label' => 'BI0062 - Bugabira', + ), + array( + 'value' => '1627', + 'label' => 'BI0063 - Busoni', + ), + array( + 'value' => '1628', + 'label' => 'BI0064 - Bwambarangwe', + ), + array( + 'value' => '1629', + 'label' => 'BI0065 - Gitobe', + ), + array( + 'value' => '1630', + 'label' => 'BI0066 - Kirundo', + ), + array( + 'value' => '1631', + 'label' => 'BI0067 - Ntega', + ), + array( + 'value' => '1632', + 'label' => 'BI0068 - Vumbi', + ), + array( + 'value' => '1633', + 'label' => 'BI0069 - Kayogoro', + ), + array( + 'value' => '1634', + 'label' => 'BI0070 - Kibago', + ), + array( + 'value' => '1635', + 'label' => 'BI0071 - Mabanda', + ), + array( + 'value' => '1636', + 'label' => 'BI0072 - Makamba', + ), + array( + 'value' => '1637', + 'label' => 'BI0073 - Nyanza-Lac', + ), + array( + 'value' => '1638', + 'label' => 'BI0074 - Vugizo', + ), + array( + 'value' => '1639', + 'label' => 'BI0075 - Bukeye', + ), + array( + 'value' => '1640', + 'label' => 'BI0076 - Kiganda', + ), + array( + 'value' => '1641', + 'label' => 'BI0077 - Mbuye', + ), + array( + 'value' => '1642', + 'label' => 'BI0078 - Muramvya', + ), + array( + 'value' => '1643', + 'label' => 'BI0079 - Rutegama', + ), + array( + 'value' => '1644', + 'label' => 'BI0080 - Buhinyuza', + ), + array( + 'value' => '1645', + 'label' => 'BI0081 - Butihinda', + ), + array( + 'value' => '1646', + 'label' => 'BI0082 - Gashoho', + ), + array( + 'value' => '1647', + 'label' => 'BI0083 - Gasorwe', + ), + array( + 'value' => '1648', + 'label' => 'BI0084 - Giteranyi', + ), + array( + 'value' => '1649', + 'label' => 'BI0085 - Muyinga', + ), + array( + 'value' => '1650', + 'label' => 'BI0086 - Mwakiro', + ), + array( + 'value' => '1651', + 'label' => 'BI0087 - Bisoro', + ), + array( + 'value' => '1652', + 'label' => 'BI0088 - Gisozi', + ), + array( + 'value' => '1653', + 'label' => 'BI0089 - Kayokwe', + ), + array( + 'value' => '1654', + 'label' => 'BI0090 - Ndava', + ), + array( + 'value' => '1655', + 'label' => 'BI0091 - Nyabihanga', + ), + array( + 'value' => '1656', + 'label' => 'BI0092 - Rusaka', + ), + array( + 'value' => '1657', + 'label' => 'BI0093 - Busiga', + ), + array( + 'value' => '1658', + 'label' => 'BI0094 - Gashikanwa', + ), + array( + 'value' => '1659', + 'label' => 'BI0095 - Kiremba', + ), + array( + 'value' => '1660', + 'label' => 'BI0096 - Marangara', + ), + array( + 'value' => '1661', + 'label' => 'BI0097 - Mwumba', + ), + array( + 'value' => '1662', + 'label' => 'BI0098 - Ngozi', + ), + array( + 'value' => '1663', + 'label' => 'BI0099 - Nyamurenza', + ), + array( + 'value' => '1664', + 'label' => 'BI0100 - Ruhororo', + ), + array( + 'value' => '1665', + 'label' => 'BI0101 - Tangara', + ), + array( + 'value' => '1666', + 'label' => 'BI0102 - Bugarama', + ), + array( + 'value' => '1667', + 'label' => 'BI0103 - Burambi', + ), + array( + 'value' => '1668', + 'label' => 'BI0104 - Buyengero', + ), + array( + 'value' => '1669', + 'label' => 'BI0105 - Muhuta', + ), + array( + 'value' => '1670', + 'label' => 'BI0106 - Rumonge', + ), + array( + 'value' => '1671', + 'label' => 'BI0107 - Bukemba', + ), + array( + 'value' => '1672', + 'label' => 'BI0108 - Giharo', + ), + array( + 'value' => '1673', + 'label' => 'BI0109 - Gitanga', + ), + array( + 'value' => '1674', + 'label' => 'BI0110 - Mpinga-Kayove', + ), + array( + 'value' => '1675', + 'label' => 'BI0111 - Musongati', + ), + array( + 'value' => '1676', + 'label' => 'BI0112 - Rutana', + ), + array( + 'value' => '1677', + 'label' => 'BI0113 - Butaganzwa', + ), + array( + 'value' => '1678', + 'label' => 'BI0114 - Butezi', + ), + array( + 'value' => '1679', + 'label' => 'BI0115 - Bweru', + ), + array( + 'value' => '1680', + 'label' => 'BI0116 - Gisuru', + ), + array( + 'value' => '1681', + 'label' => 'BI0117 - Kinyinya', + ), + array( + 'value' => '1682', + 'label' => 'BI0118 - Nyabitsinda', + ), + array( + 'value' => '1683', + 'label' => 'BI0119 - Ruyigi', + ), + array( + 'value' => '1159', + 'label' => '001 - Belisario Boeto', + ), + array( + 'value' => '1160', + 'label' => '002 - Hernando Siles', + ), + array( + 'value' => '1161', + 'label' => '003 - Jaime Zudáñez', + ), + array( + 'value' => '1162', + 'label' => '004 - Juana Azurduy de Padilla', + ), + array( + 'value' => '1163', + 'label' => '005 - Luis Calvo', + ), + array( + 'value' => '1164', + 'label' => '006 - Nor Cinti', + ), + array( + 'value' => '1165', + 'label' => '007 - Oropeza', + ), + array( + 'value' => '1166', + 'label' => '008 - Sud Cinti', + ), + array( + 'value' => '1167', + 'label' => '009 - Tomina', + ), + array( + 'value' => '1168', + 'label' => '010 - Yamparáez', + ), + array( + 'value' => '1169', + 'label' => '011 - Abel Iturralde', + ), + array( + 'value' => '1170', + 'label' => '012 - Aroma', + ), + array( + 'value' => '1171', + 'label' => '013 - Bautista Saavedra', + ), + array( + 'value' => '1172', + 'label' => '014 - Caranavi', + ), + array( + 'value' => '1173', + 'label' => '015 - Eliodoro Camacho', + ), + array( + 'value' => '1174', + 'label' => '016 - Franz Tamayo', + ), + array( + 'value' => '1175', + 'label' => '017 - Gualberto Villarroel', + ), + array( + 'value' => '1176', + 'label' => '018 - Ingaví', + ), + array( + 'value' => '1177', + 'label' => '019 - Inquisivi', + ), + array( + 'value' => '1178', + 'label' => '020 - José Ramón Loayza', + ), + array( + 'value' => '1179', + 'label' => '021 - Larecaja', + ), + array( + 'value' => '1180', + 'label' => '022 - Los Andes (Bolivia)', + ), + array( + 'value' => '1181', + 'label' => '023 - Manco Kapac', + ), + array( + 'value' => '1182', + 'label' => '024 - Muñecas', + ), + array( + 'value' => '1183', + 'label' => '025 - Nor Yungas', + ), + array( + 'value' => '1184', + 'label' => '026 - Omasuyos', + ), + array( + 'value' => '1185', + 'label' => '027 - Pacajes', + ), + array( + 'value' => '1186', + 'label' => '028 - Pedro Domingo Murillo', + ), + array( + 'value' => '1187', + 'label' => '029 - Sud Yungas', + ), + array( + 'value' => '1188', + 'label' => '030 - General José Manuel Pando', + ), + array( + 'value' => '1189', + 'label' => '031 - Arani', + ), + array( + 'value' => '1190', + 'label' => '032 - Arque', + ), + array( + 'value' => '1191', + 'label' => '033 - Ayopaya', + ), + array( + 'value' => '1192', + 'label' => '034 - Bolívar (Bolivia)', + ), + array( + 'value' => '1193', + 'label' => '035 - Campero', + ), + array( + 'value' => '1194', + 'label' => '036 - Capinota', + ), + array( + 'value' => '1195', + 'label' => '037 - Cercado (Cochabamba)', + ), + array( + 'value' => '1196', + 'label' => '038 - Esteban Arze', + ), + array( + 'value' => '1197', + 'label' => '039 - Germán Jordán', + ), + array( + 'value' => '1198', + 'label' => '040 - José Carrasco', + ), + array( + 'value' => '1199', + 'label' => '041 - Mizque', + ), + array( + 'value' => '1200', + 'label' => '042 - Punata', + ), + array( + 'value' => '1201', + 'label' => '043 - Quillacollo', + ), + array( + 'value' => '1202', + 'label' => '044 - Tapacarí', + ), + array( + 'value' => '1203', + 'label' => '045 - Tiraque', + ), + array( + 'value' => '1204', + 'label' => '046 - Chapare', + ), + array( + 'value' => '1205', + 'label' => '047 - Carangas', + ), + array( + 'value' => '1206', + 'label' => '048 - Cercado (Oruro)', + ), + array( + 'value' => '1207', + 'label' => '049 - Eduardo Avaroa', + ), + array( + 'value' => '1208', + 'label' => '050 - Ladislao Cabrera', + ), + array( + 'value' => '1209', + 'label' => '051 - Litoral de Atacama', + ), + array( + 'value' => '1210', + 'label' => '052 - Mejillones', + ), + array( + 'value' => '1211', + 'label' => '053 - Nor Carangas', + ), + array( + 'value' => '1212', + 'label' => '054 - Pantaleón Dalence', + ), + array( + 'value' => '1213', + 'label' => '055 - Poopó', + ), + array( + 'value' => '1214', + 'label' => '056 - Sabaya', + ), + array( + 'value' => '1215', + 'label' => '057 - Sajama', + ), + array( + 'value' => '1216', + 'label' => '058 - San Pedro de Totora', + ), + array( + 'value' => '1217', + 'label' => '059 - Saucarí', + ), + array( + 'value' => '1218', + 'label' => '060 - Sebastián Pagador', + ), + array( + 'value' => '1219', + 'label' => '061 - Sud Carangas', + ), + array( + 'value' => '1220', + 'label' => '062 - Tomás Barrón', + ), + array( + 'value' => '1221', + 'label' => '063 - Alonso de Ibáñez', + ), + array( + 'value' => '1222', + 'label' => '064 - Antonio Quijarro', + ), + array( + 'value' => '1223', + 'label' => '065 - Bernardino Bilbao', + ), + array( + 'value' => '1224', + 'label' => '066 - Charcas (Potosí)', + ), + array( + 'value' => '1225', + 'label' => '067 - Chayanta', + ), + array( + 'value' => '1226', + 'label' => '068 - Cornelio Saavedra', + ), + array( + 'value' => '1227', + 'label' => '069 - Daniel Campos', + ), + array( + 'value' => '1228', + 'label' => '070 - Enrique Baldivieso', + ), + array( + 'value' => '1229', + 'label' => '071 - José María Linares', + ), + array( + 'value' => '1230', + 'label' => '072 - Modesto Omiste', + ), + array( + 'value' => '1231', + 'label' => '073 - Nor Chichas', + ), + array( + 'value' => '1232', + 'label' => '074 - Nor Lípez', + ), + array( + 'value' => '1233', + 'label' => '075 - Rafael Bustillo', + ), + array( + 'value' => '1234', + 'label' => '076 - Sud Chichas', + ), + array( + 'value' => '1235', + 'label' => '077 - Sud Lípez', + ), + array( + 'value' => '1236', + 'label' => '078 - Tomás Frías', + ), + array( + 'value' => '1237', + 'label' => '079 - Aniceto Arce', + ), + array( + 'value' => '1238', + 'label' => '080 - Burdet O\'Connor', + ), + array( + 'value' => '1239', + 'label' => '081 - Cercado (Tarija)', + ), + array( + 'value' => '1240', + 'label' => '082 - Eustaquio Méndez', + ), + array( + 'value' => '1241', + 'label' => '083 - José María Avilés', + ), + array( + 'value' => '1242', + 'label' => '084 - Gran Chaco', + ), + array( + 'value' => '1243', + 'label' => '085 - Andrés Ibáñez', + ), + array( + 'value' => '1244', + 'label' => '086 - Caballero', + ), + array( + 'value' => '1245', + 'label' => '087 - Chiquitos', + ), + array( + 'value' => '1246', + 'label' => '088 - Cordillera (Bolivia)', + ), + array( + 'value' => '1247', + 'label' => '089 - Florida', + ), + array( + 'value' => '1248', + 'label' => '090 - Germán Busch', + ), + array( + 'value' => '1249', + 'label' => '091 - Guarayos', + ), + array( + 'value' => '1250', + 'label' => '092 - Ichilo', + ), + array( + 'value' => '1251', + 'label' => '093 - Obispo Santistevan', + ), + array( + 'value' => '1252', + 'label' => '094 - Sara', + ), + array( + 'value' => '1253', + 'label' => '095 - Vallegrande', + ), + array( + 'value' => '1254', + 'label' => '096 - Velasco', + ), + array( + 'value' => '1255', + 'label' => '097 - Warnes', + ), + array( + 'value' => '1256', + 'label' => '098 - Ángel Sandóval', + ), + array( + 'value' => '1257', + 'label' => '099 - Ñuflo de Chaves', + ), + array( + 'value' => '1258', + 'label' => '100 - Cercado (Beni)', + ), + array( + 'value' => '1259', + 'label' => '101 - Iténez', + ), + array( + 'value' => '1260', + 'label' => '102 - Mamoré', + ), + array( + 'value' => '1261', + 'label' => '103 - Marbán', + ), + array( + 'value' => '1262', + 'label' => '104 - Moxos', + ), + array( + 'value' => '1263', + 'label' => '105 - Vaca Díez', + ), + array( + 'value' => '1264', + 'label' => '106 - Yacuma', + ), + array( + 'value' => '1265', + 'label' => '107 - General José Ballivián Segurola', + ), + array( + 'value' => '1266', + 'label' => '108 - Abuná', + ), + array( + 'value' => '1267', + 'label' => '109 - Madre de Dios', + ), + array( + 'value' => '1268', + 'label' => '110 - Manuripi', + ), + array( + 'value' => '1269', + 'label' => '111 - Nicolás Suárez', + ), + array( + 'value' => '1270', + 'label' => '112 - General Federico Román', + ), + array( + 'value' => '150', + 'label' => 'AC - Acre', + ), + array( + 'value' => '151', + 'label' => 'AL - Alagoas', + ), + array( + 'value' => '153', + 'label' => 'AM - Amazonas', + ), + array( + 'value' => '152', + 'label' => 'AP - Amapá', + ), + array( + 'value' => '154', + 'label' => 'BA - Bahia', + ), + array( + 'value' => '155', + 'label' => 'CE - Ceará', + ), + array( + 'value' => '176', + 'label' => 'DF - Distrito Federal', + ), + array( + 'value' => '156', + 'label' => 'ES - Espirito Santo', + ), + array( + 'value' => '157', + 'label' => 'GO - Goiás', + ), + array( + 'value' => '158', + 'label' => 'MA - Maranhão', + ), + array( + 'value' => '161', + 'label' => 'MG - Minas Gerais', + ), + array( + 'value' => '160', + 'label' => 'MS - Mato Grosso do Sul', + ), + array( + 'value' => '159', + 'label' => 'MT - Mato Grosso', + ), + array( + 'value' => '162', + 'label' => 'PA - Pará', + ), + array( + 'value' => '163', + 'label' => 'PB - Paraiba', + ), + array( + 'value' => '165', + 'label' => 'PE - Pernambuco', + ), + array( + 'value' => '166', + 'label' => 'PI - Piauí', + ), + array( + 'value' => '164', + 'label' => 'PR - Paraná', + ), + array( + 'value' => '167', + 'label' => 'RJ - Rio de Janeiro', + ), + array( + 'value' => '168', + 'label' => 'RN - Rio Grande do Norte', + ), + array( + 'value' => '170', + 'label' => 'RO - Rondônia', + ), + array( + 'value' => '171', + 'label' => 'RR - Roraima', + ), + array( + 'value' => '169', + 'label' => 'RS - Rio Grande do Sul', + ), + array( + 'value' => '172', + 'label' => 'SC - Santa Catarina', + ), + array( + 'value' => '173', + 'label' => 'SE - Sergipe', + ), + array( + 'value' => '174', + 'label' => 'SP - Sao Paulo', + ), + array( + 'value' => '175', + 'label' => 'TO - Tocantins', + ), + array( + 'value' => '185', + 'label' => 'AB - Alberta', + ), + array( + 'value' => '182', + 'label' => 'BC - British Columbia', + ), + array( + 'value' => '181', + 'label' => 'MB - Manitoba', + ), + array( + 'value' => '180', + 'label' => 'NB - New Brunswick', + ), + array( + 'value' => '186', + 'label' => 'NL - Newfoundland and Labrador', + ), + array( + 'value' => '179', + 'label' => 'NS - Nova Scotia', + ), + array( + 'value' => '177', + 'label' => 'ON - Ontario', + ), + array( + 'value' => '183', + 'label' => 'PE - Prince Edward Island', + ), + array( + 'value' => '178', + 'label' => 'QC - Quebec', + ), + array( + 'value' => '184', + 'label' => 'SK - Saskatchewan', + ), + array( + 'value' => '1028', + 'label' => 'AG - Argovie', + ), + array( + 'value' => '1029', + 'label' => 'AI - Appenzell Rhodes intérieures', + ), + array( + 'value' => '1030', + 'label' => 'AR - Appenzell Rhodes extérieures', + ), + array( + 'value' => '1031', + 'label' => 'BE - Berne', + ), + array( + 'value' => '1032', + 'label' => 'BL - Bâle Campagne', + ), + array( + 'value' => '1033', + 'label' => 'BS - Bâle Ville', + ), + array( + 'value' => '1034', + 'label' => 'FR - Fribourg', + ), + array( + 'value' => '1035', + 'label' => 'GE - Genève', + ), + array( + 'value' => '1036', + 'label' => 'GL - Glaris', + ), + array( + 'value' => '1037', + 'label' => 'GR - Grisons', + ), + array( + 'value' => '1038', + 'label' => 'JU - Jura', + ), + array( + 'value' => '1039', + 'label' => 'LU - Lucerne', + ), + array( + 'value' => '1040', + 'label' => 'NE - Neuchâtel', + ), + array( + 'value' => '1041', + 'label' => 'NW - Nidwald', + ), + array( + 'value' => '1042', + 'label' => 'OW - Obwald', + ), + array( + 'value' => '1043', + 'label' => 'SG - Saint-Gall', + ), + array( + 'value' => '1044', + 'label' => 'SH - Schaffhouse', + ), + array( + 'value' => '1045', + 'label' => 'SO - Soleure', + ), + array( + 'value' => '1046', + 'label' => 'SZ - Schwyz', + ), + array( + 'value' => '1047', + 'label' => 'TG - Thurgovie', + ), + array( + 'value' => '1048', + 'label' => 'TI - Tessin', + ), + array( + 'value' => '1049', + 'label' => 'UR - Uri', + ), + array( + 'value' => '1050', + 'label' => 'VD - Vaud', + ), + array( + 'value' => '1051', + 'label' => 'VS - Valais', + ), + array( + 'value' => '1052', + 'label' => 'ZG - Zug', + ), + array( + 'value' => '1053', + 'label' => 'ZH - Zürich', + ), + array( + 'value' => '187', + 'label' => '011 - Iquique', + ), + array( + 'value' => '188', + 'label' => '014 - Tamarugal', + ), + array( + 'value' => '189', + 'label' => '021 - Antofagasa', + ), + array( + 'value' => '190', + 'label' => '022 - El Loa', + ), + array( + 'value' => '191', + 'label' => '023 - Tocopilla', + ), + array( + 'value' => '192', + 'label' => '031 - Copiapó', + ), + array( + 'value' => '193', + 'label' => '032 - Chañaral', + ), + array( + 'value' => '194', + 'label' => '033 - Huasco', + ), + array( + 'value' => '195', + 'label' => '041 - Elqui', + ), + array( + 'value' => '196', + 'label' => '042 - Choapa', + ), + array( + 'value' => '197', + 'label' => '043 - Limarí', + ), + array( + 'value' => '198', + 'label' => '051 - Valparaíso', + ), + array( + 'value' => '199', + 'label' => '052 - Isla de Pascua', + ), + array( + 'value' => '200', + 'label' => '053 - Los Andes', + ), + array( + 'value' => '201', + 'label' => '054 - Petorca', + ), + array( + 'value' => '202', + 'label' => '055 - Quillota', + ), + array( + 'value' => '203', + 'label' => '056 - San Antonio', + ), + array( + 'value' => '204', + 'label' => '057 - San Felipe de Aconcagua', + ), + array( + 'value' => '205', + 'label' => '058 - Marga Marga', + ), + array( + 'value' => '206', + 'label' => '061 - Cachapoal', + ), + array( + 'value' => '207', + 'label' => '062 - Cardenal Caro', + ), + array( + 'value' => '208', + 'label' => '063 - Colchagua', + ), + array( + 'value' => '209', + 'label' => '071 - Talca', + ), + array( + 'value' => '210', + 'label' => '072 - Cauquenes', + ), + array( + 'value' => '211', + 'label' => '073 - Curicó', + ), + array( + 'value' => '212', + 'label' => '074 - Linares', + ), + array( + 'value' => '213', + 'label' => '081 - Concepción', + ), + array( + 'value' => '214', + 'label' => '082 - Arauco', + ), + array( + 'value' => '215', + 'label' => '083 - Biobío', + ), + array( + 'value' => '216', + 'label' => '084 - Ñuble', + ), + array( + 'value' => '217', + 'label' => '091 - Cautín', + ), + array( + 'value' => '218', + 'label' => '092 - Malleco', + ), + array( + 'value' => '219', + 'label' => '101 - Llanquihue', + ), + array( + 'value' => '220', + 'label' => '102 - Chiloé', + ), + array( + 'value' => '221', + 'label' => '103 - Osorno', + ), + array( + 'value' => '222', + 'label' => '104 - Palena', + ), + array( + 'value' => '223', + 'label' => '111 - Coihaique', + ), + array( + 'value' => '224', + 'label' => '112 - Aisén', + ), + array( + 'value' => '225', + 'label' => '113 - Capitán Prat', + ), + array( + 'value' => '226', + 'label' => '114 - General Carrera', + ), + array( + 'value' => '227', + 'label' => '121 - Magallanes', + ), + array( + 'value' => '228', + 'label' => '122 - Antártica Chilena', + ), + array( + 'value' => '229', + 'label' => '123 - Tierra del Fuego', + ), + array( + 'value' => '230', + 'label' => '124 - Última Esperanza', + ), + array( + 'value' => '231', + 'label' => '131 - Santiago', + ), + array( + 'value' => '232', + 'label' => '132 - Cordillera', + ), + array( + 'value' => '233', + 'label' => '133 - Chacabuco', + ), + array( + 'value' => '234', + 'label' => '134 - Maipo', + ), + array( + 'value' => '235', + 'label' => '135 - Melipilla', + ), + array( + 'value' => '236', + 'label' => '136 - Talagante', + ), + array( + 'value' => '237', + 'label' => '141 - Valdivia', + ), + array( + 'value' => '238', + 'label' => '142 - Ranco', + ), + array( + 'value' => '239', + 'label' => '151 - Arica', + ), + array( + 'value' => '240', + 'label' => '152 - Parinacota', + ), + array( + 'value' => '262', + 'label' => 'AMA - Amazonas', + ), + array( + 'value' => '241', + 'label' => 'ANT - Antioquia', + ), + array( + 'value' => '260', + 'label' => 'ARA - Arauca', + ), + array( + 'value' => '257', + 'label' => 'ATL - Atlántico', + ), + array( + 'value' => '270', + 'label' => 'BOG - Bogotá', + ), + array( + 'value' => '242', + 'label' => 'BOL - Bolívar', + ), + array( + 'value' => '243', + 'label' => 'BOY - Boyacá', + ), + array( + 'value' => '244', + 'label' => 'CAL - Caldas', + ), + array( + 'value' => '263', + 'label' => 'CAQ - Caquetá', + ), + array( + 'value' => '261', + 'label' => 'CAS - Casanare', + ), + array( + 'value' => '245', + 'label' => 'CAU - Cauca', + ), + array( + 'value' => '272', + 'label' => 'CES - Cesar', + ), + array( + 'value' => '264', + 'label' => 'CHO - Chocó', + ), + array( + 'value' => '258', + 'label' => 'COR - Córdoba', + ), + array( + 'value' => '246', + 'label' => 'CUN - Cundinamarca', + ), + array( + 'value' => '265', + 'label' => 'GUA - Guainía', + ), + array( + 'value' => '266', + 'label' => 'GUV - Guaviare', + ), + array( + 'value' => '247', + 'label' => 'HUI - Huila', + ), + array( + 'value' => '248', + 'label' => 'LAG - La Guajira', + ), + array( + 'value' => '273', + 'label' => 'MAG - Magdalena', + ), + array( + 'value' => '249', + 'label' => 'MET - Meta', + ), + array( + 'value' => '250', + 'label' => 'NAR - Nariño', + ), + array( + 'value' => '251', + 'label' => 'NDS - Norte de Santander', + ), + array( + 'value' => '267', + 'label' => 'PUT - Putumayo', + ), + array( + 'value' => '268', + 'label' => 'QUI - Quindío', + ), + array( + 'value' => '256', + 'label' => 'RIS - Risalda', + ), + array( + 'value' => '252', + 'label' => 'SAN - Santander', + ), + array( + 'value' => '259', + 'label' => 'SAP - San Andrés, Providencia y Santa Catalina', + ), + array( + 'value' => '253', + 'label' => 'SUC - Sucre', + ), + array( + 'value' => '254', + 'label' => 'TOL - Tolima', + ), + array( + 'value' => '255', + 'label' => 'VAC - Valle del Cauca', + ), + array( + 'value' => '269', + 'label' => 'VAU - Vaupés', + ), + array( + 'value' => '271', + 'label' => 'VID - Vichada', + ), + array( + 'value' => '399', + 'label' => 'BB - Brandenburg', + ), + array( + 'value' => '398', + 'label' => 'BE - Berlin', + ), + array( + 'value' => '396', + 'label' => 'BW - Baden-Württemberg', + ), + array( + 'value' => '397', + 'label' => 'BY - Bayern', + ), + array( + 'value' => '400', + 'label' => 'HB - Bremen', + ), + array( + 'value' => '402', + 'label' => 'HE - Hessen', + ), + array( + 'value' => '401', + 'label' => 'HH - Hamburg', + ), + array( + 'value' => '403', + 'label' => 'MV - Mecklenburg-Vorpommern', + ), + array( + 'value' => '404', + 'label' => 'NI - Niedersachsen', + ), + array( + 'value' => '405', + 'label' => 'NW - Nordrhein-Westfalen', + ), + array( + 'value' => '406', + 'label' => 'RP - Rheinland-Pfalz', + ), + array( + 'value' => '410', + 'label' => 'SH - Schleswig-Holstein', + ), + array( + 'value' => '407', + 'label' => 'SL - Saarland', + ), + array( + 'value' => '408', + 'label' => 'SN - Sachsen', + ), + array( + 'value' => '409', + 'label' => 'ST - Sachsen-Anhalt', + ), + array( + 'value' => '411', + 'label' => 'TH - Thüringen', + ), + array( + 'value' => '2', + 'label' => '01 - Adrar', + ), + array( + 'value' => '3', + 'label' => '02 - Chlef', + ), + array( + 'value' => '4', + 'label' => '03 - Laghouat', + ), + array( + 'value' => '5', + 'label' => '04 - Oum El Bouaghi', + ), + array( + 'value' => '6', + 'label' => '05 - Batna', + ), + array( + 'value' => '7', + 'label' => '06 - Béjaïa', + ), + array( + 'value' => '8', + 'label' => '07 - Biskra', + ), + array( + 'value' => '9', + 'label' => '08 - Béchar', + ), + array( + 'value' => '10', + 'label' => '09 - Blida', + ), + array( + 'value' => '11', + 'label' => '10 - Bouira', + ), + array( + 'value' => '12', + 'label' => '11 - Tamanrasset', + ), + array( + 'value' => '13', + 'label' => '12 - Tébessa', + ), + array( + 'value' => '14', + 'label' => '13 - Tlemcen', + ), + array( + 'value' => '15', + 'label' => '14 - Tiaret', + ), + array( + 'value' => '16', + 'label' => '15 - Tizi Ouzou', + ), + array( + 'value' => '17', + 'label' => '16 - Alger', + ), + array( + 'value' => '18', + 'label' => '17 - Djelfa', + ), + array( + 'value' => '19', + 'label' => '18 - Jijel', + ), + array( + 'value' => '20', + 'label' => '19 - Sétif', + ), + array( + 'value' => '21', + 'label' => '20 - Saïda', + ), + array( + 'value' => '22', + 'label' => '21 - Skikda', + ), + array( + 'value' => '23', + 'label' => '22 - Sidi Bel Abbès', + ), + array( + 'value' => '24', + 'label' => '23 - Annaba', + ), + array( + 'value' => '25', + 'label' => '24 - Guelma', + ), + array( + 'value' => '26', + 'label' => '25 - Constantine', + ), + array( + 'value' => '27', + 'label' => '26 - Médéa', + ), + array( + 'value' => '28', + 'label' => '27 - Mostaganem', + ), + array( + 'value' => '29', + 'label' => '28 - M\'Sila', + ), + array( + 'value' => '30', + 'label' => '29 - Mascara', + ), + array( + 'value' => '31', + 'label' => '30 - Ouargla', + ), + array( + 'value' => '32', + 'label' => '31 - Oran', + ), + array( + 'value' => '33', + 'label' => '32 - El Bayadh', + ), + array( + 'value' => '34', + 'label' => '33 - Illizi', + ), + array( + 'value' => '35', + 'label' => '34 - Bordj Bou Arreridj', + ), + array( + 'value' => '36', + 'label' => '35 - Boumerdès', + ), + array( + 'value' => '37', + 'label' => '36 - El Tarf', + ), + array( + 'value' => '38', + 'label' => '37 - Tindouf', + ), + array( + 'value' => '39', + 'label' => '38 - Tissemsilt', + ), + array( + 'value' => '40', + 'label' => '39 - El Oued', + ), + array( + 'value' => '41', + 'label' => '40 - Khenchela', + ), + array( + 'value' => '42', + 'label' => '41 - Souk Ahras', + ), + array( + 'value' => '43', + 'label' => '42 - Tipaza', + ), + array( + 'value' => '44', + 'label' => '43 - Mila', + ), + array( + 'value' => '45', + 'label' => '44 - Aïn Defla', + ), + array( + 'value' => '46', + 'label' => '45 - Naâma', + ), + array( + 'value' => '47', + 'label' => '46 - Aïn Témouchent', + ), + array( + 'value' => '48', + 'label' => '47 - Ghardaïa', + ), + array( + 'value' => '49', + 'label' => '48 - Relizane', + ), + array( + 'value' => '50', + 'label' => '49 - Timimoun', + ), + array( + 'value' => '51', + 'label' => '50 - Bordj Badji Mokhtar', + ), + array( + 'value' => '52', + 'label' => '51 - Ouled Djellal', + ), + array( + 'value' => '53', + 'label' => '52 - Béni Abbès', + ), + array( + 'value' => '54', + 'label' => '53 - In Salah', + ), + array( + 'value' => '55', + 'label' => '54 - In Guezzam', + ), + array( + 'value' => '56', + 'label' => '55 - Touggourt', + ), + array( + 'value' => '57', + 'label' => '56 - Djanet', + ), + array( + 'value' => '58', + 'label' => '57 - El M\'Ghair', + ), + array( + 'value' => '59', + 'label' => '58 - El Ménéa', + ), + array( + 'value' => '1273', + 'label' => 'A - Alacant', + ), + array( + 'value' => '1272', + 'label' => 'AB - Albacete', + ), + array( + 'value' => '1274', + 'label' => 'AL - Almería', + ), + array( + 'value' => '1276', + 'label' => 'AV - Ávila', + ), + array( + 'value' => '1278', + 'label' => 'B - Barcelona', + ), + array( + 'value' => '1277', + 'label' => 'BA - Badajoz', + ), + array( + 'value' => '1320', + 'label' => 'BI - Bizkaia', + ), + array( + 'value' => '1279', + 'label' => 'BU - Burgos', + ), + array( + 'value' => '1296', + 'label' => 'C - La Coruña', + ), + array( + 'value' => '1281', + 'label' => 'CA - Cádiz', + ), + array( + 'value' => '1280', + 'label' => 'CC - Cáceres', + ), + array( + 'value' => '1284', + 'label' => 'CE - Ceuta', + ), + array( + 'value' => '1286', + 'label' => 'CO - Córdoba', + ), + array( + 'value' => '1285', + 'label' => 'CR - Ciudad Real', + ), + array( + 'value' => '1283', + 'label' => 'CS - Castelló', + ), + array( + 'value' => '1287', + 'label' => 'CU - Cuenca', + ), + array( + 'value' => '1298', + 'label' => 'GC - Las Palmas', + ), + array( + 'value' => '1288', + 'label' => 'GI - Girona', + ), + array( + 'value' => '1289', + 'label' => 'GR - Granada', + ), + array( + 'value' => '1290', + 'label' => 'GU - Guadalajara', + ), + array( + 'value' => '1292', + 'label' => 'H - Huelva', + ), + array( + 'value' => '1293', + 'label' => 'HU - Huesca', + ), + array( + 'value' => '1295', + 'label' => 'J - Jaén', + ), + array( + 'value' => '1300', + 'label' => 'L - Lleida', + ), + array( + 'value' => '1299', + 'label' => 'LE - León', + ), + array( + 'value' => '1297', + 'label' => 'LO - La Rioja', + ), + array( + 'value' => '1301', + 'label' => 'LU - Lugo', + ), + array( + 'value' => '1302', + 'label' => 'M - Madrid', + ), + array( + 'value' => '1303', + 'label' => 'MA - Málaga', + ), + array( + 'value' => '1304', + 'label' => 'ML - Melilla', + ), + array( + 'value' => '1305', + 'label' => 'MU - Murcia', + ), + array( + 'value' => '1306', + 'label' => 'NA - Navarra', + ), + array( + 'value' => '1275', + 'label' => 'O - Asturias', + ), + array( + 'value' => '1307', + 'label' => 'OR - Orense', + ), + array( + 'value' => '1308', + 'label' => 'P - Palencia', + ), + array( + 'value' => '1294', + 'label' => 'PM - Illes Balears', + ), + array( + 'value' => '1309', + 'label' => 'PO - Pontevedra', + ), + array( + 'value' => '1282', + 'label' => 'S - Cantabria', + ), + array( + 'value' => '1310', + 'label' => 'SA - Salamanca', + ), + array( + 'value' => '1313', + 'label' => 'SE - Sevilla', + ), + array( + 'value' => '1312', + 'label' => 'SG - Segovia', + ), + array( + 'value' => '1314', + 'label' => 'SO - Soria', + ), + array( + 'value' => '1291', + 'label' => 'SS - Gipuzkoa', + ), + array( + 'value' => '1315', + 'label' => 'T - Tarragona', + ), + array( + 'value' => '1316', + 'label' => 'TE - Teruel', + ), + array( + 'value' => '1311', + 'label' => 'TF - Santa Cruz de Tenerife', + ), + array( + 'value' => '1317', + 'label' => 'TO - Toledo', + ), + array( + 'value' => '1318', + 'label' => 'V - València', + ), + array( + 'value' => '1319', + 'label' => 'VA - Valladolid', + ), + array( + 'value' => '1271', + 'label' => 'VI - Araba', + ), + array( + 'value' => '1322', + 'label' => 'Z - Zaragoza', + ), + array( + 'value' => '1321', + 'label' => 'ZA - Zamora', + ), + array( + 'value' => '300', + 'label' => '01 - Ain', + ), + array( + 'value' => '301', + 'label' => '02 - Aisne', + ), + array( + 'value' => '302', + 'label' => '03 - Allier', + ), + array( + 'value' => '303', + 'label' => '04 - Alpes-de-Haute-Provence', + ), + array( + 'value' => '304', + 'label' => '05 - Hautes-Alpes', + ), + array( + 'value' => '305', + 'label' => '06 - Alpes-Maritimes', + ), + array( + 'value' => '306', + 'label' => '07 - Ardèche', + ), + array( + 'value' => '307', + 'label' => '08 - Ardennes', + ), + array( + 'value' => '308', + 'label' => '09 - Ariège', + ), + array( + 'value' => '309', + 'label' => '10 - Aube', + ), + array( + 'value' => '310', + 'label' => '11 - Aude', + ), + array( + 'value' => '311', + 'label' => '12 - Aveyron', + ), + array( + 'value' => '312', + 'label' => '13 - Bouches-du-Rhône', + ), + array( + 'value' => '313', + 'label' => '14 - Calvados', + ), + array( + 'value' => '314', + 'label' => '15 - Cantal', + ), + array( + 'value' => '315', + 'label' => '16 - Charente', + ), + array( + 'value' => '316', + 'label' => '17 - Charente-Maritime', + ), + array( + 'value' => '317', + 'label' => '18 - Cher', + ), + array( + 'value' => '318', + 'label' => '19 - Corrèze', + ), + array( + 'value' => '321', + 'label' => '21 - Côte-d Or', + ), + array( + 'value' => '322', + 'label' => '22 - Côtes-d Armor', + ), + array( + 'value' => '323', + 'label' => '23 - Creuse', + ), + array( + 'value' => '324', + 'label' => '24 - Dordogne', + ), + array( + 'value' => '325', + 'label' => '25 - Doubs', + ), + array( + 'value' => '326', + 'label' => '26 - Drôme', + ), + array( + 'value' => '327', + 'label' => '27 - Eure', + ), + array( + 'value' => '328', + 'label' => '28 - Eure-et-Loir', + ), + array( + 'value' => '329', + 'label' => '29 - Finistère', + ), + array( + 'value' => '319', + 'label' => '2A - Corse-du-Sud', + ), + array( + 'value' => '320', + 'label' => '2B - Haute-Corse', + ), + array( + 'value' => '330', + 'label' => '30 - Gard', + ), + array( + 'value' => '331', + 'label' => '31 - Haute-Garonne', + ), + array( + 'value' => '332', + 'label' => '32 - Gers', + ), + array( + 'value' => '333', + 'label' => '33 - Gironde', + ), + array( + 'value' => '334', + 'label' => '34 - Hérault', + ), + array( + 'value' => '335', + 'label' => '35 - Ille-et-Vilaine', + ), + array( + 'value' => '336', + 'label' => '36 - Indre', + ), + array( + 'value' => '337', + 'label' => '37 - Indre-et-Loire', + ), + array( + 'value' => '338', + 'label' => '38 - Isère', + ), + array( + 'value' => '339', + 'label' => '39 - Jura', + ), + array( + 'value' => '340', + 'label' => '40 - Landes', + ), + array( + 'value' => '341', + 'label' => '41 - Loir-et-Cher', + ), + array( + 'value' => '342', + 'label' => '42 - Loire', + ), + array( + 'value' => '343', + 'label' => '43 - Haute-Loire', + ), + array( + 'value' => '344', + 'label' => '44 - Loire-Atlantique', + ), + array( + 'value' => '345', + 'label' => '45 - Loiret', + ), + array( + 'value' => '346', + 'label' => '46 - Lot', + ), + array( + 'value' => '347', + 'label' => '47 - Lot-et-Garonne', + ), + array( + 'value' => '348', + 'label' => '48 - Lozère', + ), + array( + 'value' => '349', + 'label' => '49 - Maine-et-Loire', + ), + array( + 'value' => '350', + 'label' => '50 - Manche', + ), + array( + 'value' => '351', + 'label' => '51 - Marne', + ), + array( + 'value' => '352', + 'label' => '52 - Haute-Marne', + ), + array( + 'value' => '353', + 'label' => '53 - Mayenne', + ), + array( + 'value' => '354', + 'label' => '54 - Meurthe-et-Moselle', + ), + array( + 'value' => '355', + 'label' => '55 - Meuse', + ), + array( + 'value' => '356', + 'label' => '56 - Morbihan', + ), + array( + 'value' => '357', + 'label' => '57 - Moselle', + ), + array( + 'value' => '358', + 'label' => '58 - Nièvre', + ), + array( + 'value' => '359', + 'label' => '59 - Nord', + ), + array( + 'value' => '360', + 'label' => '60 - Oise', + ), + array( + 'value' => '361', + 'label' => '61 - Orne', + ), + array( + 'value' => '362', + 'label' => '62 - Pas-de-Calais', + ), + array( + 'value' => '363', + 'label' => '63 - Puy-de-Dôme', + ), + array( + 'value' => '364', + 'label' => '64 - Pyrénées-Atlantiques', + ), + array( + 'value' => '365', + 'label' => '65 - Hautes-Pyrénées', + ), + array( + 'value' => '366', + 'label' => '66 - Pyrénées-Orientales', + ), + array( + 'value' => '367', + 'label' => '67 - Bas-Rhin', + ), + array( + 'value' => '368', + 'label' => '68 - Haut-Rhin', + ), + array( + 'value' => '369', + 'label' => '69 - Rhône', + ), + array( + 'value' => '370', + 'label' => '70 - Haute-Saône', + ), + array( + 'value' => '371', + 'label' => '71 - Saône-et-Loire', + ), + array( + 'value' => '372', + 'label' => '72 - Sarthe', + ), + array( + 'value' => '373', + 'label' => '73 - Savoie', + ), + array( + 'value' => '374', + 'label' => '74 - Haute-Savoie', + ), + array( + 'value' => '375', + 'label' => '75 - Paris', + ), + array( + 'value' => '376', + 'label' => '76 - Seine-Maritime', + ), + array( + 'value' => '377', + 'label' => '77 - Seine-et-Marne', + ), + array( + 'value' => '378', + 'label' => '78 - Yvelines', + ), + array( + 'value' => '379', + 'label' => '79 - Deux-Sèvres', + ), + array( + 'value' => '380', + 'label' => '80 - Somme', + ), + array( + 'value' => '381', + 'label' => '81 - Tarn', + ), + array( + 'value' => '382', + 'label' => '82 - Tarn-et-Garonne', + ), + array( + 'value' => '383', + 'label' => '83 - Var', + ), + array( + 'value' => '384', + 'label' => '84 - Vaucluse', + ), + array( + 'value' => '385', + 'label' => '85 - Vendée', + ), + array( + 'value' => '386', + 'label' => '86 - Vienne', + ), + array( + 'value' => '387', + 'label' => '87 - Haute-Vienne', + ), + array( + 'value' => '388', + 'label' => '88 - Vosges', + ), + array( + 'value' => '389', + 'label' => '89 - Yonne', + ), + array( + 'value' => '390', + 'label' => '90 - Territoire de Belfort', + ), + array( + 'value' => '391', + 'label' => '91 - Essonne', + ), + array( + 'value' => '392', + 'label' => '92 - Hauts-de-Seine', + ), + array( + 'value' => '393', + 'label' => '93 - Seine-Saint-Denis', + ), + array( + 'value' => '394', + 'label' => '94 - Val-de-Marne', + ), + array( + 'value' => '395', + 'label' => '95 - Val-d Oise', + ), + array( + 'value' => '295', + 'label' => '971 - Guadeloupe', + ), + array( + 'value' => '296', + 'label' => '972 - Martinique', + ), + array( + 'value' => '297', + 'label' => '973 - Guyane', + ), + array( + 'value' => '298', + 'label' => '974 - Réunion', + ), + array( + 'value' => '299', + 'label' => '976 - Mayotte', + ), + array( + 'value' => '1323', + 'label' => '701 - Bedfordshire', + ), + array( + 'value' => '1324', + 'label' => '702 - Berkshire', + ), + array( + 'value' => '1325', + 'label' => '703 - Bristol, City of', + ), + array( + 'value' => '1326', + 'label' => '704 - Buckinghamshire', + ), + array( + 'value' => '1327', + 'label' => '705 - Cambridgeshire', + ), + array( + 'value' => '1328', + 'label' => '706 - Cheshire', + ), + array( + 'value' => '1329', + 'label' => '707 - Cleveland', + ), + array( + 'value' => '1330', + 'label' => '708 - Cornwall', + ), + array( + 'value' => '1331', + 'label' => '709 - Cumberland', + ), + array( + 'value' => '1332', + 'label' => '710 - Cumbria', + ), + array( + 'value' => '1333', + 'label' => '711 - Derbyshire', + ), + array( + 'value' => '1334', + 'label' => '712 - Devon', + ), + array( + 'value' => '1335', + 'label' => '713 - Dorset', + ), + array( + 'value' => '1336', + 'label' => '714 - Co. Durham', + ), + array( + 'value' => '1337', + 'label' => '715 - East Riding of Yorkshire', + ), + array( + 'value' => '1338', + 'label' => '716 - East Sussex', + ), + array( + 'value' => '1339', + 'label' => '717 - Essex', + ), + array( + 'value' => '1340', + 'label' => '718 - Gloucestershire', + ), + array( + 'value' => '1341', + 'label' => '719 - Greater Manchester', + ), + array( + 'value' => '1342', + 'label' => '720 - Hampshire', + ), + array( + 'value' => '1343', + 'label' => '721 - Hertfordshire', + ), + array( + 'value' => '1344', + 'label' => '722 - Hereford and Worcester', + ), + array( + 'value' => '1345', + 'label' => '723 - Herefordshire', + ), + array( + 'value' => '1346', + 'label' => '724 - Huntingdonshire', + ), + array( + 'value' => '1347', + 'label' => '725 - Isle of Man', + ), + array( + 'value' => '1348', + 'label' => '726 - Isle of Wight', + ), + array( + 'value' => '1349', + 'label' => '727 - Jersey', + ), + array( + 'value' => '1350', + 'label' => '728 - Kent', + ), + array( + 'value' => '1351', + 'label' => '729 - Lancashire', + ), + array( + 'value' => '1352', + 'label' => '730 - Leicestershire', + ), + array( + 'value' => '1353', + 'label' => '731 - Lincolnshire', + ), + array( + 'value' => '1354', + 'label' => '732 - London - City of London', + ), + array( + 'value' => '1355', + 'label' => '733 - Merseyside', + ), + array( + 'value' => '1356', + 'label' => '734 - Middlesex', + ), + array( + 'value' => '1357', + 'label' => '735 - Norfolk', + ), + array( + 'value' => '1358', + 'label' => '736 - North Yorkshire', + ), + array( + 'value' => '1359', + 'label' => '737 - North Riding of Yorkshire', + ), + array( + 'value' => '1360', + 'label' => '738 - Northamptonshire', + ), + array( + 'value' => '1361', + 'label' => '739 - Northumberland', + ), + array( + 'value' => '1362', + 'label' => '740 - Nottinghamshire', + ), + array( + 'value' => '1363', + 'label' => '741 - Oxfordshire', + ), + array( + 'value' => '1364', + 'label' => '742 - Rutland', + ), + array( + 'value' => '1365', + 'label' => '743 - Shropshire', + ), + array( + 'value' => '1366', + 'label' => '744 - Somerset', + ), + array( + 'value' => '1367', + 'label' => '745 - Staffordshire', + ), + array( + 'value' => '1368', + 'label' => '746 - Suffolk', + ), + array( + 'value' => '1369', + 'label' => '747 - Surrey', + ), + array( + 'value' => '1370', + 'label' => '748 - Sussex', + ), + array( + 'value' => '1371', + 'label' => '749 - Tyne and Wear', + ), + array( + 'value' => '1372', + 'label' => '750 - Warwickshire', + ), + array( + 'value' => '1373', + 'label' => '751 - West Midlands', + ), + array( + 'value' => '1374', + 'label' => '752 - West Sussex', + ), + array( + 'value' => '1375', + 'label' => '753 - West Yorkshire', + ), + array( + 'value' => '1376', + 'label' => '754 - West Riding of Yorkshire', + ), + array( + 'value' => '1377', + 'label' => '755 - Wiltshire', + ), + array( + 'value' => '1378', + 'label' => '756 - Worcestershire', + ), + array( + 'value' => '1379', + 'label' => '757 - Yorkshire', + ), + array( + 'value' => '1380', + 'label' => '758 - Anglesey', + ), + array( + 'value' => '1381', + 'label' => '759 - Breconshire', + ), + array( + 'value' => '1382', + 'label' => '760 - Caernarvonshire', + ), + array( + 'value' => '1383', + 'label' => '761 - Cardiganshire', + ), + array( + 'value' => '1384', + 'label' => '762 - Carmarthenshire', + ), + array( + 'value' => '1385', + 'label' => '763 - Ceredigion', + ), + array( + 'value' => '1386', + 'label' => '764 - Denbighshire', + ), + array( + 'value' => '1387', + 'label' => '765 - Flintshire', + ), + array( + 'value' => '1388', + 'label' => '766 - Glamorgan', + ), + array( + 'value' => '1389', + 'label' => '767 - Gwent', + ), + array( + 'value' => '1390', + 'label' => '768 - Gwynedd', + ), + array( + 'value' => '1391', + 'label' => '769 - Merionethshire', + ), + array( + 'value' => '1392', + 'label' => '770 - Monmouthshire', + ), + array( + 'value' => '1393', + 'label' => '771 - Mid Glamorgan', + ), + array( + 'value' => '1394', + 'label' => '772 - Montgomeryshire', + ), + array( + 'value' => '1395', + 'label' => '773 - Pembrokeshire', + ), + array( + 'value' => '1396', + 'label' => '774 - Powys', + ), + array( + 'value' => '1397', + 'label' => '775 - Radnorshire', + ), + array( + 'value' => '1398', + 'label' => '776 - South Glamorgan', + ), + array( + 'value' => '1399', + 'label' => '777 - Aberdeen, City of', + ), + array( + 'value' => '1400', + 'label' => '778 - Angus', + ), + array( + 'value' => '1401', + 'label' => '779 - Argyll', + ), + array( + 'value' => '1402', + 'label' => '780 - Ayrshire', + ), + array( + 'value' => '1403', + 'label' => '781 - Banffshire', + ), + array( + 'value' => '1404', + 'label' => '782 - Berwickshire', + ), + array( + 'value' => '1405', + 'label' => '783 - Bute', + ), + array( + 'value' => '1406', + 'label' => '784 - Caithness', + ), + array( + 'value' => '1407', + 'label' => '785 - Clackmannanshire', + ), + array( + 'value' => '1408', + 'label' => '786 - Dumfriesshire', + ), + array( + 'value' => '1409', + 'label' => '787 - Dumbartonshire', + ), + array( + 'value' => '1410', + 'label' => '788 - Dundee, City of', + ), + array( + 'value' => '1411', + 'label' => '789 - East Lothian', + ), + array( + 'value' => '1412', + 'label' => '790 - Fife', + ), + array( + 'value' => '1413', + 'label' => '791 - Inverness', + ), + array( + 'value' => '1414', + 'label' => '792 - Kincardineshire', + ), + array( + 'value' => '1415', + 'label' => '793 - Kinross-shire', + ), + array( + 'value' => '1416', + 'label' => '794 - Kirkcudbrightshire', + ), + array( + 'value' => '1417', + 'label' => '795 - Lanarkshire', + ), + array( + 'value' => '1418', + 'label' => '796 - Midlothian', + ), + array( + 'value' => '1419', + 'label' => '797 - Morayshire', + ), + array( + 'value' => '1420', + 'label' => '798 - Nairnshire', + ), + array( + 'value' => '1421', + 'label' => '799 - Orkney', + ), + array( + 'value' => '1422', + 'label' => '800 - Peebleshire', + ), + array( + 'value' => '1423', + 'label' => '801 - Perthshire', + ), + array( + 'value' => '1424', + 'label' => '802 - Renfrewshire', + ), + array( + 'value' => '1425', + 'label' => '803 - Ross & Cromarty', + ), + array( + 'value' => '1426', + 'label' => '804 - Roxburghshire', + ), + array( + 'value' => '1427', + 'label' => '805 - Selkirkshire', + ), + array( + 'value' => '1428', + 'label' => '806 - Shetland', + ), + array( + 'value' => '1429', + 'label' => '807 - Stirlingshire', + ), + array( + 'value' => '1430', + 'label' => '808 - Sutherland', + ), + array( + 'value' => '1431', + 'label' => '809 - West Lothian', + ), + array( + 'value' => '1432', + 'label' => '810 - Wigtownshire', + ), + array( + 'value' => '1433', + 'label' => '811 - Antrim', + ), + array( + 'value' => '1434', + 'label' => '812 - Armagh', + ), + array( + 'value' => '1435', + 'label' => '813 - Co. Down', + ), + array( + 'value' => '1436', + 'label' => '814 - Co. Fermanagh', + ), + array( + 'value' => '1437', + 'label' => '815 - Co. Londonderry', + ), + array( + 'value' => '414', + 'label' => '01 - Έβρος', + ), + array( + 'value' => '415', + 'label' => '02 - Θάσος', + ), + array( + 'value' => '416', + 'label' => '03 - Καβάλα', + ), + array( + 'value' => '417', + 'label' => '04 - Ξάνθη', + ), + array( + 'value' => '418', + 'label' => '05 - Ροδόπη', + ), + array( + 'value' => '419', + 'label' => '06 - Ημαθία', + ), + array( + 'value' => '420', + 'label' => '07 - Θεσσαλονίκη', + ), + array( + 'value' => '421', + 'label' => '08 - Κιλκίς', + ), + array( + 'value' => '422', + 'label' => '09 - Πέλλα', + ), + array( + 'value' => '423', + 'label' => '10 - Πιερία', + ), + array( + 'value' => '424', + 'label' => '11 - Σέρρες', + ), + array( + 'value' => '425', + 'label' => '12 - Χαλκιδική', + ), + array( + 'value' => '426', + 'label' => '13 - Άρτα', + ), + array( + 'value' => '427', + 'label' => '14 - Θεσπρωτία', + ), + array( + 'value' => '428', + 'label' => '15 - Ιωάννινα', + ), + array( + 'value' => '429', + 'label' => '16 - Πρέβεζα', + ), + array( + 'value' => '430', + 'label' => '17 - Γρεβενά', + ), + array( + 'value' => '431', + 'label' => '18 - Καστοριά', + ), + array( + 'value' => '432', + 'label' => '19 - Κοζάνη', + ), + array( + 'value' => '433', + 'label' => '20 - Φλώρινα', + ), + array( + 'value' => '434', + 'label' => '21 - Καρδίτσα', + ), + array( + 'value' => '435', + 'label' => '22 - Λάρισα', + ), + array( + 'value' => '436', + 'label' => '23 - Μαγνησία', + ), + array( + 'value' => '437', + 'label' => '24 - Τρίκαλα', + ), + array( + 'value' => '438', + 'label' => '25 - Σποράδες', + ), + array( + 'value' => '439', + 'label' => '26 - Βοιωτία', + ), + array( + 'value' => '440', + 'label' => '27 - Εύβοια', + ), + array( + 'value' => '441', + 'label' => '28 - Ευρυτανία', + ), + array( + 'value' => '442', + 'label' => '29 - Φθιώτιδα', + ), + array( + 'value' => '443', + 'label' => '30 - Φωκίδα', + ), + array( + 'value' => '444', + 'label' => '31 - Αργολίδα', + ), + array( + 'value' => '445', + 'label' => '32 - Αρκαδία', + ), + array( + 'value' => '446', + 'label' => '33 - Κορινθία', + ), + array( + 'value' => '447', + 'label' => '34 - Λακωνία', + ), + array( + 'value' => '448', + 'label' => '35 - Μεσσηνία', + ), + array( + 'value' => '449', + 'label' => '36 - Αιτωλοακαρνανία', + ), + array( + 'value' => '450', + 'label' => '37 - Αχαΐα', + ), + array( + 'value' => '451', + 'label' => '38 - Ηλεία', + ), + array( + 'value' => '452', + 'label' => '39 - Ζάκυνθος', + ), + array( + 'value' => '453', + 'label' => '40 - Κέρκυρα', + ), + array( + 'value' => '454', + 'label' => '41 - Κεφαλληνία', + ), + array( + 'value' => '455', + 'label' => '42 - Ιθάκη', + ), + array( + 'value' => '456', + 'label' => '43 - Λευκάδα', + ), + array( + 'value' => '457', + 'label' => '44 - Ικαρία', + ), + array( + 'value' => '458', + 'label' => '45 - Λέσβος', + ), + array( + 'value' => '459', + 'label' => '46 - Λήμνος', + ), + array( + 'value' => '460', + 'label' => '47 - Σάμος', + ), + array( + 'value' => '461', + 'label' => '48 - Χίος', + ), + array( + 'value' => '462', + 'label' => '49 - Άνδρος', + ), + array( + 'value' => '463', + 'label' => '50 - Θήρα', + ), + array( + 'value' => '464', + 'label' => '51 - Κάλυμνος', + ), + array( + 'value' => '465', + 'label' => '52 - Κάρπαθος', + ), + array( + 'value' => '466', + 'label' => '53 - Κέα-Κύθνος', + ), + array( + 'value' => '467', + 'label' => '54 - Κω', + ), + array( + 'value' => '468', + 'label' => '55 - Μήλος', + ), + array( + 'value' => '469', + 'label' => '56 - Μύκονος', + ), + array( + 'value' => '470', + 'label' => '57 - Νάξος', + ), + array( + 'value' => '471', + 'label' => '58 - Πάρος', + ), + array( + 'value' => '472', + 'label' => '59 - Ρόδος', + ), + array( + 'value' => '473', + 'label' => '60 - Σύρος', + ), + array( + 'value' => '474', + 'label' => '61 - Τήνος', + ), + array( + 'value' => '475', + 'label' => '62 - Ηράκλειο', + ), + array( + 'value' => '476', + 'label' => '63 - Λασίθι', + ), + array( + 'value' => '477', + 'label' => '64 - Ρέθυμνο', + ), + array( + 'value' => '478', + 'label' => '65 - Χανιά', + ), + array( + 'value' => '412', + 'label' => '66 - Αθήνα', + ), + array( + 'value' => '413', + 'label' => '67 - Δράμα', + ), + array( + 'value' => '479', + 'label' => 'AT - Atlántida', + ), + array( + 'value' => '480', + 'label' => 'CH - Choluteca', + ), + array( + 'value' => '481', + 'label' => 'CL - Colón', + ), + array( + 'value' => '482', + 'label' => 'CM - Comayagua', + ), + array( + 'value' => '483', + 'label' => 'CO - Copán', + ), + array( + 'value' => '484', + 'label' => 'CR - Cortés', + ), + array( + 'value' => '497', + 'label' => 'DC - Distrito Central', + ), + array( + 'value' => '485', + 'label' => 'EP - El Paraíso', + ), + array( + 'value' => '486', + 'label' => 'FM - Francisco Morazán', + ), + array( + 'value' => '487', + 'label' => 'GD - Gracias a Dios', + ), + array( + 'value' => '489', + 'label' => 'IB - Islas de la Bahía', + ), + array( + 'value' => '488', + 'label' => 'IN - Intibucá', + ), + array( + 'value' => '491', + 'label' => 'LM - Lempira', + ), + array( + 'value' => '490', + 'label' => 'LP - La Paz', + ), + array( + 'value' => '492', + 'label' => 'OC - Ocotepeque', + ), + array( + 'value' => '493', + 'label' => 'OL - Olancho', + ), + array( + 'value' => '494', + 'label' => 'SB - Santa Bárbara', + ), + array( + 'value' => '495', + 'label' => 'VL - Valle', + ), + array( + 'value' => '496', + 'label' => 'YO - Yoro', + ), + array( + 'value' => '274', + 'label' => 'HR-01 - Bjelovarsko-bilogorska županija', + ), + array( + 'value' => '275', + 'label' => 'HR-02 - Karlovačka županija', + ), + array( + 'value' => '276', + 'label' => 'HR-03 - Koprivničko-križevačka županija', + ), + array( + 'value' => '277', + 'label' => 'HR-04 - Krapinsko-zagorska županija', + ), + array( + 'value' => '278', + 'label' => 'HR-05 - Ličko-senjska županija', + ), + array( + 'value' => '279', + 'label' => 'HR-06 - Međimurska županija', + ), + array( + 'value' => '280', + 'label' => 'HR-07 - Primorsko-goranska županija', + ), + array( + 'value' => '281', + 'label' => 'HR-08 - Sisačko-moslavačka županija', + ), + array( + 'value' => '282', + 'label' => 'HR-09 - Varaždinska županija', + ), + array( + 'value' => '283', + 'label' => 'HR-10 - Zagrebačka županija', + ), + array( + 'value' => '284', + 'label' => 'HR-11 - Grad Zagreb', + ), + array( + 'value' => '285', + 'label' => 'HR-12 - Zadarska županija', + ), + array( + 'value' => '286', + 'label' => 'HR-13 - Šibensko-kninska županija', + ), + array( + 'value' => '287', + 'label' => 'HR-14 - Splitsko-dalmatinska županija', + ), + array( + 'value' => '288', + 'label' => 'HR-15 - Dubrovačko-neretvanska županija', + ), + array( + 'value' => '289', + 'label' => 'HR-16 - Brodsko-posavska županija', + ), + array( + 'value' => '290', + 'label' => 'HR-17 - Osječko-baranjska županija', + ), + array( + 'value' => '291', + 'label' => 'HR-18 - Požeško-slavonska županija', + ), + array( + 'value' => '292', + 'label' => 'HR-19 - Virovitičko-podravska županija', + ), + array( + 'value' => '293', + 'label' => 'HR-20 - Vukovarsko-srijemska županija', + ), + array( + 'value' => '294', + 'label' => 'HR-21 - Istarska županija', + ), + array( + 'value' => '506', + 'label' => 'HU-BA - Baranya', + ), + array( + 'value' => '516', + 'label' => 'HU-BE - Békés', + ), + array( + 'value' => '515', + 'label' => 'HU-BK - Bács-Kiskun', + ), + array( + 'value' => '498', + 'label' => 'HU-BU - Budapest', + ), + array( + 'value' => '509', + 'label' => 'HU-BZ - Borsod-Abaúj-Zemplén', + ), + array( + 'value' => '517', + 'label' => 'HU-CS - Csongrád', + ), + array( + 'value' => '500', + 'label' => 'HU-FE - Fejér', + ), + array( + 'value' => '503', + 'label' => 'HU-GS - Győr-Moson-Sopron', + ), + array( + 'value' => '512', + 'label' => 'HU-HB - Hajdú-Bihar', + ), + array( + 'value' => '510', + 'label' => 'HU-HE - Heves', + ), + array( + 'value' => '513', + 'label' => 'HU-JN - Jász-Nagykun-Szolnok', + ), + array( + 'value' => '501', + 'label' => 'HU-KE - Komárom-Esztergom', + ), + array( + 'value' => '511', + 'label' => 'HU-NO - Nógrád', + ), + array( + 'value' => '499', + 'label' => 'HU-PE - Pest', + ), + array( + 'value' => '507', + 'label' => 'HU-SO - Somogy', + ), + array( + 'value' => '514', + 'label' => 'HU-SZ - Szabolcs-Szatmár-Bereg', + ), + array( + 'value' => '508', + 'label' => 'HU-TO - Tolna', + ), + array( + 'value' => '504', + 'label' => 'HU-VA - Vas', + ), + array( + 'value' => '502', + 'label' => 'HU-VE - Veszprém', + ), + array( + 'value' => '505', + 'label' => 'HU-ZA - Zala', + ), + array( + 'value' => '1494', + 'label' => 'AC - Nanggroe Aceh Darussalam', + ), + array( + 'value' => '1474', + 'label' => 'BA - Bali', + ), + array( + 'value' => '1475', + 'label' => 'BB - Bangka Belitung', + ), + array( + 'value' => '1477', + 'label' => 'BE - Bengkulu', + ), + array( + 'value' => '1476', + 'label' => 'BT - Banten', + ), + array( + 'value' => '1480', + 'label' => 'GO - Gorontalo', + ), + array( + 'value' => '1481', + 'label' => 'JA - Jambi', + ), + array( + 'value' => '1482', + 'label' => 'JB - Jawa Barat', + ), + array( + 'value' => '1484', + 'label' => 'JI - Jawa Timur', + ), + array( + 'value' => '1479', + 'label' => 'JK - DKI Jakarta', + ), + array( + 'value' => '1483', + 'label' => 'JT - Jawa Tengah', + ), + array( + 'value' => '1485', + 'label' => 'KB - Kalimantan Barat', + ), + array( + 'value' => '1488', + 'label' => 'KI - Kalimantan Timur', + ), + array( + 'value' => '1490', + 'label' => 'KR - Kepulauan Riau', + ), + array( + 'value' => '1486', + 'label' => 'KS - Kalimantan Selatan', + ), + array( + 'value' => '1487', + 'label' => 'KT - Kalimantan Tengah', + ), + array( + 'value' => '1489', + 'label' => 'KU - Kalimantan Utara', + ), + array( + 'value' => '1491', + 'label' => 'LA - Lampung', + ), + array( + 'value' => '1492', + 'label' => 'MA - Maluku', + ), + array( + 'value' => '1493', + 'label' => 'MU - Maluku Utara', + ), + array( + 'value' => '1495', + 'label' => 'NB - Nusa Tenggara Barat', + ), + array( + 'value' => '1496', + 'label' => 'NT - Nusa Tenggara Timur', + ), + array( + 'value' => '1497', + 'label' => 'PA - Papua', + ), + array( + 'value' => '1498', + 'label' => 'PB - Papua Barat', + ), + array( + 'value' => '1499', + 'label' => 'RI - Riau', + ), + array( + 'value' => '1504', + 'label' => 'SA - Sulawesi Utara', + ), + array( + 'value' => '1505', + 'label' => 'SB - Sumatera Barat', + ), + array( + 'value' => '1503', + 'label' => 'SG - Sulawesi Tenggara', + ), + array( + 'value' => '1501', + 'label' => 'SN - Sulawesi Selatan', + ), + array( + 'value' => '1500', + 'label' => 'SR - Sulawesi Barat', + ), + array( + 'value' => '1506', + 'label' => 'SS - Sumatera Selatan', + ), + array( + 'value' => '1502', + 'label' => 'ST - Sulawesi Tengah', + ), + array( + 'value' => '1507', + 'label' => 'SU - Sumatera Utara ', + ), + array( + 'value' => '1478', + 'label' => 'YO - DI Yogyakarta', + ), + array( + 'value' => '1438', + 'label' => 'AN - Andaman & Nicobar', + ), + array( + 'value' => '1439', + 'label' => 'AP - Andhra Pradesh', + ), + array( + 'value' => '1440', + 'label' => 'AR - Arunachal Pradesh', + ), + array( + 'value' => '1441', + 'label' => 'AS - Assam', + ), + array( + 'value' => '1442', + 'label' => 'BR - Bihar', + ), + array( + 'value' => '1443', + 'label' => 'CG - Chattisgarh', + ), + array( + 'value' => '1444', + 'label' => 'CH - Chandigarh', + ), + array( + 'value' => '1445', + 'label' => 'DD - Daman & Diu', + ), + array( + 'value' => '1446', + 'label' => 'DL - Delhi', + ), + array( + 'value' => '1447', + 'label' => 'DN - Dadra and Nagar Haveli', + ), + array( + 'value' => '1448', + 'label' => 'GA - Goa', + ), + array( + 'value' => '1449', + 'label' => 'GJ - Gujarat', + ), + array( + 'value' => '1450', + 'label' => 'HP - Himachal Pradesh', + ), + array( + 'value' => '1451', + 'label' => 'HR - Haryana', + ), + array( + 'value' => '1452', + 'label' => 'JH - Jharkhand', + ), + array( + 'value' => '1453', + 'label' => 'JK - Jammu & Kashmir', + ), + array( + 'value' => '1454', + 'label' => 'KA - Karnataka', + ), + array( + 'value' => '1455', + 'label' => 'KL - Kerala', + ), + array( + 'value' => '1456', + 'label' => 'LD - Lakshadweep', + ), + array( + 'value' => '1457', + 'label' => 'MH - Maharashtra', + ), + array( + 'value' => '1458', + 'label' => 'ML - Meghalaya', + ), + array( + 'value' => '1459', + 'label' => 'MN - Manipur', + ), + array( + 'value' => '1460', + 'label' => 'MP - Madhya Pradesh', + ), + array( + 'value' => '1461', + 'label' => 'MZ - Mizoram', + ), + array( + 'value' => '1462', + 'label' => 'NL - Nagaland', + ), + array( + 'value' => '1463', + 'label' => 'OR - Orissa', + ), + array( + 'value' => '1464', + 'label' => 'PB - Punjab', + ), + array( + 'value' => '1465', + 'label' => 'PY - Puducherry', + ), + array( + 'value' => '1466', + 'label' => 'RJ - Rajasthan', + ), + array( + 'value' => '1467', + 'label' => 'SK - Sikkim', + ), + array( + 'value' => '1468', + 'label' => 'TE - Telangana', + ), + array( + 'value' => '1469', + 'label' => 'TN - Tamil Nadu', + ), + array( + 'value' => '1470', + 'label' => 'TR - Tripura', + ), + array( + 'value' => '1471', + 'label' => 'UL - Uttarakhand', + ), + array( + 'value' => '1472', + 'label' => 'UP - Uttar Pradesh', + ), + array( + 'value' => '1473', + 'label' => 'WB - West Bengal', + ), + array( + 'value' => '518', + 'label' => 'AG - AGRIGENTO', + ), + array( + 'value' => '519', + 'label' => 'AL - ALESSANDRIA', + ), + array( + 'value' => '520', + 'label' => 'AN - ANCONA', + ), + array( + 'value' => '521', + 'label' => 'AO - AOSTA', + ), + array( + 'value' => '523', + 'label' => 'AP - ASCOLI PICENO', + ), + array( + 'value' => '562', + 'label' => 'AQ - L AQUILA', + ), + array( + 'value' => '522', + 'label' => 'AR - AREZZO', + ), + array( + 'value' => '524', + 'label' => 'AT - ASTI', + ), + array( + 'value' => '525', + 'label' => 'AV - AVELLINO', + ), + array( + 'value' => '526', + 'label' => 'BA - BARI', + ), + array( + 'value' => '530', + 'label' => 'BG - BERGAMO', + ), + array( + 'value' => '531', + 'label' => 'BI - BIELLA', + ), + array( + 'value' => '528', + 'label' => 'BL - BELLUNO', + ), + array( + 'value' => '529', + 'label' => 'BN - BENEVENTO', + ), + array( + 'value' => '532', + 'label' => 'BO - BOLOGNA', + ), + array( + 'value' => '535', + 'label' => 'BR - BRINDISI', + ), + array( + 'value' => '534', + 'label' => 'BS - BRESCIA', + ), + array( + 'value' => '527', + 'label' => 'BT - BARLETTA-ANDRIA-TRANI', + ), + array( + 'value' => '533', + 'label' => 'BZ - BOLZANO', + ), + array( + 'value' => '536', + 'label' => 'CA - CAGLIARI', + ), + array( + 'value' => '538', + 'label' => 'CB - CAMPOBASSO', + ), + array( + 'value' => '540', + 'label' => 'CE - CASERTA', + ), + array( + 'value' => '543', + 'label' => 'CH - CHIETI', + ), + array( + 'value' => '539', + 'label' => 'CI - CARBONIA-IGLESIAS', + ), + array( + 'value' => '537', + 'label' => 'CL - CALTANISSETTA', + ), + array( + 'value' => '548', + 'label' => 'CN - CUNEO', + ), + array( + 'value' => '544', + 'label' => 'CO - COMO', + ), + array( + 'value' => '546', + 'label' => 'CR - CREMONA', + ), + array( + 'value' => '545', + 'label' => 'CS - COSENZA', + ), + array( + 'value' => '541', + 'label' => 'CT - CATANIA', + ), + array( + 'value' => '542', + 'label' => 'CZ - CATANZARO', + ), + array( + 'value' => '549', + 'label' => 'EN - ENNA', + ), + array( + 'value' => '554', + 'label' => 'FC - FORLI-CESENA', + ), + array( + 'value' => '551', + 'label' => 'FE - FERRARA', + ), + array( + 'value' => '553', + 'label' => 'FG - FOGGIA', + ), + array( + 'value' => '552', + 'label' => 'FI - FIRENZE', + ), + array( + 'value' => '550', + 'label' => 'FM - FERMO', + ), + array( + 'value' => '555', + 'label' => 'FR - FROSINONE', + ), + array( + 'value' => '556', + 'label' => 'GE - GENOVA', + ), + array( + 'value' => '557', + 'label' => 'GO - GORIZIA', + ), + array( + 'value' => '558', + 'label' => 'GR - GROSSETO', + ), + array( + 'value' => '559', + 'label' => 'IM - Instant messaging', + ), + array( + 'value' => '560', + 'label' => 'IS - ISERNIA', + ), + array( + 'value' => '547', + 'label' => 'KR - CROTONE', + ), + array( + 'value' => '565', + 'label' => 'LC - LECCO', + ), + array( + 'value' => '564', + 'label' => 'LE - LECCE', + ), + array( + 'value' => '566', + 'label' => 'LI - LIVORNO', + ), + array( + 'value' => '567', + 'label' => 'LO - LODI', + ), + array( + 'value' => '563', + 'label' => 'LT - LATINA', + ), + array( + 'value' => '568', + 'label' => 'LU - LUCCA', + ), + array( + 'value' => '576', + 'label' => 'MB - MONZA e BRIANZA', + ), + array( + 'value' => '569', + 'label' => 'MC - MACERATA', + ), + array( + 'value' => '574', + 'label' => 'ME - MESSINA', + ), + array( + 'value' => '575', + 'label' => 'MI - MILANO', + ), + array( + 'value' => '570', + 'label' => 'MN - MANTOVA', + ), + array( + 'value' => '577', + 'label' => 'MO - MODENA', + ), + array( + 'value' => '571', + 'label' => 'MS - MASSA-CARRARA', + ), + array( + 'value' => '572', + 'label' => 'MT - MATERA', + ), + array( + 'value' => '578', + 'label' => 'NA - NAPOLI', + ), + array( + 'value' => '579', + 'label' => 'NO - NOVARA', + ), + array( + 'value' => '580', + 'label' => 'NU - NUORO', + ), + array( + 'value' => '581', + 'label' => 'OG - OGLIASTRA', + ), + array( + 'value' => '583', + 'label' => 'OR - ORISTANO', + ), + array( + 'value' => '582', + 'label' => 'OT - OLBIA-TEMPIO', + ), + array( + 'value' => '585', + 'label' => 'PA - PALERMO', + ), + array( + 'value' => '591', + 'label' => 'PC - PIACENZA', + ), + array( + 'value' => '584', + 'label' => 'PD - PADOVA', + ), + array( + 'value' => '590', + 'label' => 'PE - PESCARA', + ), + array( + 'value' => '588', + 'label' => 'PG - PERUGIA', + ), + array( + 'value' => '592', + 'label' => 'PI - PISA', + ), + array( + 'value' => '594', + 'label' => 'PN - PORDENONE', + ), + array( + 'value' => '596', + 'label' => 'PO - PRATO', + ), + array( + 'value' => '586', + 'label' => 'PR - PARMA', + ), + array( + 'value' => '593', + 'label' => 'PT - PISTOIA', + ), + array( + 'value' => '589', + 'label' => 'PU - PESARO e URBINO', + ), + array( + 'value' => '587', + 'label' => 'PV - PAVIA', + ), + array( + 'value' => '595', + 'label' => 'PZ - POTENZA', + ), + array( + 'value' => '598', + 'label' => 'RA - RAVENNA', + ), + array( + 'value' => '599', + 'label' => 'RC - REGGIO CALABRIA', + ), + array( + 'value' => '600', + 'label' => 'RE - REGGIO NELL EMILIA', + ), + array( + 'value' => '597', + 'label' => 'RG - RAGUSA', + ), + array( + 'value' => '601', + 'label' => 'RI - RIETI', + ), + array( + 'value' => '603', + 'label' => 'RM - ROMA', + ), + array( + 'value' => '602', + 'label' => 'RN - RIMINI', + ), + array( + 'value' => '604', + 'label' => 'RO - ROVIGO', + ), + array( + 'value' => '605', + 'label' => 'SA - SALERNO', + ), + array( + 'value' => '608', + 'label' => 'SI - SIENA', + ), + array( + 'value' => '610', + 'label' => 'SO - SONDRIO', + ), + array( + 'value' => '561', + 'label' => 'SP - LA SPEZIA', + ), + array( + 'value' => '609', + 'label' => 'SR - SIRACUSA', + ), + array( + 'value' => '606', + 'label' => 'SS - SASSARI', + ), + array( + 'value' => '607', + 'label' => 'SV - SAVONA', + ), + array( + 'value' => '611', + 'label' => 'TA - TARANTO', + ), + array( + 'value' => '612', + 'label' => 'TE - TERAMO', + ), + array( + 'value' => '616', + 'label' => 'TN - TRENTO', + ), + array( + 'value' => '614', + 'label' => 'TO - TORINO', + ), + array( + 'value' => '615', + 'label' => 'TP - TRAPANI', + ), + array( + 'value' => '613', + 'label' => 'TR - TERNI', + ), + array( + 'value' => '618', + 'label' => 'TS - TRIESTE', + ), + array( + 'value' => '617', + 'label' => 'TV - TREVISO', + ), + array( + 'value' => '619', + 'label' => 'UD - UDINE', + ), + array( + 'value' => '620', + 'label' => 'VA - VARESE', + ), + array( + 'value' => '622', + 'label' => 'VB - VERBANO-CUSIO-OSSOLA', + ), + array( + 'value' => '623', + 'label' => 'VC - VERCELLI', + ), + array( + 'value' => '621', + 'label' => 'VE - VENEZIA', + ), + array( + 'value' => '626', + 'label' => 'VI - VICENZA', + ), + array( + 'value' => '624', + 'label' => 'VR - VERONA', + ), + array( + 'value' => '573', + 'label' => 'VS - MEDIO CAMPIDANO', + ), + array( + 'value' => '627', + 'label' => 'VT - VITERBO', + ), + array( + 'value' => '625', + 'label' => 'VV - VIBO VALENTIA', + ), + array( + 'value' => '1691', + 'label' => '01 - 北海道', + ), + array( + 'value' => '1692', + 'label' => '02 - 青森県', + ), + array( + 'value' => '1693', + 'label' => '03 - 岩手県', + ), + array( + 'value' => '1694', + 'label' => '04 - 宮城県', + ), + array( + 'value' => '1695', + 'label' => '05 - 秋田県', + ), + array( + 'value' => '1696', + 'label' => '06 - 山形県', + ), + array( + 'value' => '1697', + 'label' => '07 - 福島県', + ), + array( + 'value' => '1698', + 'label' => '08 - 茨城県', + ), + array( + 'value' => '1699', + 'label' => '09 - 栃木県', + ), + array( + 'value' => '1700', + 'label' => '10 - 群馬県', + ), + array( + 'value' => '1701', + 'label' => '11 - 埼玉県', + ), + array( + 'value' => '1702', + 'label' => '12 - 千葉県', + ), + array( + 'value' => '1703', + 'label' => '13 - 東京都', + ), + array( + 'value' => '1704', + 'label' => '14 - 神奈川県', + ), + array( + 'value' => '1705', + 'label' => '15 - 新潟県', + ), + array( + 'value' => '1706', + 'label' => '16 - 富山県', + ), + array( + 'value' => '1707', + 'label' => '17 - 石川県', + ), + array( + 'value' => '1708', + 'label' => '18 - 福井県', + ), + array( + 'value' => '1709', + 'label' => '19 - 山梨県', + ), + array( + 'value' => '1710', + 'label' => '20 - 長野県', + ), + array( + 'value' => '1711', + 'label' => '21 - 岐阜県', + ), + array( + 'value' => '1712', + 'label' => '22 - 静岡県', + ), + array( + 'value' => '1713', + 'label' => '23 - 愛知県', + ), + array( + 'value' => '1714', + 'label' => '24 - 三重県', + ), + array( + 'value' => '1715', + 'label' => '25 - 滋賀県', + ), + array( + 'value' => '1716', + 'label' => '26 - 京都府', + ), + array( + 'value' => '1717', + 'label' => '27 - 大阪府', + ), + array( + 'value' => '1718', + 'label' => '28 - 兵庫県', + ), + array( + 'value' => '1719', + 'label' => '29 - 奈良県', + ), + array( + 'value' => '1720', + 'label' => '30 - 和歌山県', + ), + array( + 'value' => '1721', + 'label' => '31 - 鳥取県', + ), + array( + 'value' => '1722', + 'label' => '32 - 島根県', + ), + array( + 'value' => '1723', + 'label' => '33 - 岡山県', + ), + array( + 'value' => '1724', + 'label' => '34 - 広島県', + ), + array( + 'value' => '1725', + 'label' => '35 - 山口県', + ), + array( + 'value' => '1726', + 'label' => '36 - 徳島県', + ), + array( + 'value' => '1727', + 'label' => '37 - 香川県', + ), + array( + 'value' => '1728', + 'label' => '38 - 愛媛県', + ), + array( + 'value' => '1729', + 'label' => '39 - 高知県', + ), + array( + 'value' => '1730', + 'label' => '40 - 福岡県', + ), + array( + 'value' => '1731', + 'label' => '41 - 佐賀県', + ), + array( + 'value' => '1732', + 'label' => '42 - 長崎県', + ), + array( + 'value' => '1733', + 'label' => '43 - 熊本県', + ), + array( + 'value' => '1734', + 'label' => '44 - 大分県', + ), + array( + 'value' => '1735', + 'label' => '45 - 宮崎県', + ), + array( + 'value' => '1736', + 'label' => '46 - 鹿児島県', + ), + array( + 'value' => '1737', + 'label' => '47 - 沖縄県', + ), + array( + 'value' => '628', + 'label' => 'LU0001 - Clervaux', + ), + array( + 'value' => '629', + 'label' => 'LU0002 - Diekirch', + ), + array( + 'value' => '630', + 'label' => 'LU0003 - Redange', + ), + array( + 'value' => '631', + 'label' => 'LU0004 - Vianden', + ), + array( + 'value' => '632', + 'label' => 'LU0005 - Wiltz', + ), + array( + 'value' => '633', + 'label' => 'LU0006 - Echternach', + ), + array( + 'value' => '634', + 'label' => 'LU0007 - Grevenmacher', + ), + array( + 'value' => '635', + 'label' => 'LU0008 - Remich', + ), + array( + 'value' => '636', + 'label' => 'LU0009 - Capellen', + ), + array( + 'value' => '637', + 'label' => 'LU0010 - Esch-sur-Alzette', + ), + array( + 'value' => '638', + 'label' => 'LU0011 - Luxembourg', + ), + array( + 'value' => '639', + 'label' => 'LU0012 - Mersch', + ), + array( + 'value' => '640', + 'label' => 'MA - Province de Benslimane', + ), + array( + 'value' => '641', + 'label' => 'MA1 - Province de Berrechid', + ), + array( + 'value' => '654', + 'label' => 'MA10 - Province de Sidi Slimane', + ), + array( + 'value' => '655', + 'label' => 'MA11 - Préfecture de Casablanca', + ), + array( + 'value' => '656', + 'label' => 'MA12 - Préfecture de Mohammédia', + ), + array( + 'value' => '657', + 'label' => 'MA13 - Province de Médiouna', + ), + array( + 'value' => '658', + 'label' => 'MA14 - Province de Nouaceur', + ), + array( + 'value' => '664', + 'label' => 'MA15 - Province de Boujdour', + ), + array( + 'value' => '659', + 'label' => 'MA15 - Province d\'Assa-Zag', + ), + array( + 'value' => '717', + 'label' => 'MA15A - Province d\'Assa-Zag', + ), + array( + 'value' => '665', + 'label' => 'MA16 - Province de Lâayoune', + ), + array( + 'value' => '660', + 'label' => 'MA16 - Province d\'Es-Semara', + ), + array( + 'value' => '718', + 'label' => 'MA16A - Province d\'Es-Semara', + ), + array( + 'value' => '666', + 'label' => 'MA17 - Province de Tarfaya', + ), + array( + 'value' => '661', + 'label' => 'MA17A - Province de Guelmim', + ), + array( + 'value' => '667', + 'label' => 'MA18 - Préfecture de Marrakech', + ), + array( + 'value' => '662', + 'label' => 'MA18 - Province de Tata', + ), + array( + 'value' => '719', + 'label' => 'MA18A - Préfecture de Marrakech', + ), + array( + 'value' => '663', + 'label' => 'MA19 - Province de Tan-Tan', + ), + array( + 'value' => '668', + 'label' => 'MA19 - Province d\'Al Haouz', + ), + array( + 'value' => '720', + 'label' => 'MA19A - Province de Tan-Tan', + ), + array( + 'value' => '721', + 'label' => 'MA19B - Province de Tan-Tan', + ), + array( + 'value' => '642', + 'label' => 'MA2 - Province de Khouribga', + ), + array( + 'value' => '669', + 'label' => 'MA20 - Province de Chichaoua', + ), + array( + 'value' => '670', + 'label' => 'MA21 - Province d\'El Kelâa des Sraghna', + ), + array( + 'value' => '671', + 'label' => 'MA22 - Province d\'Essaouira', + ), + array( + 'value' => '672', + 'label' => 'MA23 - Province de Rehamna', + ), + array( + 'value' => '673', + 'label' => 'MA24 - Préfecture de Meknès', + ), + array( + 'value' => '674', + 'label' => 'MA25 - Province d’El Hajeb', + ), + array( + 'value' => '675', + 'label' => 'MA26 - Province d\'Errachidia', + ), + array( + 'value' => '676', + 'label' => 'MA27 - Province d’Ifrane', + ), + array( + 'value' => '677', + 'label' => 'MA28 - Province de Khénifra', + ), + array( + 'value' => '678', + 'label' => 'MA29 - Province de Midelt', + ), + array( + 'value' => '643', + 'label' => 'MA3 - Province de Settat', + ), + array( + 'value' => '679', + 'label' => 'MA30 - Préfecture d\'Oujda-Angad', + ), + array( + 'value' => '680', + 'label' => 'MA31 - Province de Berkane', + ), + array( + 'value' => '681', + 'label' => 'MA32 - Province de Driouch', + ), + array( + 'value' => '682', + 'label' => 'MA33 - Province de Figuig', + ), + array( + 'value' => '683', + 'label' => 'MA34 - Province de Jerada', + ), + array( + 'value' => '684', + 'label' => 'MA35 - Province de Nador', + ), + array( + 'value' => '685', + 'label' => 'MA36 - Province de Taourirt', + ), + array( + 'value' => '686', + 'label' => 'MA37 - Province d\'Aousserd', + ), + array( + 'value' => '687', + 'label' => 'MA38 - Province d\'Oued Ed-Dahab', + ), + array( + 'value' => '688', + 'label' => 'MA39 - Préfecture de Rabat', + ), + array( + 'value' => '644', + 'label' => 'MA4 - Province d\'El Jadida', + ), + array( + 'value' => '689', + 'label' => 'MA40 - Préfecture de Skhirat-Témara', + ), + array( + 'value' => '690', + 'label' => 'MA41 - Préfecture de Salé', + ), + array( + 'value' => '691', + 'label' => 'MA42 - Province de Khémisset', + ), + array( + 'value' => '692', + 'label' => 'MA43 - Préfecture d\'Agadir Ida-Outanane', + ), + array( + 'value' => '693', + 'label' => 'MA44 - Préfecture d\'Inezgane-Aït Melloul', + ), + array( + 'value' => '694', + 'label' => 'MA45 - Province de Chtouka-Aït Baha', + ), + array( + 'value' => '695', + 'label' => 'MA46 - Province d\'Ouarzazate', + ), + array( + 'value' => '696', + 'label' => 'MA47 - Province de Sidi Ifni', + ), + array( + 'value' => '697', + 'label' => 'MA48 - Province de Taroudant', + ), + array( + 'value' => '698', + 'label' => 'MA49 - Province de Tinghir', + ), + array( + 'value' => '645', + 'label' => 'MA5 - Province de Safi', + ), + array( + 'value' => '699', + 'label' => 'MA50 - Province de Tiznit', + ), + array( + 'value' => '700', + 'label' => 'MA51 - Province de Zagora', + ), + array( + 'value' => '701', + 'label' => 'MA52 - Province d\'Azilal', + ), + array( + 'value' => '702', + 'label' => 'MA53 - Province de Beni Mellal', + ), + array( + 'value' => '703', + 'label' => 'MA54 - Province de Fquih Ben Salah', + ), + array( + 'value' => '704', + 'label' => 'MA55 - Préfecture de M\'diq-Fnideq', + ), + array( + 'value' => '705', + 'label' => 'MA56 - Préfecture de Tanger-Asilah', + ), + array( + 'value' => '706', + 'label' => 'MA57 - Province de Chefchaouen', + ), + array( + 'value' => '707', + 'label' => 'MA58 - Province de Fahs-Anjra', + ), + array( + 'value' => '708', + 'label' => 'MA59 - Province de Larache', + ), + array( + 'value' => '646', + 'label' => 'MA6 - Province de Sidi Bennour', + ), + array( + 'value' => '709', + 'label' => 'MA60 - Province d\'Ouezzane', + ), + array( + 'value' => '710', + 'label' => 'MA61 - Province de Tétouan', + ), + array( + 'value' => '711', + 'label' => 'MA62 - Province de Guercif', + ), + array( + 'value' => '712', + 'label' => 'MA63 - Province d\'Al Hoceïma', + ), + array( + 'value' => '713', + 'label' => 'MA64 - Province de Taounate', + ), + array( + 'value' => '714', + 'label' => 'MA65 - Province de Taza', + ), + array( + 'value' => '715', + 'label' => 'MA6A - Préfecture de Fès', + ), + array( + 'value' => '648', + 'label' => 'MA6B - Préfecture de Fès', + ), + array( + 'value' => '647', + 'label' => 'MA7 - Province de Youssoufia', + ), + array( + 'value' => '716', + 'label' => 'MA7A - Province de Boulemane', + ), + array( + 'value' => '649', + 'label' => 'MA7B - Province de Boulemane', + ), + array( + 'value' => '650', + 'label' => 'MA8 - Province de Moulay Yacoub', + ), + array( + 'value' => '652', + 'label' => 'MA8A - Province de Kénitra', + ), + array( + 'value' => '651', + 'label' => 'MA9 - Province de Sefrou', + ), + array( + 'value' => '653', + 'label' => 'MA9A - Province de Sidi Kacem', + ), + array( + 'value' => '1509', + 'label' => 'AGS - Aguascalientes', + ), + array( + 'value' => '1510', + 'label' => 'BCN - Baja California Norte', + ), + array( + 'value' => '1511', + 'label' => 'BCS - Baja California Sur', + ), + array( + 'value' => '1512', + 'label' => 'CAM - Campeche', + ), + array( + 'value' => '1514', + 'label' => 'CHI - Chihuahua', + ), + array( + 'value' => '1513', + 'label' => 'CHP - Chiapas', + ), + array( + 'value' => '1508', + 'label' => 'CMX - Ciudad de México', + ), + array( + 'value' => '1515', + 'label' => 'COA - Coahuila', + ), + array( + 'value' => '1516', + 'label' => 'COL - Colima', + ), + array( + 'value' => '1517', + 'label' => 'DUR - Durango', + ), + array( + 'value' => '1519', + 'label' => 'GRO - Guerrero', + ), + array( + 'value' => '1518', + 'label' => 'GTO - Guanajuato', + ), + array( + 'value' => '1520', + 'label' => 'HGO - Hidalgo', + ), + array( + 'value' => '1521', + 'label' => 'JAL - Jalisco', + ), + array( + 'value' => '1522', + 'label' => 'MEX - México', + ), + array( + 'value' => '1523', + 'label' => 'MIC - Michoacán de Ocampo', + ), + array( + 'value' => '1524', + 'label' => 'MOR - Morelos', + ), + array( + 'value' => '1525', + 'label' => 'NAY - Nayarit', + ), + array( + 'value' => '1526', + 'label' => 'NLE - Nuevo León', + ), + array( + 'value' => '1527', + 'label' => 'OAX - Oaxaca', + ), + array( + 'value' => '1528', + 'label' => 'PUE - Puebla', + ), + array( + 'value' => '1529', + 'label' => 'QRO - Querétaro', + ), + array( + 'value' => '1530', + 'label' => 'ROO - Quintana Roo', + ), + array( + 'value' => '1532', + 'label' => 'SIN - Sinaloa', + ), + array( + 'value' => '1531', + 'label' => 'SLP - San Luis Potosí', + ), + array( + 'value' => '1533', + 'label' => 'SON - Sonora', + ), + array( + 'value' => '1534', + 'label' => 'TAB - Tabasco', + ), + array( + 'value' => '1535', + 'label' => 'TAM - Tamaulipas', + ), + array( + 'value' => '1536', + 'label' => 'TLX - Tlaxcala', + ), + array( + 'value' => '1537', + 'label' => 'VER - Veracruz', + ), + array( + 'value' => '1538', + 'label' => 'YUC - Yucatán', + ), + array( + 'value' => '1539', + 'label' => 'ZAC - Zacatecas', + ), + array( + 'value' => '724', + 'label' => 'DR - Drenthe', + ), + array( + 'value' => '727', + 'label' => 'FL - Flevoland', + ), + array( + 'value' => '723', + 'label' => 'FR - Friesland', + ), + array( + 'value' => '726', + 'label' => 'GD - Gelderland', + ), + array( + 'value' => '722', + 'label' => 'GR - Groningen', + ), + array( + 'value' => '733', + 'label' => 'LB - Limburg', + ), + array( + 'value' => '732', + 'label' => 'NB - Noord-Brabant', + ), + array( + 'value' => '729', + 'label' => 'NH - Noord-Holland', + ), + array( + 'value' => '725', + 'label' => 'OV - Overijssel', + ), + array( + 'value' => '728', + 'label' => 'UT - Utrecht', + ), + array( + 'value' => '730', + 'label' => 'ZH - Zuid-Holland', + ), + array( + 'value' => '731', + 'label' => 'ZL - Zeeland', + ), + array( + 'value' => '734', + 'label' => 'PA-1 - Bocas del Toro', + ), + array( + 'value' => '743', + 'label' => 'PA-13 - Panamá Oeste', + ), + array( + 'value' => '735', + 'label' => 'PA-2 - Coclé', + ), + array( + 'value' => '736', + 'label' => 'PA-3 - Colón', + ), + array( + 'value' => '737', + 'label' => 'PA-4 - Chiriquí', + ), + array( + 'value' => '738', + 'label' => 'PA-5 - Darién', + ), + array( + 'value' => '739', + 'label' => 'PA-6 - Herrera', + ), + array( + 'value' => '740', + 'label' => 'PA-7 - Los Santos', + ), + array( + 'value' => '741', + 'label' => 'PA-8 - Panamá', + ), + array( + 'value' => '742', + 'label' => 'PA-9 - Veraguas', + ), + array( + 'value' => '744', + 'label' => '0101 - Chachapoyas', + ), + array( + 'value' => '745', + 'label' => '0102 - Bagua', + ), + array( + 'value' => '746', + 'label' => '0103 - Bongará', + ), + array( + 'value' => '747', + 'label' => '0104 - Condorcanqui', + ), + array( + 'value' => '748', + 'label' => '0105 - Luya', + ), + array( + 'value' => '749', + 'label' => '0106 - Rodríguez de Mendoza', + ), + array( + 'value' => '750', + 'label' => '0107 - Utcubamba', + ), + array( + 'value' => '751', + 'label' => '0201 - Huaraz', + ), + array( + 'value' => '752', + 'label' => '0202 - Aija', + ), + array( + 'value' => '753', + 'label' => '0203 - Antonio Raymondi', + ), + array( + 'value' => '754', + 'label' => '0204 - Asunción', + ), + array( + 'value' => '755', + 'label' => '0205 - Bolognesi', + ), + array( + 'value' => '756', + 'label' => '0206 - Carhuaz', + ), + array( + 'value' => '757', + 'label' => '0207 - Carlos Fermín Fitzcarrald', + ), + array( + 'value' => '758', + 'label' => '0208 - Casma', + ), + array( + 'value' => '759', + 'label' => '0209 - Corongo', + ), + array( + 'value' => '760', + 'label' => '0210 - Huari', + ), + array( + 'value' => '761', + 'label' => '0211 - Huarmey', + ), + array( + 'value' => '762', + 'label' => '0212 - Huaylas', + ), + array( + 'value' => '763', + 'label' => '0213 - Mariscal Luzuriaga', + ), + array( + 'value' => '764', + 'label' => '0214 - Ocros', + ), + array( + 'value' => '765', + 'label' => '0215 - Pallasca', + ), + array( + 'value' => '766', + 'label' => '0216 - Pomabamba', + ), + array( + 'value' => '767', + 'label' => '0217 - Recuay', + ), + array( + 'value' => '768', + 'label' => '0218 - Papá', + ), + array( + 'value' => '769', + 'label' => '0219 - Sihuas', + ), + array( + 'value' => '770', + 'label' => '0220 - Yungay', + ), + array( + 'value' => '771', + 'label' => '0301 - Abancay', + ), + array( + 'value' => '772', + 'label' => '0302 - Andahuaylas', + ), + array( + 'value' => '773', + 'label' => '0303 - Antabamba', + ), + array( + 'value' => '774', + 'label' => '0304 - Aymaraes', + ), + array( + 'value' => '775', + 'label' => '0305 - Cotabambas', + ), + array( + 'value' => '776', + 'label' => '0306 - Chincheros', + ), + array( + 'value' => '777', + 'label' => '0307 - Grau', + ), + array( + 'value' => '778', + 'label' => '0401 - Arequipa', + ), + array( + 'value' => '779', + 'label' => '0402 - Camaná', + ), + array( + 'value' => '780', + 'label' => '0403 - Caravelí', + ), + array( + 'value' => '781', + 'label' => '0404 - Castilla', + ), + array( + 'value' => '782', + 'label' => '0405 - Caylloma', + ), + array( + 'value' => '783', + 'label' => '0406 - Condesuyos', + ), + array( + 'value' => '784', + 'label' => '0407 - Islay', + ), + array( + 'value' => '785', + 'label' => '0408 - La Unión', + ), + array( + 'value' => '786', + 'label' => '0501 - Huamanga', + ), + array( + 'value' => '787', + 'label' => '0502 - Cangallo', + ), + array( + 'value' => '788', + 'label' => '0503 - Huanca Sancos', + ), + array( + 'value' => '789', + 'label' => '0504 - Huanta', + ), + array( + 'value' => '790', + 'label' => '0505 - La Mar', + ), + array( + 'value' => '791', + 'label' => '0506 - Lucanas', + ), + array( + 'value' => '792', + 'label' => '0507 - Parinacochas', + ), + array( + 'value' => '793', + 'label' => '0508 - Páucar del Sara Sara', + ), + array( + 'value' => '794', + 'label' => '0509 - Sucre', + ), + array( + 'value' => '795', + 'label' => '0510 - Víctor Fajardo', + ), + array( + 'value' => '796', + 'label' => '0511 - Vilcas Huamán', + ), + array( + 'value' => '797', + 'label' => '0601 - Cajamarca', + ), + array( + 'value' => '798', + 'label' => '0602 - Cajabamba', + ), + array( + 'value' => '799', + 'label' => '0603 - Celendín', + ), + array( + 'value' => '800', + 'label' => '0604 - Chota', + ), + array( + 'value' => '801', + 'label' => '0605 - Contumazá', + ), + array( + 'value' => '802', + 'label' => '0606 - Cutervo', + ), + array( + 'value' => '803', + 'label' => '0607 - Hualgayoc', + ), + array( + 'value' => '804', + 'label' => '0608 - Jaén', + ), + array( + 'value' => '805', + 'label' => '0609 - San Ignacio', + ), + array( + 'value' => '806', + 'label' => '0610 - San Marcos', + ), + array( + 'value' => '807', + 'label' => '0611 - San Miguel', + ), + array( + 'value' => '808', + 'label' => '0612 - San Pablo', + ), + array( + 'value' => '809', + 'label' => '0613 - Santa Cruz', + ), + array( + 'value' => '810', + 'label' => '0701 - Callao', + ), + array( + 'value' => '811', + 'label' => '0801 - Cusco', + ), + array( + 'value' => '812', + 'label' => '0802 - Acomayo', + ), + array( + 'value' => '813', + 'label' => '0803 - Anta', + ), + array( + 'value' => '814', + 'label' => '0804 - Calca', + ), + array( + 'value' => '815', + 'label' => '0805 - Canas', + ), + array( + 'value' => '816', + 'label' => '0806 - Canchis', + ), + array( + 'value' => '817', + 'label' => '0807 - Chumbivilcas', + ), + array( + 'value' => '818', + 'label' => '0808 - Espinar', + ), + array( + 'value' => '819', + 'label' => '0809 - La Convención', + ), + array( + 'value' => '820', + 'label' => '0810 - Paruro', + ), + array( + 'value' => '821', + 'label' => '0811 - Paucartambo', + ), + array( + 'value' => '822', + 'label' => '0812 - Quispicanchi', + ), + array( + 'value' => '823', + 'label' => '0813 - Urubamba', + ), + array( + 'value' => '824', + 'label' => '0901 - Huancavelica', + ), + array( + 'value' => '825', + 'label' => '0902 - Acobamba', + ), + array( + 'value' => '826', + 'label' => '0903 - Angaraes', + ), + array( + 'value' => '827', + 'label' => '0904 - Castrovirreyna', + ), + array( + 'value' => '828', + 'label' => '0905 - Churcampa', + ), + array( + 'value' => '829', + 'label' => '0906 - Huaytará', + ), + array( + 'value' => '830', + 'label' => '0907 - Tayacaja', + ), + array( + 'value' => '831', + 'label' => '1001 - Huánuco', + ), + array( + 'value' => '832', + 'label' => '1002 - Ambón', + ), + array( + 'value' => '833', + 'label' => '1003 - Dos de Mayo', + ), + array( + 'value' => '834', + 'label' => '1004 - Huacaybamba', + ), + array( + 'value' => '835', + 'label' => '1005 - Huamalíes', + ), + array( + 'value' => '836', + 'label' => '1006 - Leoncio Prado', + ), + array( + 'value' => '837', + 'label' => '1007 - Marañón', + ), + array( + 'value' => '838', + 'label' => '1008 - Pachitea', + ), + array( + 'value' => '839', + 'label' => '1009 - Puerto Inca', + ), + array( + 'value' => '840', + 'label' => '1010 - Lauricocha', + ), + array( + 'value' => '841', + 'label' => '1011 - Yarowilca', + ), + array( + 'value' => '842', + 'label' => '1101 - Ica', + ), + array( + 'value' => '843', + 'label' => '1102 - Chincha', + ), + array( + 'value' => '844', + 'label' => '1103 - Nazca', + ), + array( + 'value' => '845', + 'label' => '1104 - Palpa', + ), + array( + 'value' => '846', + 'label' => '1105 - Pisco', + ), + array( + 'value' => '847', + 'label' => '1201 - Huancayo', + ), + array( + 'value' => '848', + 'label' => '1202 - Concepción', + ), + array( + 'value' => '849', + 'label' => '1203 - Chanchamayo', + ), + array( + 'value' => '850', + 'label' => '1204 - Jauja', + ), + array( + 'value' => '851', + 'label' => '1205 - Junín', + ), + array( + 'value' => '852', + 'label' => '1206 - Satipo', + ), + array( + 'value' => '853', + 'label' => '1207 - Tarma', + ), + array( + 'value' => '854', + 'label' => '1208 - Yauli', + ), + array( + 'value' => '855', + 'label' => '1209 - Chupaca', + ), + array( + 'value' => '856', + 'label' => '1301 - Trujillo', + ), + array( + 'value' => '857', + 'label' => '1302 - Ascope', + ), + array( + 'value' => '858', + 'label' => '1303 - Bolívar', + ), + array( + 'value' => '859', + 'label' => '1304 - Chepén', + ), + array( + 'value' => '860', + 'label' => '1305 - Julcán', + ), + array( + 'value' => '861', + 'label' => '1306 - Otuzco', + ), + array( + 'value' => '862', + 'label' => '1307 - Pacasmayo', + ), + array( + 'value' => '863', + 'label' => '1308 - Pataz', + ), + array( + 'value' => '864', + 'label' => '1309 - Sánchez Carrión', + ), + array( + 'value' => '865', + 'label' => '1310 - Santiago de Chuco', + ), + array( + 'value' => '866', + 'label' => '1311 - Gran Chimú', + ), + array( + 'value' => '867', + 'label' => '1312 - Virú', + ), + array( + 'value' => '868', + 'label' => '1401 - Chiclayo', + ), + array( + 'value' => '869', + 'label' => '1402 - Ferreñafe', + ), + array( + 'value' => '870', + 'label' => '1403 - Lambayeque', + ), + array( + 'value' => '871', + 'label' => '1501 - Lima', + ), + array( + 'value' => '872', + 'label' => '1502 - Huaura', + ), + array( + 'value' => '873', + 'label' => '1503 - Barranca', + ), + array( + 'value' => '874', + 'label' => '1504 - Cajatambo', + ), + array( + 'value' => '875', + 'label' => '1505 - Canta', + ), + array( + 'value' => '876', + 'label' => '1506 - Cañete', + ), + array( + 'value' => '877', + 'label' => '1507 - Huaral', + ), + array( + 'value' => '878', + 'label' => '1508 - Huarochirí', + ), + array( + 'value' => '879', + 'label' => '1509 - Oyón', + ), + array( + 'value' => '880', + 'label' => '1510 - Yauyos', + ), + array( + 'value' => '881', + 'label' => '1601 - Maynas', + ), + array( + 'value' => '882', + 'label' => '1602 - Alto Amazonas', + ), + array( + 'value' => '883', + 'label' => '1603 - Loreto', + ), + array( + 'value' => '884', + 'label' => '1604 - Mariscal Ramón Castilla', + ), + array( + 'value' => '885', + 'label' => '1605 - Requena', + ), + array( + 'value' => '886', + 'label' => '1606 - Ucayali', + ), + array( + 'value' => '887', + 'label' => '1607 - Datem del Marañón', + ), + array( + 'value' => '888', + 'label' => '1701 - Tambopata', + ), + array( + 'value' => '889', + 'label' => '1702 - Manú', + ), + array( + 'value' => '890', + 'label' => '1703 - Tahuamanu', + ), + array( + 'value' => '891', + 'label' => '1801 - Mariscal Nieto', + ), + array( + 'value' => '892', + 'label' => '1802 - General Sánchez Cerro', + ), + array( + 'value' => '893', + 'label' => '1803 - Ilo', + ), + array( + 'value' => '894', + 'label' => '1901 - Pasco', + ), + array( + 'value' => '895', + 'label' => '1902 - Daniel Alcides Carrión', + ), + array( + 'value' => '896', + 'label' => '1903 - Oxapampa', + ), + array( + 'value' => '897', + 'label' => '2001 - Piura', + ), + array( + 'value' => '898', + 'label' => '2002 - Ayabaca', + ), + array( + 'value' => '899', + 'label' => '2003 - Huancabamba', + ), + array( + 'value' => '900', + 'label' => '2004 - Morropón', + ), + array( + 'value' => '901', + 'label' => '2005 - Paita', + ), + array( + 'value' => '902', + 'label' => '2006 - Sullana', + ), + array( + 'value' => '903', + 'label' => '2007 - Talara', + ), + array( + 'value' => '904', + 'label' => '2008 - Sechura', + ), + array( + 'value' => '905', + 'label' => '2101 - Puno', + ), + array( + 'value' => '906', + 'label' => '2102 - Azángaro', + ), + array( + 'value' => '907', + 'label' => '2103 - Carabaya', + ), + array( + 'value' => '908', + 'label' => '2104 - Chucuito', + ), + array( + 'value' => '909', + 'label' => '2105 - El Collao', + ), + array( + 'value' => '910', + 'label' => '2106 - Huancané', + ), + array( + 'value' => '911', + 'label' => '2107 - Lampa', + ), + array( + 'value' => '912', + 'label' => '2108 - Melgar', + ), + array( + 'value' => '913', + 'label' => '2109 - Moho', + ), + array( + 'value' => '914', + 'label' => '2110 - San Antonio de Putina', + ), + array( + 'value' => '915', + 'label' => '2111 - San Román', + ), + array( + 'value' => '916', + 'label' => '2112 - Sandia', + ), + array( + 'value' => '917', + 'label' => '2113 - Yunguyo', + ), + array( + 'value' => '918', + 'label' => '2201 - Moyobamba', + ), + array( + 'value' => '919', + 'label' => '2202 - Bellavista', + ), + array( + 'value' => '920', + 'label' => '2203 - El Dorado', + ), + array( + 'value' => '921', + 'label' => '2204 - Huallaga', + ), + array( + 'value' => '922', + 'label' => '2205 - Lamas', + ), + array( + 'value' => '923', + 'label' => '2206 - Mariscal Cáceres', + ), + array( + 'value' => '924', + 'label' => '2207 - Picota', + ), + array( + 'value' => '925', + 'label' => '2208 - La Rioja', + ), + array( + 'value' => '926', + 'label' => '2209 - San Martín', + ), + array( + 'value' => '927', + 'label' => '2210 - Tocache', + ), + array( + 'value' => '928', + 'label' => '2301 - Tacna', + ), + array( + 'value' => '929', + 'label' => '2302 - Candarave', + ), + array( + 'value' => '930', + 'label' => '2303 - Jorge Basadre', + ), + array( + 'value' => '931', + 'label' => '2304 - Tarata', + ), + array( + 'value' => '932', + 'label' => '2401 - Tumbes', + ), + array( + 'value' => '933', + 'label' => '2402 - Contralmirante Villar', + ), + array( + 'value' => '934', + 'label' => '2403 - Zarumilla', + ), + array( + 'value' => '935', + 'label' => '2501 - Coronel Portillo', + ), + array( + 'value' => '936', + 'label' => '2502 - Atalaya', + ), + array( + 'value' => '937', + 'label' => '2503 - Padre Abad', + ), + array( + 'value' => '938', + 'label' => '2504 - Purús', + ), + array( + 'value' => '940', + 'label' => 'PT-AC - Azores', + ), + array( + 'value' => '951', + 'label' => 'PT-AML - Área Metropolitana de Lisboa', + ), + array( + 'value' => '939', + 'label' => 'PT-AV - Aveiro', + ), + array( + 'value' => '943', + 'label' => 'PT-BA - Bragança', + ), + array( + 'value' => '941', + 'label' => 'PT-BE - Beja', + ), + array( + 'value' => '942', + 'label' => 'PT-BR - Braga', + ), + array( + 'value' => '944', + 'label' => 'PT-CB - Castelo Branco', + ), + array( + 'value' => '945', + 'label' => 'PT-CO - Coimbra', + ), + array( + 'value' => '946', + 'label' => 'PT-EV - Évora', + ), + array( + 'value' => '947', + 'label' => 'PT-FA - Faro', + ), + array( + 'value' => '948', + 'label' => 'PT-GU - Guarda', + ), + array( + 'value' => '949', + 'label' => 'PT-LE - Leiria', + ), + array( + 'value' => '950', + 'label' => 'PT-LI - Lisboa', + ), + array( + 'value' => '952', + 'label' => 'PT-MA - Madeira', + ), + array( + 'value' => '953', + 'label' => 'PT-PA - Portalegre', + ), + array( + 'value' => '954', + 'label' => 'PT-PO - Porto', + ), + array( + 'value' => '955', + 'label' => 'PT-SA - Santarém', + ), + array( + 'value' => '956', + 'label' => 'PT-SE - Setúbal', + ), + array( + 'value' => '957', + 'label' => 'PT-VC - Viana Do Castelo', + ), + array( + 'value' => '959', + 'label' => 'PT-VI - Viseu', + ), + array( + 'value' => '958', + 'label' => 'PT-VR - Vila Real', + ), + array( + 'value' => '960', + 'label' => 'AB - Alba', + ), + array( + 'value' => '962', + 'label' => 'AG - Argeș', + ), + array( + 'value' => '961', + 'label' => 'AR - Arad', + ), + array( + 'value' => '963', + 'label' => 'BC - Bacău', + ), + array( + 'value' => '964', + 'label' => 'BH - Bihor', + ), + array( + 'value' => '965', + 'label' => 'BN - Bistrița-Năsăud', + ), + array( + 'value' => '968', + 'label' => 'BR - Brăila', + ), + array( + 'value' => '966', + 'label' => 'BT - Botoșani', + ), + array( + 'value' => '969', + 'label' => 'BU - Bucuresti', + ), + array( + 'value' => '967', + 'label' => 'BV - Brașov', + ), + array( + 'value' => '970', + 'label' => 'BZ - Buzău', + ), + array( + 'value' => '973', + 'label' => 'CJ - Cluj', + ), + array( + 'value' => '971', + 'label' => 'CL - Călărași', + ), + array( + 'value' => '972', + 'label' => 'CS - Caraș-Severin', + ), + array( + 'value' => '974', + 'label' => 'CT - Constanța', + ), + array( + 'value' => '975', + 'label' => 'CV - Covasna', + ), + array( + 'value' => '976', + 'label' => 'DB - Dâmbovița', + ), + array( + 'value' => '977', + 'label' => 'DJ - Dolj', + ), + array( + 'value' => '980', + 'label' => 'GJ - Gorj', + ), + array( + 'value' => '978', + 'label' => 'GL - Galați', + ), + array( + 'value' => '979', + 'label' => 'GR - Giurgiu', + ), + array( + 'value' => '982', + 'label' => 'HD - Hunedoara', + ), + array( + 'value' => '981', + 'label' => 'HR - Harghita', + ), + array( + 'value' => '985', + 'label' => 'IF - Ilfov', + ), + array( + 'value' => '983', + 'label' => 'IL - Ialomița', + ), + array( + 'value' => '984', + 'label' => 'IS - Iași', + ), + array( + 'value' => '987', + 'label' => 'MH - Mehedinți', + ), + array( + 'value' => '986', + 'label' => 'MM - Maramureș', + ), + array( + 'value' => '988', + 'label' => 'MS - Mureș', + ), + array( + 'value' => '989', + 'label' => 'NT - Neamț', + ), + array( + 'value' => '990', + 'label' => 'OT - Olt', + ), + array( + 'value' => '991', + 'label' => 'PH - Prahova', + ), + array( + 'value' => '994', + 'label' => 'SB - Sibiu', + ), + array( + 'value' => '993', + 'label' => 'SJ - Sălaj', + ), + array( + 'value' => '992', + 'label' => 'SM - Satu Mare', + ), + array( + 'value' => '995', + 'label' => 'SV - Suceava', + ), + array( + 'value' => '998', + 'label' => 'TL - Tulcea', + ), + array( + 'value' => '997', + 'label' => 'TM - Timiș', + ), + array( + 'value' => '996', + 'label' => 'TR - Teleorman', + ), + array( + 'value' => '1000', + 'label' => 'VL - Vâlcea', + ), + array( + 'value' => '1001', + 'label' => 'VN - Vrancea', + ), + array( + 'value' => '999', + 'label' => 'VS - Vaslui', + ), + array( + 'value' => '1016', + 'label' => 'SI031 - Mura', + ), + array( + 'value' => '1017', + 'label' => 'SI032 - Drava', + ), + array( + 'value' => '1018', + 'label' => 'SI033 - Carinthia', + ), + array( + 'value' => '1019', + 'label' => 'SI034 - Savinja', + ), + array( + 'value' => '1020', + 'label' => 'SI035 - Central Sava', + ), + array( + 'value' => '1021', + 'label' => 'SI036 - Lower Sava', + ), + array( + 'value' => '1022', + 'label' => 'SI037 - Southeast Slovenia', + ), + array( + 'value' => '1023', + 'label' => 'SI038 - Littoral–Inner Carniola', + ), + array( + 'value' => '1025', + 'label' => 'SI038 - Upper Carniola', + ), + array( + 'value' => '1024', + 'label' => 'SI041 - Central Slovenia', + ), + array( + 'value' => '1026', + 'label' => 'SI043 - Gorizia', + ), + array( + 'value' => '1027', + 'label' => 'SI044 - Coastal–Karst', + ), + array( + 'value' => '1013', + 'label' => 'AH - Ahuachapan', + ), + array( + 'value' => '1005', + 'label' => 'CA - Cabañas', + ), + array( + 'value' => '1004', + 'label' => 'CH - Chalatenango', + ), + array( + 'value' => '1008', + 'label' => 'CU - Cuscatlan', + ), + array( + 'value' => '1003', + 'label' => 'LL - La Libertad', + ), + array( + 'value' => '1006', + 'label' => 'LP - La Paz', + ), + array( + 'value' => '1012', + 'label' => 'LU - La Union', + ), + array( + 'value' => '1011', + 'label' => 'MO - Morazan', + ), + array( + 'value' => '1014', + 'label' => 'SA - Santa Ana', + ), + array( + 'value' => '1010', + 'label' => 'SM - San Miguel', + ), + array( + 'value' => '1015', + 'label' => 'SO - Sonsonate', + ), + array( + 'value' => '1002', + 'label' => 'SS - San Salvador', + ), + array( + 'value' => '1007', + 'label' => 'SV - San Vicente', + ), + array( + 'value' => '1009', + 'label' => 'US - Usulutan', + ), + array( + 'value' => '1085', + 'label' => 'TN01 - Ariana', + ), + array( + 'value' => '1086', + 'label' => 'TN02 - Béja', + ), + array( + 'value' => '1087', + 'label' => 'TN03 - Ben Arous', + ), + array( + 'value' => '1088', + 'label' => 'TN04 - Bizerte', + ), + array( + 'value' => '1089', + 'label' => 'TN05 - Gabès', + ), + array( + 'value' => '1090', + 'label' => 'TN06 - Gafsa', + ), + array( + 'value' => '1091', + 'label' => 'TN07 - Jendouba', + ), + array( + 'value' => '1092', + 'label' => 'TN08 - Kairouan', + ), + array( + 'value' => '1093', + 'label' => 'TN09 - Kasserine', + ), + array( + 'value' => '1094', + 'label' => 'TN10 - Kébili', + ), + array( + 'value' => '1095', + 'label' => 'TN11 - La Manouba', + ), + array( + 'value' => '1096', + 'label' => 'TN12 - Le Kef', + ), + array( + 'value' => '1097', + 'label' => 'TN13 - Mahdia', + ), + array( + 'value' => '1098', + 'label' => 'TN14 - Médenine', + ), + array( + 'value' => '1099', + 'label' => 'TN15 - Monastir', + ), + array( + 'value' => '1100', + 'label' => 'TN16 - Nabeul', + ), + array( + 'value' => '1101', + 'label' => 'TN17 - Sfax', + ), + array( + 'value' => '1102', + 'label' => 'TN18 - Sidi Bouzid', + ), + array( + 'value' => '1103', + 'label' => 'TN19 - Siliana', + ), + array( + 'value' => '1104', + 'label' => 'TN20 - Sousse', + ), + array( + 'value' => '1105', + 'label' => 'TN21 - Tataouine', + ), + array( + 'value' => '1106', + 'label' => 'TN22 - Tozeur', + ), + array( + 'value' => '1107', + 'label' => 'TN23 - Tunis', + ), + array( + 'value' => '1108', + 'label' => 'TN24 - Zaghouan', + ), + array( + 'value' => '1738', + 'label' => 'TR-01 - Adana', + ), + array( + 'value' => '1739', + 'label' => 'TR-02 - Adıyaman', + ), + array( + 'value' => '1740', + 'label' => 'TR-03 - Afyon', + ), + array( + 'value' => '1741', + 'label' => 'TR-04 - Ağrı', + ), + array( + 'value' => '1742', + 'label' => 'TR-05 - Amasya', + ), + array( + 'value' => '1743', + 'label' => 'TR-06 - Ankara', + ), + array( + 'value' => '1744', + 'label' => 'TR-07 - Antalya', + ), + array( + 'value' => '1745', + 'label' => 'TR-08 - Artvin', + ), + array( + 'value' => '1746', + 'label' => 'TR-09 - Aydın', + ), + array( + 'value' => '1747', + 'label' => 'TR-10 - Balıkesir', + ), + array( + 'value' => '1748', + 'label' => 'TR-11 - Bilecik', + ), + array( + 'value' => '1749', + 'label' => 'TR-12 - Bingöl', + ), + array( + 'value' => '1750', + 'label' => 'TR-13 - Bitlis', + ), + array( + 'value' => '1751', + 'label' => 'TR-14 - Bolu', + ), + array( + 'value' => '1752', + 'label' => 'TR-15 - Burdur', + ), + array( + 'value' => '1753', + 'label' => 'TR-16 - Bursa', + ), + array( + 'value' => '1754', + 'label' => 'TR-17 - Çanakkale', + ), + array( + 'value' => '1755', + 'label' => 'TR-18 - Çankırı', + ), + array( + 'value' => '1756', + 'label' => 'TR-19 - Çorum', + ), + array( + 'value' => '1757', + 'label' => 'TR-20 - Denizli', + ), + array( + 'value' => '1758', + 'label' => 'TR-21 - Diyarbakır', + ), + array( + 'value' => '1759', + 'label' => 'TR-22 - Edirne', + ), + array( + 'value' => '1760', + 'label' => 'TR-23 - Elazığ', + ), + array( + 'value' => '1761', + 'label' => 'TR-24 - Erzincan', + ), + array( + 'value' => '1762', + 'label' => 'TR-25 - Erzurum', + ), + array( + 'value' => '1763', + 'label' => 'TR-26 - Eskişehir', + ), + array( + 'value' => '1764', + 'label' => 'TR-27 - Gaziantep', + ), + array( + 'value' => '1765', + 'label' => 'TR-28 - Giresun', + ), + array( + 'value' => '1766', + 'label' => 'TR-29 - Gümüşhane', + ), + array( + 'value' => '1767', + 'label' => 'TR-30 - Hakkari', + ), + array( + 'value' => '1768', + 'label' => 'TR-31 - Hatay', + ), + array( + 'value' => '1769', + 'label' => 'TR-32 - Isparta', + ), + array( + 'value' => '1770', + 'label' => 'TR-33 - İçel', + ), + array( + 'value' => '1771', + 'label' => 'TR-34 - İstanbul', + ), + array( + 'value' => '1772', + 'label' => 'TR-35 - İzmir', + ), + array( + 'value' => '1773', + 'label' => 'TR-36 - Kars', + ), + array( + 'value' => '1774', + 'label' => 'TR-37 - Kastamonu', + ), + array( + 'value' => '1775', + 'label' => 'TR-38 - Kayseri', + ), + array( + 'value' => '1776', + 'label' => 'TR-39 - Kırklareli', + ), + array( + 'value' => '1777', + 'label' => 'TR-40 - Kırşehir', + ), + array( + 'value' => '1778', + 'label' => 'TR-41 - Kocaeli', + ), + array( + 'value' => '1779', + 'label' => 'TR-42 - Konya', + ), + array( + 'value' => '1780', + 'label' => 'TR-43 - Kütahya', + ), + array( + 'value' => '1781', + 'label' => 'TR-44 - Malatya', + ), + array( + 'value' => '1782', + 'label' => 'TR-45 - Manisa', + ), + array( + 'value' => '1783', + 'label' => 'TR-46 - Kahramanmaraş', + ), + array( + 'value' => '1784', + 'label' => 'TR-47 - Mardin', + ), + array( + 'value' => '1785', + 'label' => 'TR-48 - Muğla', + ), + array( + 'value' => '1786', + 'label' => 'TR-49 - Muş', + ), + array( + 'value' => '1787', + 'label' => 'TR-50 - Nevşehir', + ), + array( + 'value' => '1788', + 'label' => 'TR-51 - Niğde', + ), + array( + 'value' => '1789', + 'label' => 'TR-52 - Ordu', + ), + array( + 'value' => '1790', + 'label' => 'TR-53 - Rize', + ), + array( + 'value' => '1791', + 'label' => 'TR-54 - Sakarya', + ), + array( + 'value' => '1792', + 'label' => 'TR-55 - Samsun', + ), + array( + 'value' => '1793', + 'label' => 'TR-56 - Siirt', + ), + array( + 'value' => '1794', + 'label' => 'TR-57 - Sinop', + ), + array( + 'value' => '1795', + 'label' => 'TR-58 - Sivas', + ), + array( + 'value' => '1796', + 'label' => 'TR-59 - Tekirdağ', + ), + array( + 'value' => '1797', + 'label' => 'TR-60 - Tokat', + ), + array( + 'value' => '1798', + 'label' => 'TR-61 - Trabzon', + ), + array( + 'value' => '1799', + 'label' => 'TR-62 - Tunceli', + ), + array( + 'value' => '1801', + 'label' => 'TR-63 - Uşak', + ), + array( + 'value' => '1800', + 'label' => 'TR-63 - Şanlıurfa', + ), + array( + 'value' => '1802', + 'label' => 'TR-65 - Van', + ), + array( + 'value' => '1803', + 'label' => 'TR-66 - Yozgat', + ), + array( + 'value' => '1804', + 'label' => 'TR-67 - Zonguldak', + ), + array( + 'value' => '1805', + 'label' => 'TR-68 - Aksaray', + ), + array( + 'value' => '1806', + 'label' => 'TR-69 - Bayburt', + ), + array( + 'value' => '1807', + 'label' => 'TR-70 - Karaman', + ), + array( + 'value' => '1808', + 'label' => 'TR-71 - Kırıkkale', + ), + array( + 'value' => '1809', + 'label' => 'TR-72 - Batman', + ), + array( + 'value' => '1810', + 'label' => 'TR-73 - Şırnak', + ), + array( + 'value' => '1811', + 'label' => 'TR-74 - Bartın', + ), + array( + 'value' => '1812', + 'label' => 'TR-75 - Ardahan', + ), + array( + 'value' => '1813', + 'label' => 'TR-76 - Iğdır', + ), + array( + 'value' => '1814', + 'label' => 'TR-77 - Yalova', + ), + array( + 'value' => '1815', + 'label' => 'TR-78 - Karabük', + ), + array( + 'value' => '1816', + 'label' => 'TR-79 - Kilis', + ), + array( + 'value' => '1817', + 'label' => 'TR-80 - Osmaniye', + ), + array( + 'value' => '1818', + 'label' => 'TR-81 - Düzce', + ), + array( + 'value' => '1068', + 'label' => 'TW-CHY - 嘉義縣', + ), + array( + 'value' => '1063', + 'label' => 'TW-CWH - 彰化縣', + ), + array( + 'value' => '1064', + 'label' => 'TW-CWS - 彰化市', + ), + array( + 'value' => '1069', + 'label' => 'TW-CYI - 嘉義市', + ), + array( + 'value' => '1081', + 'label' => 'TW-GNI - 綠島', + ), + array( + 'value' => '1059', + 'label' => 'TW-HSC - 新竹市', + ), + array( + 'value' => '1058', + 'label' => 'TW-HSH - 新竹縣', + ), + array( + 'value' => '1076', + 'label' => 'TW-HWA - 花蓮縣', + ), + array( + 'value' => '1077', + 'label' => 'TW-HWC - 花蓮市', + ), + array( + 'value' => '1075', + 'label' => 'TW-ILC - 宜蘭市', + ), + array( + 'value' => '1074', + 'label' => 'TW-ILN - 宜蘭縣', + ), + array( + 'value' => '1072', + 'label' => 'TW-IUH - 屏東縣', + ), + array( + 'value' => '1071', + 'label' => 'TW-KHH - 高雄市', + ), + array( + 'value' => '1054', + 'label' => 'TW-KLU - 基隆市', + ), + array( + 'value' => '1083', + 'label' => 'TW-KMN - 金門縣', + ), + array( + 'value' => '1082', + 'label' => 'TW-KYD - 蘭嶼', + ), + array( + 'value' => '1084', + 'label' => 'TW-LNN - 連江縣', + ), + array( + 'value' => '1061', + 'label' => 'TW-MAC - 苗栗市', + ), + array( + 'value' => '1060', + 'label' => 'TW-MAL - 苗栗縣', + ), + array( + 'value' => '1065', + 'label' => 'TW-NTC - 南投市', + ), + array( + 'value' => '1066', + 'label' => 'TW-NTO - 南投縣', + ), + array( + 'value' => '1080', + 'label' => 'TW-PEH - 澎湖縣', + ), + array( + 'value' => '1073', + 'label' => 'TW-PTS - 屏東市', + ), + array( + 'value' => '1070', + 'label' => 'TW-TNN - 臺南市', + ), + array( + 'value' => '1055', + 'label' => 'TW-TPE - 臺北市', + ), + array( + 'value' => '1056', + 'label' => 'TW-TPH - 新北市', + ), + array( + 'value' => '1078', + 'label' => 'TW-TTC - 臺東市', + ), + array( + 'value' => '1079', + 'label' => 'TW-TTT - 臺東縣', + ), + array( + 'value' => '1062', + 'label' => 'TW-TXG - 臺中市', + ), + array( + 'value' => '1057', + 'label' => 'TW-TYC - 桃園市', + ), + array( + 'value' => '1067', + 'label' => 'TW-YLH - 雲林縣', + ), + array( + 'value' => '1110', + 'label' => 'AK - Alaska', + ), + array( + 'value' => '1109', + 'label' => 'AL - Alabama', + ), + array( + 'value' => '1112', + 'label' => 'AR - Arkansas', + ), + array( + 'value' => '1111', + 'label' => 'AZ - Arizona', + ), + array( + 'value' => '1113', + 'label' => 'CA - California', + ), + array( + 'value' => '1114', + 'label' => 'CO - Colorado', + ), + array( + 'value' => '1115', + 'label' => 'CT - Connecticut', + ), + array( + 'value' => '1116', + 'label' => 'DE - Delaware', + ), + array( + 'value' => '1117', + 'label' => 'FL - Florida', + ), + array( + 'value' => '1118', + 'label' => 'GA - Georgia', + ), + array( + 'value' => '1119', + 'label' => 'HI - Hawaii', + ), + array( + 'value' => '1123', + 'label' => 'IA - Iowa', + ), + array( + 'value' => '1120', + 'label' => 'ID - Idaho', + ), + array( + 'value' => '1121', + 'label' => 'IL - Illinois', + ), + array( + 'value' => '1122', + 'label' => 'IN - Indiana', + ), + array( + 'value' => '1124', + 'label' => 'KS - Kansas', + ), + array( + 'value' => '1125', + 'label' => 'KY - Kentucky', + ), + array( + 'value' => '1126', + 'label' => 'LA - Louisiana', + ), + array( + 'value' => '1129', + 'label' => 'MA - Massachusetts', + ), + array( + 'value' => '1128', + 'label' => 'MD - Maryland', + ), + array( + 'value' => '1127', + 'label' => 'ME - Maine', + ), + array( + 'value' => '1130', + 'label' => 'MI - Michigan', + ), + array( + 'value' => '1131', + 'label' => 'MN - Minnesota', + ), + array( + 'value' => '1133', + 'label' => 'MO - Missouri', + ), + array( + 'value' => '1132', + 'label' => 'MS - Mississippi', + ), + array( + 'value' => '1134', + 'label' => 'MT - Montana', + ), + array( + 'value' => '1141', + 'label' => 'NC - North Carolina', + ), + array( + 'value' => '1142', + 'label' => 'ND - North Dakota', + ), + array( + 'value' => '1135', + 'label' => 'NE - Nebraska', + ), + array( + 'value' => '1137', + 'label' => 'NH - New Hampshire', + ), + array( + 'value' => '1138', + 'label' => 'NJ - New Jersey', + ), + array( + 'value' => '1139', + 'label' => 'NM - New Mexico', + ), + array( + 'value' => '1136', + 'label' => 'NV - Nevada', + ), + array( + 'value' => '1140', + 'label' => 'NY - New York', + ), + array( + 'value' => '1143', + 'label' => 'OH - Ohio', + ), + array( + 'value' => '1144', + 'label' => 'OK - Oklahoma', + ), + array( + 'value' => '1145', + 'label' => 'OR - Oregon', + ), + array( + 'value' => '1146', + 'label' => 'PA - Pennsylvania', + ), + array( + 'value' => '1147', + 'label' => 'RI - Rhode Island', + ), + array( + 'value' => '1148', + 'label' => 'SC - South Carolina', + ), + array( + 'value' => '1149', + 'label' => 'SD - South Dakota', + ), + array( + 'value' => '1150', + 'label' => 'TN - Tennessee', + ), + array( + 'value' => '1151', + 'label' => 'TX - Texas', + ), + array( + 'value' => '1152', + 'label' => 'UT - Utah', + ), + array( + 'value' => '1154', + 'label' => 'VA - Virginia', + ), + array( + 'value' => '1153', + 'label' => 'VT - Vermont', + ), + array( + 'value' => '1155', + 'label' => 'WA - Washington', + ), + array( + 'value' => '1157', + 'label' => 'WI - Wisconsin', + ), + array( + 'value' => '1156', + 'label' => 'WV - West Virginia', + ), + array( + 'value' => '1158', + 'label' => 'WY - Wyoming', + ), + array( + 'value' => '1545', + 'label' => 'VE-A - Distrito Capital', + ), + array( + 'value' => '1560', + 'label' => 'VE-B - Anzoátegui', + ), + array( + 'value' => '1556', + 'label' => 'VE-C - Apure', + ), + array( + 'value' => '1546', + 'label' => 'VE-D - Aragua', + ), + array( + 'value' => '1542', + 'label' => 'VE-E - Barinas', + ), + array( + 'value' => '1551', + 'label' => 'VE-F - Bolívar', + ), + array( + 'value' => '1547', + 'label' => 'VE-G - Carabobo', + ), + array( + 'value' => '1558', + 'label' => 'VE-H - Cojedes', + ), + array( + 'value' => '1548', + 'label' => 'VE-I - Falcón', + ), + array( + 'value' => '1557', + 'label' => 'VE-J - Guárico', + ), + array( + 'value' => '1549', + 'label' => 'VE-K - Lara', + ), + array( + 'value' => '1540', + 'label' => 'VE-L - Mérida', + ), + array( + 'value' => '1543', + 'label' => 'VE-M - Miranda', + ), + array( + 'value' => '1561', + 'label' => 'VE-N - Monagas', + ), + array( + 'value' => '1554', + 'label' => 'VE-O - Nueva Esparta', + ), + array( + 'value' => '1559', + 'label' => 'VE-P - Portuguesa', + ), + array( + 'value' => '1562', + 'label' => 'VE-R - Sucre', + ), + array( + 'value' => '1564', + 'label' => 'VE-S - Táchira', + ), + array( + 'value' => '1541', + 'label' => 'VE-T - Trujillo', + ), + array( + 'value' => '1550', + 'label' => 'VE-U - Yaracuy', + ), + array( + 'value' => '1563', + 'label' => 'VE-V - Zulia', + ), + array( + 'value' => '1544', + 'label' => 'VE-W - Vargas', + ), + array( + 'value' => '1552', + 'label' => 'VE-X - Amazonas', + ), + array( + 'value' => '1553', + 'label' => 'VE-Y - Delta Amacuro', + ), + array( + 'value' => '1555', + 'label' => 'VE-Z - Dependencias Federales', + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/appointments.php b/forms-bridge/addons/dolibarr/templates/appointments.php new file mode 100644 index 00000000..a1dd028b --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/appointments.php @@ -0,0 +1,294 @@ + __( 'Appointments', 'forms-bridge' ), + 'description' => __( + 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/agendaevents', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'userownerid', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( 'Host user of the event', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/users', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].email', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'type_code', + 'label' => __( 'Event type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/setup/dictionary/event_types', + 'finger' => array( + 'value' => '[].code', + 'label' => '[].label', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'label', + 'label' => __( 'Event label', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web appointment', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'fulldayevent', + 'label' => __( 'Is all day event?', 'forms-bridge' ), + 'type' => 'boolean', + 'default' => false, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'duration', + 'label' => __( 'Duration (Hours)', 'forms-bridge' ), + 'type' => 'number', + 'default' => 1, + 'required' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Appointments', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'date', + 'label' => __( 'Date', 'forms-bridge' ), + 'type' => 'date', + 'required' => true, + ), + array( + 'name' => 'hour', + 'label' => __( 'Hour', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => __( '1 AM', 'forms-bridge' ), + 'value' => '01', + ), + array( + 'label' => __( '2 AM', 'forms-bridge' ), + 'value' => '02', + ), + array( + 'label' => __( '3 AM', 'forms-bridge' ), + 'value' => '03', + ), + array( + 'label' => __( '4 AM', 'forms-bridge' ), + 'value' => '04', + ), + array( + 'label' => __( '5 AM', 'forms-bridge' ), + 'value' => '05', + ), + array( + 'label' => __( '6 AM', 'forms-bridge' ), + 'value' => '06', + ), + array( + 'label' => __( '7 AM', 'forms-bridge' ), + 'value' => '07', + ), + array( + 'label' => __( '8 AM', 'forms-bridge' ), + 'value' => '08', + ), + array( + 'label' => __( '9 AM', 'forms-bridge' ), + 'value' => '09', + ), + array( + 'label' => __( '10 AM', 'forms-bridge' ), + 'value' => '10', + ), + array( + 'label' => __( '11 AM', 'forms-bridge' ), + 'value' => '11', + ), + array( + 'label' => __( '12 AM', 'forms-bridge' ), + 'value' => '12', + ), + array( + 'label' => __( '1 PM', 'forms-bridge' ), + 'value' => '13', + ), + array( + 'label' => __( '2 PM', 'forms-bridge' ), + 'value' => '14', + ), + array( + 'label' => __( '3 PM', 'forms-bridge' ), + 'value' => '15', + ), + array( + 'label' => __( '4 PM', 'forms-bridge' ), + 'value' => '16', + ), + array( + 'label' => __( '5 PM', 'forms-bridge' ), + 'value' => '17', + ), + array( + 'label' => __( '6 PM', 'forms-bridge' ), + 'value' => '18', + ), + array( + 'label' => __( '7 PM', 'forms-bridge' ), + 'value' => '19', + ), + array( + 'label' => __( '8 PM', 'forms-bridge' ), + 'value' => '20', + ), + array( + 'label' => __( '9 PM', 'forms-bridge' ), + 'value' => '21', + ), + array( + 'label' => __( '10 PM', 'forms-bridge' ), + 'value' => '22', + ), + array( + 'label' => __( '11 PM', 'forms-bridge' ), + 'value' => '23', + ), + array( + 'label' => __( '12 PM', 'forms-bridge' ), + 'value' => '24', + ), + ), + ), + array( + 'name' => 'minute', + 'label' => __( 'Minute', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => '00', + 'value' => '00.0', + ), + array( + 'label' => '05', + 'value' => '05', + ), + array( + 'label' => '10', + 'value' => '10', + ), + array( + 'label' => '15', + 'value' => '15', + ), + array( + 'label' => '20', + 'value' => '20', + ), + array( + 'label' => '25', + 'value' => '25', + ), + array( + 'label' => '30', + 'value' => '30', + ), + array( + 'label' => '35', + 'value' => '35', + ), + array( + 'label' => '40', + 'value' => '40', + ), + array( + 'label' => '45', + 'value' => '45', + ), + array( + 'label' => '50', + 'value' => '50', + ), + array( + 'label' => '55', + 'value' => '55', + ), + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/agendaevents', + 'mutations' => array( + array( + array( + 'from' => '?duration', + 'to' => 'duration', + 'cast' => 'number', + ), + array( + 'from' => 'userownerid', + 'to' => 'userownerid', + 'cast' => 'integer', + ), + ), + array( + array( + 'from' => 'datetime', + 'to' => 'date', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( + 'date-fields-to-date', + 'appointment-dates', + 'appointment-attendee', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/company-appointments.php b/forms-bridge/addons/dolibarr/templates/company-appointments.php new file mode 100644 index 00000000..63992420 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/company-appointments.php @@ -0,0 +1,374 @@ + __( 'Company Appointments', 'forms-bridge' ), + 'description' => __( + 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/agendaevents', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'userownerid', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( 'Host user of the event', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/users', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].email', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'type_code', + 'label' => __( 'Event type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/setup/dictionary/event_types', + 'finger' => array( + 'value' => '[].code', + 'label' => '[].label', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'label', + 'label' => __( 'Event label', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web appointment', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'fulldayevent', + 'label' => __( 'Is all day event?', 'forms-bridge' ), + 'type' => 'boolean', + 'default' => false, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'duration', + 'label' => __( 'Duration (Hours)', 'forms-bridge' ), + 'type' => 'number', + 'default' => 1, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'client', + 'label' => __( 'Thirdparty status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'value' => '1', + 'label' => __( 'Client', 'forms-bridge' ), + ), + array( + 'value' => '2', + 'label' => __( 'Prospect', 'forms-bridge' ), + ), + array( + 'value' => '3', + 'label' => __( 'Client/Prospect', 'forms-bridge' ), + ), + array( + 'value' => '0', + 'label' => __( + 'Neither customer nor supplier', + 'forms-bridge' + ), + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Company type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Appointments', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'company_name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'contact_email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'poste', + 'label' => __( 'Job position', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'date', + 'label' => __( 'Date', 'forms-bridge' ), + 'type' => 'date', + 'required' => true, + ), + array( + 'name' => 'hour', + 'label' => __( 'Hour', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => __( '1 AM', 'forms-bridge' ), + 'value' => '01', + ), + array( + 'label' => __( '2 AM', 'forms-bridge' ), + 'value' => '02', + ), + array( + 'label' => __( '3 AM', 'forms-bridge' ), + 'value' => '03', + ), + array( + 'label' => __( '4 AM', 'forms-bridge' ), + 'value' => '04', + ), + array( + 'label' => __( '5 AM', 'forms-bridge' ), + 'value' => '05', + ), + array( + 'label' => __( '6 AM', 'forms-bridge' ), + 'value' => '06', + ), + array( + 'label' => __( '7 AM', 'forms-bridge' ), + 'value' => '07', + ), + array( + 'label' => __( '8 AM', 'forms-bridge' ), + 'value' => '08', + ), + array( + 'label' => __( '9 AM', 'forms-bridge' ), + 'value' => '09', + ), + array( + 'label' => __( '10 AM', 'forms-bridge' ), + 'value' => '10', + ), + array( + 'label' => __( '11 AM', 'forms-bridge' ), + 'value' => '11', + ), + array( + 'label' => __( '12 AM', 'forms-bridge' ), + 'value' => '12', + ), + array( + 'label' => __( '1 PM', 'forms-bridge' ), + 'value' => '13', + ), + array( + 'label' => __( '2 PM', 'forms-bridge' ), + 'value' => '14', + ), + array( + 'label' => __( '3 PM', 'forms-bridge' ), + 'value' => '15', + ), + array( + 'label' => __( '4 PM', 'forms-bridge' ), + 'value' => '16', + ), + array( + 'label' => __( '5 PM', 'forms-bridge' ), + 'value' => '17', + ), + array( + 'label' => __( '6 PM', 'forms-bridge' ), + 'value' => '18', + ), + array( + 'label' => __( '7 PM', 'forms-bridge' ), + 'value' => '19', + ), + array( + 'label' => __( '8 PM', 'forms-bridge' ), + 'value' => '20', + ), + array( + 'label' => __( '9 PM', 'forms-bridge' ), + 'value' => '21', + ), + array( + 'label' => __( '10 PM', 'forms-bridge' ), + 'value' => '22', + ), + array( + 'label' => __( '11 PM', 'forms-bridge' ), + 'value' => '23', + ), + array( + 'label' => __( '12 PM', 'forms-bridge' ), + 'value' => '24', + ), + ), + ), + array( + 'name' => 'minute', + 'label' => __( 'Minute', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => '00', + 'value' => '00.0', + ), + array( + 'label' => '05', + 'value' => '05', + ), + array( + 'label' => '10', + 'value' => '10', + ), + array( + 'label' => '15', + 'value' => '15', + ), + array( + 'label' => '20', + 'value' => '20', + ), + array( + 'label' => '25', + 'value' => '25', + ), + array( + 'label' => '30', + 'value' => '30', + ), + array( + 'label' => '35', + 'value' => '35', + ), + array( + 'label' => '40', + 'value' => '40', + ), + array( + 'label' => '45', + 'value' => '45', + ), + array( + 'label' => '50', + 'value' => '50', + ), + array( + 'label' => '55', + 'value' => '55', + ), + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/agendaevents', + 'mutations' => array( + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?duration', + 'to' => 'duration', + 'cast' => 'number', + ), + ), + array( + array( + 'from' => 'datetime', + 'to' => 'date', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + ), + array(), + ), + 'workflow' => array( + 'date-fields-to-date', + 'appointment-dates', + 'contact-socid', + 'appointment-attendee', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/company-leads.php b/forms-bridge/addons/dolibarr/templates/company-leads.php new file mode 100644 index 00000000..32b2ec75 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/company-leads.php @@ -0,0 +1,285 @@ + __( 'Company Leads', 'forms-bridge' ), + 'description' => __( + 'Leads form template. The resulting bridge will convert form submissions into company lead projects linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/projects', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'userownerid', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( 'Owner user of the lead', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/users', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].email', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Company type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'stcomm_id', + 'label' => __( 'Prospect status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Never contacted', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'To contact', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Contact in progress', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Contacted', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Do not contact', 'forms-bridge' ), + 'value' => '-1', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'opp_status', + 'label' => __( 'Lead status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Prospection', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Qualification', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Proposal', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Negociation', 'forms-bridge' ), + 'value' => '4', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'opp_amount', + 'label' => __( 'Lead amount', 'forms-bridge' ), + 'type' => 'number', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Leads', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Company Leads', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'company_name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'idprof1', + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'address', + 'label' => __( 'Address', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'zip', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'town', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'poste', + 'label' => __( 'Job position', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'note_private', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/projects', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'client', + 'value' => '2', + ), + array( + 'name' => 'usage_opportunity', + 'value' => '1', + ), + array( + 'name' => 'date_start', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => '?userownerid', + 'to' => 'userid', + 'cast' => 'integer', + ), + ), + array(), + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'title', + 'cast' => 'copy', + ), + array( + 'from' => 'email', + 'to' => 'contact_email', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'socid', + 'to' => 'lead_socid', + 'cast' => 'copy', + ), + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'lead_socid', + 'to' => 'socid', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'iso2-country-code', + 'country-id', + 'contact-socid', + 'contact-id', + 'next-project-ref', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/company-proposals.php b/forms-bridge/addons/dolibarr/templates/company-proposals.php new file mode 100644 index 00000000..1133510c --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/company-proposals.php @@ -0,0 +1,251 @@ + __( 'Company Proposals', 'forms-bridge' ), + 'description' => __( + 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/proposals', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Thirdparty type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'stcomm_id', + 'label' => __( 'Prospect status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Never contacted', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'To contact', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Contact in progress', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Contacted', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Do not contact', 'forms-bridge' ), + 'value' => '-1', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'fk_product', + 'label' => __( 'Product', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/products', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].label', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Proposals', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Company Proposals', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'quantity', + 'label' => __( 'Quantity', 'forms-bridge' ), + 'type' => 'number', + 'required' => true, + 'default' => 1, + 'min' => 1, + ), + array( + 'name' => 'company_name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'tva_intra', + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'address', + 'label' => __( 'Address', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'zip', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'town', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/proposals', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'client', + 'value' => '2', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + array( + 'name' => 'lines[0].product_type', + 'value' => '1', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'quantity', + 'to' => 'lines[0].qty', + 'cast' => 'integer', + ), + array( + 'from' => 'fk_product', + 'to' => 'lines[0].fk_product', + 'cast' => 'integer', + ), + ), + array(), + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'contact_email', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'socid', + 'to' => 'order_socid', + 'cast' => 'copy', + ), + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'order_socid', + 'to' => 'socid', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'iso2-country-code', + 'country-id', + 'contact-socid', + 'contact-id', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/company-prospects.php b/forms-bridge/addons/dolibarr/templates/company-prospects.php new file mode 100644 index 00000000..c9c7f2ea --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/company-prospects.php @@ -0,0 +1,211 @@ + __( 'Company Prospects', 'forms-bridge' ), + 'description' => __( + 'Leads form template. The resulting bridge will convert form submissions into company prospects linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/contacts', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'stcomm_id', + 'label' => __( 'Prospect status', 'forms-bridge' ), + 'required' => true, + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Never contacted', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'To contact', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Contact in progress', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Contacted', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Do not contact', 'forms-bridge' ), + 'value' => '-1', + ), + ), + 'default' => '0', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Thirdparty type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Prospects', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Company Prospects', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'company_name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'idprof1', + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'address', + 'label' => __( 'Address', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'zip', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'town', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'poste', + 'label' => __( 'Job position', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'note_private', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/contacts', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'client', + 'value' => '2', + ), + ), + 'mutations' => array( + array(), + array(), + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'contact_email', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( + 'iso2-country-code', + 'country-id', + 'contact-socid', + 'skip-if-contact-exists', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/contacts.php b/forms-bridge/addons/dolibarr/templates/contacts.php new file mode 100644 index 00000000..24e7d67c --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/contacts.php @@ -0,0 +1,74 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/contacts', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'no_email', + 'label' => __( 'Subscrive to email', 'forms-bridge' ), + 'type' => 'boolean', + 'default' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Contacts', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Contacts', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/contacts', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'no_email', + 'to' => 'no_email', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( 'skip-if-contact-exists' ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/customers.php b/forms-bridge/addons/dolibarr/templates/customers.php new file mode 100644 index 00000000..75a521d2 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/customers.php @@ -0,0 +1,177 @@ + __( 'Customers', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into customers.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/contacts', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Customer type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Customers', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Customers', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'company_name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'tva_intra', + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'address', + 'label' => __( 'Address', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'zip', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'town', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'contact_email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'poste', + 'label' => __( 'Job position', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'note_private', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/contacts', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'client', + 'value' => '1', + ), + ), + 'mutations' => array( + array(), + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + array(), + array( + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + ), + array(), + ), + 'workflow' => array( + 'iso2-country-code', + 'country-id', + 'contact-socid', + 'skip-if-contact-exists', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/individual-leads.php b/forms-bridge/addons/dolibarr/templates/individual-leads.php new file mode 100644 index 00000000..3ff5cca0 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/individual-leads.php @@ -0,0 +1,190 @@ + __( 'Leads', 'forms-bridge' ), + 'description' => __( + 'Lead form template. The resulting bridge will convert form submissions into lead projects linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/projects', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'userownerid', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( 'Owner user of the lead', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/users', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].email', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'stcomm_id', + 'label' => __( 'Prospect status', 'forms-bridge' ), + 'required' => true, + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Never contacted', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'To contact', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Contact in progress', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Contacted', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Do not contact', 'forms-bridge' ), + 'value' => '-1', + ), + ), + 'default' => '0', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'opp_status', + 'label' => __( 'Lead status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Prospection', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Qualification', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Proposal', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Negociation', 'forms-bridge' ), + 'value' => '4', + ), + ), + 'required' => true, + 'default' => '1', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'opp_amount', + 'label' => __( 'Lead amount', 'forms-bridge' ), + 'type' => 'number', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Leads', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Leads', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'note_private', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/projects', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'typent_id', + 'value' => '8', + ), + array( + 'name' => 'client', + 'value' => '2', + ), + array( + 'name' => 'usage_opportunity', + 'value' => '1', + ), + array( + 'name' => 'date_start', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'firstname', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => 'lastname', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => 'name', + 'to' => 'title', + 'cast' => 'copy', + ), + array( + 'from' => 'userownerid', + 'to' => 'userid', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( 'contact-socid', 'next-project-ref' ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/individual-proposals.php b/forms-bridge/addons/dolibarr/templates/individual-proposals.php new file mode 100644 index 00000000..d33d4579 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/individual-proposals.php @@ -0,0 +1,203 @@ + __( 'Proposals', 'forms-bridge' ), + 'description' => __( + 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/proposals', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'stcomm_id', + 'label' => __( 'Prospect status', 'forms-bridge' ), + 'required' => true, + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Never contacted', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'To contact', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Contact in progress', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Contacted', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Do not contact', 'forms-bridge' ), + 'value' => '-1', + ), + ), + 'default' => '0', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'fk_product', + 'label' => __( 'Product', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/index.php/products', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].label', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Proposals', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Proposals', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'quantity', + 'label' => __( 'Quantity', 'forms-bridge' ), + 'type' => 'number', + 'required' => true, + 'default' => 1, + 'min' => 1, + ), + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'tva_intra', + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'address', + 'label' => __( 'Address', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'zip', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'town', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/proposals', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'typent_id', + 'value' => '8', + ), + array( + 'name' => 'client', + 'value' => '2', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + array( + 'name' => 'lines[0].product_type', + 'value' => '1', + ), + ), + 'mutations' => array( + array(), + array( + array( + 'from' => 'firstname', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => 'lastname', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => 'quantity', + 'to' => 'lines[0].qty', + 'cast' => 'integer', + ), + array( + 'from' => 'fk_product', + 'to' => 'lines[0].fk_product', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'country-id', 'contact-socid' ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/individual-prospects.php b/forms-bridge/addons/dolibarr/templates/individual-prospects.php new file mode 100644 index 00000000..30776928 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/individual-prospects.php @@ -0,0 +1,125 @@ + __( 'Prospects', 'forms-bridge' ), + 'description' => __( + 'Lead form template. The resulting bridge will convert form submissions into prospect contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/thirdparties', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'stcomm_id', + 'label' => __( 'Prospect status', 'forms-bridge' ), + 'required' => true, + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Never contacted', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'To contact', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Contact in progress', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Contacted', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Do not contact', 'forms-bridge' ), + 'value' => '-1', + ), + ), + 'default' => '0', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Prospects', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Prospects', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'firstname', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'lastname', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'note_private', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/thirdparties', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'typent_id', + 'value' => '8', + ), + array( + 'name' => 'client', + 'value' => '2', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'firstname', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => 'lastname', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + ), + ), + 'workflow' => array( 'skip-if-thirdparty-exists', 'next-client-code' ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/thirdparties.php b/forms-bridge/addons/dolibarr/templates/thirdparties.php new file mode 100644 index 00000000..01946cdf --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/thirdparties.php @@ -0,0 +1,185 @@ + __( 'Thirdparties', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into thirdparties.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/thirdparties', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'client', + 'label' => __( 'Thirdparty status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'value' => '1', + 'label' => __( 'Client', 'forms-bridge' ), + ), + array( + 'value' => '2', + 'label' => __( 'Prospect', 'forms-bridge' ), + ), + array( + 'value' => '3', + 'label' => __( 'Client/Prospect', 'forms-bridge' ), + ), + array( + 'value' => '0', + 'label' => __( + 'Neither customer nor supplier', + 'forms-bridge' + ), + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Thirdparty type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Retailer', 'forms-bridge' ), + 'value' => '7', + ), + array( + 'label' => __( 'Private individual', 'forms-bridge' ), + 'value' => '8', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + // [ + // 'ref' => '#bridge/custom_fields[]', + // 'name' => 'fournisseur', + // 'label' => __('Provider', 'forms-bridge'), + // 'type' => 'boolean', + // 'default' => false, + // ], + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Thirdparties', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Thirdparties', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'tva_intra', + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'address', + 'label' => __( 'Address', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'zip', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'town', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'name' => 'note_private', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/thirdparties', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + ), + 'workflow' => array( + 'iso2-country-code', + 'country-id', + 'skip-if-thirdparty-exists', + 'next-client-code', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/woo-orders.php b/forms-bridge/addons/dolibarr/templates/woo-orders.php new file mode 100644 index 00000000..79697f58 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/woo-orders.php @@ -0,0 +1,428 @@ + __( 'Sale Orders', 'forms-bridge' ), + 'description' => __( + 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into sale orders linked to new contacts. To work propertly, the bridge needs that your WooCommerce product sku values matches with the dolibarr\'s product refs..', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/orders', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Thirdparty type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Retailer', 'forms-bridge' ), + 'value' => '7', + ), + array( + 'label' => __( 'Private individual', 'forms-bridge' ), + 'value' => '8', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/orders', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'typent_id', + 'value' => '8', + ), + array( + 'name' => 'client', + 'value' => '1', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'shipping.email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'customer_note', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'lines[].qty', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].subtotal_tax.percentage', + 'to' => 'lines[].tva_tx', + 'cast' => 'number', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'lines[].subprice', + 'cast' => 'number', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'lines[].ref', + 'cast' => 'string', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'socid', + 'to' => 'order_socid', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'firstname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'lastname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone_perso', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'lines[].ref', + 'to' => 'product_refs', + 'cast' => 'inherit', + ), + array( + 'from' => 'contact_ids', + 'to' => 'contact_ids', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => '?customer_note', + 'to' => 'note_private', + 'cast' => 'string', + ), + array( + 'from' => 'order_socid', + 'to' => 'socid', + 'cast' => 'integer', + ), + array( + 'from' => 'fk_products[]', + 'to' => 'lines[].fk_product', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( 'contact-socid', 'contact-id', 'products-by-ref' ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/woo-sync-orders.php b/forms-bridge/addons/dolibarr/templates/woo-sync-orders.php new file mode 100644 index 00000000..2c12dca4 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/woo-sync-orders.php @@ -0,0 +1,435 @@ + __( 'Sale Orders + Sync', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Dolibarr by product refs.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/orders', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Thirdparty type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Retailer', 'forms-bridge' ), + 'value' => '7', + ), + array( + 'label' => __( 'Private individual', 'forms-bridge' ), + 'value' => '8', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/orders', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'typent_id', + 'value' => '8', + ), + array( + 'name' => 'client', + 'value' => '1', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'shipping.email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'customer_note', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'lines[].qty', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].subtotal_tax.percentage', + 'to' => 'lines[].tva_tx', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'lines[].subprice', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'lines[].ref', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'socid', + 'to' => 'order_socid', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'firstname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'lastname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone_perso', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'lines[].ref', + 'to' => 'product_refs', + 'cast' => 'inherit', + ), + array( + 'from' => 'contact_ids', + 'to' => 'contact_ids', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => '?customer_note', + 'to' => 'note_private', + 'cast' => 'string', + ), + array( + 'from' => 'order_socid', + 'to' => 'socid', + 'cast' => 'integer', + ), + array( + 'from' => 'fk_products[]', + 'to' => 'lines[].fk_product', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'sync-products-by-ref', + 'contact-socid', + 'contact-id', + 'products-by-ref', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/woo-validated-orders.php b/forms-bridge/addons/dolibarr/templates/woo-validated-orders.php new file mode 100644 index 00000000..5b5bda92 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/woo-validated-orders.php @@ -0,0 +1,429 @@ + __( 'Validated Orders', 'forms-bridge' ), + 'description' => __( + 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into validated sale orders linked to new third parties. To work properly, the bridge needs that your WooCommerce product sku values matches with the dolibarr\'s product refs..', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/orders', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Thirdparty type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Retailer', 'forms-bridge' ), + 'value' => '7', + ), + array( + 'label' => __( 'Private individual', 'forms-bridge' ), + 'value' => '8', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/orders', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'client', + 'value' => '1', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'shipping.email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'customer_note', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'lines[].qty', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].subtotal_tax.percentage', + 'to' => 'lines[].tva_tx', + 'cast' => 'number', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'lines[].subprice', + 'cast' => 'number', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'lines[].ref', + 'cast' => 'string', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'socid', + 'to' => 'order_socid', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'firstname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'lastname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone_perso', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'lines[].ref', + 'to' => 'product_refs', + 'cast' => 'inherit', + ), + array( + 'from' => 'contact_ids', + 'to' => 'contact_ids', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => '?customer_note', + 'to' => 'note_private', + 'cast' => 'string', + ), + array( + 'from' => 'order_socid', + 'to' => 'socid', + 'cast' => 'integer', + ), + array( + 'from' => 'fk_products[]', + 'to' => 'lines[].fk_product', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'contact-socid', + 'contact-id', + 'products-by-ref', + 'validate-order', + ), + ), +); diff --git a/forms-bridge/addons/dolibarr/templates/woo-validated-sync-orders.php b/forms-bridge/addons/dolibarr/templates/woo-validated-sync-orders.php new file mode 100644 index 00000000..64278a01 --- /dev/null +++ b/forms-bridge/addons/dolibarr/templates/woo-validated-sync-orders.php @@ -0,0 +1,432 @@ + __( 'Validated Orders + Sync', 'forms-bridge' ), + 'description' => __( + 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into validated sale orders linked to new third parties. The template includes a job that synchronize products between WooCommerce and Dolibarr by product refs..', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/index.php/orders', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'typent_id', + 'label' => __( 'Thirdparty type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Large company', 'forms-bridge' ), + 'value' => '2', + ), + array( + 'label' => __( 'Medium company', 'forms-bridge' ), + 'value' => '3', + ), + array( + 'label' => __( 'Small company', 'forms-bridge' ), + 'value' => '4', + ), + array( + 'label' => __( 'Governmental', 'forms-bridge' ), + 'value' => '5', + ), + array( + 'label' => __( 'Startup', 'forms-bridge' ), + 'value' => '1', + ), + array( + 'label' => __( 'Retailer', 'forms-bridge' ), + 'value' => '7', + ), + array( + 'label' => __( 'Private individual', 'forms-bridge' ), + 'value' => '8', + ), + array( + 'label' => __( 'Other', 'forms-bridge' ), + 'value' => '100', + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/index.php/orders', + 'custom_fields' => array( + array( + 'name' => 'status', + 'value' => '1', + ), + array( + 'name' => 'client', + 'value' => '1', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'shipping.email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'customer_note', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'lines[].qty', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].subtotal_tax.percentage', + 'to' => 'lines[].tva_tx', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'lines[].subprice', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'lines[].ref', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'socid', + 'to' => 'order_socid', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'firstname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'lastname', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone_perso', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'address', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'town', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'lines[].ref', + 'to' => 'product_refs', + 'cast' => 'inherit', + ), + array( + 'from' => 'contact_ids', + 'to' => 'contact_ids', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => '?customer_note', + 'to' => 'note_private', + 'cast' => 'string', + ), + array( + 'from' => 'order_socid', + 'to' => 'socid', + 'cast' => 'integer', + ), + array( + 'from' => 'fk_products[]', + 'to' => 'lines[].fk_product', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'sync-products-by-ref', + 'contact-socid', + 'contact-id', + 'products-by-ref', + 'validate-order', + ), + ), +); diff --git a/addons/financoop/assets/logo.png b/forms-bridge/addons/financoop/assets/logo.png similarity index 100% rename from addons/financoop/assets/logo.png rename to forms-bridge/addons/financoop/assets/logo.png diff --git a/forms-bridge/addons/financoop/class-financoop-addon.php b/forms-bridge/addons/financoop/class-financoop-addon.php new file mode 100644 index 00000000..90b2f23a --- /dev/null +++ b/forms-bridge/addons/financoop/class-financoop-addon.php @@ -0,0 +1,198 @@ + '__financoop-' . time(), + 'endpoint' => '/api/campaign', + 'method' => 'GET', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit(); + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge = new Finan_Coop_Form_Bridge( + array( + 'name' => '__financoop-' . time(), + 'endpoint' => $endpoint, + 'backend' => $backend, + 'method' => 'GET', + ) + ); + + return $bridge->submit(); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array + */ + public function get_endpoint_schema( $endpoint, $backend ) { + $bridge = new Finan_Coop_Form_Bridge( + array( + 'name' => '__financoop-' . time(), + 'endpoint' => $endpoint, + 'backend' => $backend, + 'method' => 'GET', + ) + ); + + if ( + ! preg_match( + '/\/api\/campaign\/\d+\/([a-z_]+)$/', + $bridge->endpoint, + $matches + ) + ) { + return array(); + } + + $source = $matches[1]; + + $common_schema = array( + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'firstname', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'lastname', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'address', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'lang', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + ), + ); + + switch ( $source ) { + case 'subscription_request': + return array_merge( + array( + array( + 'name' => 'ordered_parts', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'type', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'remuneration_type', + 'schema' => array( 'type' => 'string' ), + ), + ), + $common_schema + ); + case 'donation_request': + return array_merge( + array( + array( + 'name' => 'donation_amount', + 'schema' => array( 'type' => 'integer' ), + ), + // [ + // 'name' => 'tax_receipt_option', + // 'schema' => ['type' => 'string'], + // ], + ), + $common_schema + ); + case 'loan_request': + return array_merge( + array( + array( + 'name' => 'loan_amount', + 'schema' => array( 'type' => 'integer' ), + ), + ), + $common_schema + ); + } + } +} + +Finan_Coop_Addon::setup(); diff --git a/forms-bridge/addons/financoop/class-financoop-form-bridge.php b/forms-bridge/addons/financoop/class-financoop-form-bridge.php new file mode 100644 index 00000000..db8780f6 --- /dev/null +++ b/forms-bridge/addons/financoop/class-financoop-form-bridge.php @@ -0,0 +1,129 @@ + '2.0', + 'params' => $payload, + ); + } + + add_filter( + 'http_bridge_backend_headers', + function ( $headers, $backend ) { + if ( $backend->name === $this->data['backend'] ) { + $credential = $backend->credential; + if ( ! $credential ) { + return $headers; + } + + [ + $database, + $username, + $password, + ] = $credential->authorization(); + $headers['X-Odoo-Db'] = $database; + $headers['X-Odoo-Username'] = $username; + $headers['X-Odoo-Api-Key'] = $password; + } + + return $headers; + }, + 10, + 2 + ); + + add_filter( + 'http_bridge_request', + static function ( $request ) { + self::$request = $request; + return $request; + }, + 10, + 1 + ); + + $response = parent::submit( $payload ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + if ( isset( $response['data']['error'] ) ) { + $error = new WP_Error( + 'response_code_' . $response['data']['error']['code'], + $response['data']['error']['message'], + $response['data']['error']['data'] + ); + + $error_data = array( 'response' => $response ); + if ( self::$request ) { + $error_data['request'] = self::$request; + } + + $error->add_data( $error_data ); + return $error; + } + + if ( isset( $response['data']['result']['error'] ) ) { + /* TODO: Gestionar els errors RPC (status is not a key) */ + $error = new WP_Error( + 'response_code_' . $response['data']['result']['status'], + $response['data']['result']['error'], + $response['data']['result']['details'] + ); + + $error_data = array( 'response' => $response ); + if ( self::$request ) { + $error_data['request'] = self::$request; + } + + $error->add_data( $error_data ); + return $error; + } + + $response['data'] = $response['data']['data'] ?? array(); + return $response; + } +} diff --git a/forms-bridge/addons/financoop/hooks.php b/forms-bridge/addons/financoop/hooks.php new file mode 100644 index 00000000..2f226ca7 --- /dev/null +++ b/forms-bridge/addons/financoop/hooks.php @@ -0,0 +1,230 @@ + array( + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'campaign_id', + 'label' => __( 'Campaign', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/campaign', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#backend', + 'name' => 'name', + 'default' => 'FinanCoop', + ), + array( + 'ref' => '#credential', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'schema', + 'type' => 'text', + 'value' => 'RPC', + ), + array( + 'ref' => '#credential', + 'name' => 'database', + 'label' => __( 'Database', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_id', + 'label' => __( 'Username', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_secret', + 'label' => __( 'Password', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + ), + 'bridge' => array( + 'backend' => 'FinanCoop', + 'method' => 'POST', + ), + 'backend' => array( + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'credential' => array( + 'name' => '', + 'schema' => 'RPC', + 'client_id' => '', + 'client_secret' => '', + 'database' => '', + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'financoop-' ) !== 0 ) { + return $data; + } + + $index = array_search( + 'campaign_id', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $campaign_id = $data['bridge']['custom_fields'][ $index ]['value']; + + $data['bridge']['endpoint'] = preg_replace( + '/\{campaign_id\}/', + $campaign_id, + $data['bridge']['endpoint'] + ); + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } else { + return new WP_Error( + 'invalid_fields', + __( + 'Financoop template requireds the field $campaign_id', + 'forms-bridge' + ), + array( 'status' => 400 ) + ); + } + + $endpoint = implode( + '/', + array_slice( explode( '/', $data['bridge']['endpoint'] ), 0, 4 ) + ); + + $data['backend']['credential'] = $data['credential']['name']; + + Backend::temp_registration( $data['backend'] ); + Credential::temp_registration( $data['credential'] ); + + $addon = FBAPI::get_addon( 'financoop' ); + $response = $addon->fetch( + $endpoint, + $data['backend']['name'], + $data['credential']['name'] + ); + + if ( is_wp_error( $response ) ) { + return new WP_Error( + 'financoop_api_error', + __( 'Can\'t fetch campaign data', 'forms-bridge' ), + array( 'status' => 500 ) + ); + } + + $campaign = $response['data']; + $field_names = array_column( $data['form']['fields'], 'name' ); + + $index = array_search( 'donation_amount', $field_names ); + if ( $index !== false ) { + $field = &$data['form']['fields'][ $index ]; + + $min = $campaign['minimal_donation_amount']; + if ( ! empty( $min ) ) { + $field['min'] = $min; + $field['default'] = $min; + } + } + + $index = array_search( 'loan_amount', $field_names ); + if ( $index !== false ) { + $field = &$data['form']['fields'][ $index ]; + + $min = $campaign['minimal_loan_amount']; + if ( ! empty( $min ) ) { + $field['min'] = $min; + $field['default'] = $min; + } + + $max = $campaign['maximal_loan_amount']; + if ( ! empty( $max ) ) { + $field['max'] = $max; + } + } + + $index = array_search( 'ordered_parts', $field_names ); + if ( $index !== false ) { + $field = &$data['form']['fields'][ $index ]; + + $min = $campaign['minimal_subscription_amount']; + if ( ! empty( $min ) ) { + $field['min'] = $min; + $field['step'] = $min; + $field['default'] = $min; + } + + $max = $campaign['maximal_subscription_amount']; + if ( ! empty( $max ) ) { + $field['max'] = $max; + } + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/financoop/jobs/vat-id.php b/forms-bridge/addons/financoop/jobs/vat-id.php new file mode 100644 index 00000000..9ec35727 --- /dev/null +++ b/forms-bridge/addons/financoop/jobs/vat-id.php @@ -0,0 +1,83 @@ + __( 'Prefixed vat ID', 'forms-bridge' ), + 'description' => __( + 'Prefix the vat with country code, or the current locale, if it isn\'t prefixed', + 'forms-bridge' + ), + 'method' => 'forms_bridge_financoop_vat_id', + 'input' => array( + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'country' ), + ), + ), +); diff --git a/forms-bridge/addons/financoop/shortcodes.php b/forms-bridge/addons/financoop/shortcodes.php new file mode 100644 index 00000000..8c945f51 --- /dev/null +++ b/forms-bridge/addons/financoop/shortcodes.php @@ -0,0 +1,390 @@ + '__financoop-' . time(), + 'endpoint' => "/api/campaign/{$atts['id']}", + 'backend' => $atts['backend'], + 'method' => 'GET', + ), + 'financoop' + ); + + $response = $bridge->submit(); + if ( is_wp_error( $response ) ) { + $message = $response->get_error_message(); + return forms_bridge_financoop_shortcode_error( + "Fetch campaign error message: {$message}", + $atts + ); + } + + $campaign = $response['data']; + } catch ( Error | Exception $e ) { + return forms_bridge_financoop_shortcode_error( + "Fetch campaign error message: {$e->getMessage()}", + $atts + ); + } + + ob_start(); + ?>
+
+
+ +
+

+ +

+ +

+ +
+
+ + +
+
+ +
+

+ +

+ + + : + + / + + +

+ +

+ + + : + + +

+ +

+ + + : + + +

+ +

+ + + : + + % +

+ +
+ % +
+
+ $source, + 'label' => $label, + 'progress' => $progress, + 'amount' => $amount, + 'goal' => $goal, + ) + ); +} + +function financoop_render_campaign_dates( $campaign ) { + if ( $campaign['is_permanent'] ) { + return ''; + } + + $start = $campaign['start_date']; + $end = $campaign['end_date']; + $days_to_start = null; + $days_to_end = null; + + $start_time = strtotime( $start ); + $end_time = null; + + $now = time(); + + if ( $start_time > $now ) { + $days_to_start = max( 1, ( $start_time - $now ) / ( 60 * 60 * 24 ) ); + } + + if ( $end ) { + $end_time = strtotime( $end ); + $days_to_end = max( 0, ( $end_time - $now ) / ( 60 * 60 * 24 ) ); + } + + if ( ! $start ) { + return ''; + } + + $output = '
'; + + if ( $days_to_start ) { + $output .= sprintf( + '

%s: %s

', + esc_html( + _x( 'Days to start', 'financoop campaign widget', 'forms-bridge' ) + ), + (int) $days_to_start + ); + } + + if ( $end ) { + $end_date = new DateTime( $end ); + $output .= sprintf( + '

%s: %s

', + esc_html( + _x( 'End date', 'financoop campaign widget', 'forms-bridge' ) + ), + esc_html( IntlDateFormatter::formatObject( $end_date, 'dd/MM/Y' ) ) + ); + + if ( ! $days_to_start && $days_to_end > 0 ) { + $output .= sprintf( + '

%s: %s

', + esc_html( + _x( + 'Days to end', + 'financoop campaign widget', + 'forms-bridge' + ) + ), + (int) $days_to_end + ); + } + } + + $output .= '
'; + + return apply_filters( + 'forms_bridge_financoop_dates_html', + $output, + $campaign, + array( + 'start' => $start, + 'end' => $end, + 'days_to_start' => $days_to_start, + 'days_to_end' => $days_to_end, + ) + ); +} + +function forms_bridge_financoop_shortcode_error( $message, $atts ) { + $atts = implode( + ' ', + array_reduce( + array( 'id', 'backend', 'sources', 'currency' ), + function ( $handle, $attr ) use ( $atts ) { + if ( isset( $atts[ $attr ] ) ) { + $value = implode( ',', (array) $atts[ $attr ] ); + $handle[] = "{$attr}='{$value}'"; + } + + return $handle; + }, + array() + ) + ); + + return "[financoop_campaign {$atts}]{$message}[/financoop_campaign]"; +} diff --git a/forms-bridge/addons/financoop/templates/donation-requests.php b/forms-bridge/addons/financoop/templates/donation-requests.php new file mode 100644 index 00000000..83ca5ac2 --- /dev/null +++ b/forms-bridge/addons/financoop/templates/donation-requests.php @@ -0,0 +1,146 @@ + __( 'Donation Requests', 'forms-bridge' ), + 'description' => __( + 'Donations form template. The resulting bridge will convert form submissions into donation requests.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/campaign/{campaign_id}/donation_request', + ), + // [ + // 'ref' => '#bridge/custom_fields[]', + // 'name' => 'tax_receipt_option', + // 'label' => __('Tax receipt', 'forms-bridge'), + // 'type' => 'select', + // 'options' => [ + // [ + // 'label' => 'foo', + // 'value' => 'foo', + // ], + // [ + // 'label' => 'bar', + // 'value' => 'bar', + // ], + // ], + // 'required' => true, + // ], + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Donation Requests', 'forms-bridge' ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/campaign/{campaign_id}/donation_request', + 'mutations' => array( + array( + array( + 'from' => 'donation_amount', + 'to' => 'donation_amount', + 'cast' => 'integer', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'country_code', + 'cast' => 'string', + ), + ), + ), + 'custom_fields' => array( + array( + 'name' => 'lang', + 'value' => '$locale', + ), + ), + 'workflow' => array( 'iso2-country-code', 'vat-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Donation amount', 'forms-bridge' ), + 'name' => 'donation_amount', + 'type' => 'number', + 'required' => true, + 'min' => 0, + ), + array( + 'label' => __( 'First name', 'forms-bridge' ), + 'name' => 'firstname', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Last name', 'forms-bridge' ), + 'name' => 'lastname', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'ID number', 'forms-bridge' ), + 'name' => 'vat', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Nationality', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip_code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/financoop/templates/loan-requests.php b/forms-bridge/addons/financoop/templates/loan-requests.php new file mode 100644 index 00000000..1ebee0f8 --- /dev/null +++ b/forms-bridge/addons/financoop/templates/loan-requests.php @@ -0,0 +1,129 @@ + __( 'Loan Requests', 'forms-bridge' ), + 'description' => __( + 'Loans form template. The resulting bridge will convert form submissions into loan requests.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/campaign/{campaign_id}/loan_request', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Loan Requests', 'forms-bridge' ), + ), + ), + 'bridge' => array( + 'endpoint' => '/api/campaign/{campaign_id}/loan_request', + 'mutations' => array( + array( + array( + 'from' => 'loan_amount', + 'to' => 'loan_amount', + 'cast' => 'integer', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'country_code', + 'cast' => 'string', + ), + ), + ), + 'custom_fields' => array( + array( + 'name' => 'lang', + 'value' => '$locale', + ), + ), + 'workflow' => array( 'iso2-country-code', 'vat-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Loan amount', 'forms-bridge' ), + 'name' => 'loan_amount', + 'type' => 'number', + 'required' => true, + 'min' => 0, + ), + array( + 'label' => __( 'First name', 'forms-bridge' ), + 'name' => 'firstname', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Last name', 'forms-bridge' ), + 'name' => 'lastname', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'ID number', 'forms-bridge' ), + 'name' => 'vat', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Nationality', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip_code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/financoop/templates/subscription-requests.php b/forms-bridge/addons/financoop/templates/subscription-requests.php new file mode 100644 index 00000000..6f09dd35 --- /dev/null +++ b/forms-bridge/addons/financoop/templates/subscription-requests.php @@ -0,0 +1,149 @@ + __( 'Subscription Requests', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will convert form submissions into subscription requests.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Subscription Requests', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/campaign/{campaign_id}/subscription_request', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/campaign/{campaign_id}/subscription_request', + 'custom_fields' => array( + array( + 'name' => 'lang', + 'value' => '$locale', + ), + array( + 'name' => 'type', + 'value' => 'increase', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'ordered_parts', + 'to' => 'ordered_parts', + 'cast' => 'integer', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'country_code', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'vat-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Ordered parts', 'forms-bridge' ), + 'name' => 'ordered_parts', + 'type' => 'number', + 'required' => true, + 'min' => 1, + ), + array( + 'label' => __( 'Remuneration type', 'forms-bridge' ), + 'name' => 'remuneration_type', + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'value' => 'cash', + 'label' => __( 'Cash', 'forms-bridge' ), + ), + array( + 'value' => 'wallet', + 'label' => __( 'Wallet', 'forms-bridge' ), + ), + ), + ), + array( + 'label' => __( 'First name', 'forms-bridge' ), + 'name' => 'firstname', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Last name', 'forms-bridge' ), + 'name' => 'lastname', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'ID number', 'forms-bridge' ), + 'name' => 'vat', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Nationality', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip_code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + ), + ), +); diff --git a/addons/gsheets/assets/logo.png b/forms-bridge/addons/gsheets/assets/logo.png similarity index 100% rename from addons/gsheets/assets/logo.png rename to forms-bridge/addons/gsheets/assets/logo.png diff --git a/forms-bridge/addons/gsheets/class-gsheets-addon.php b/forms-bridge/addons/gsheets/class-gsheets-addon.php new file mode 100644 index 00000000..eafc1859 --- /dev/null +++ b/forms-bridge/addons/gsheets/class-gsheets-addon.php @@ -0,0 +1,183 @@ +addon === 'gsheets' ) { + return false; + } + + return $prune; + }, + 5, + 2 + ); + } + + /** + * Performs a request against the backend to check the connexion status. + * + * @param string $backend Backend name. + * + * @return boolean + */ + public function ping( $backend ) { + $bridge = new Google_Sheets_Form_Bridge( + array( + 'name' => '__gsheets-' . time(), + 'backend' => $backend, + 'endpoint' => '/', + 'method' => 'GET', + 'tab' => 'foo', + ) + ); + + $backend = $bridge->backend; + if ( ! $backend ) { + return false; + } + + $credential = $backend->credential; + if ( ! $credential ) { + return false; + } + + $parsed = wp_parse_url( $backend->base_url ); + $host = $parsed['host'] ?? ''; + + if ( $host !== 'sheets.googleapis.com' ) { + return false; + } + + $access_token = $credential->get_access_token(); + return (bool) $access_token; + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint Concatenation of spreadsheet ID and tab name. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $backend = FBAPI::get_backend( $backend ); + if ( ! $backend ) { + return new WP_Error( 'invalid_backend' ); + } + + $credential = $backend->credential; + if ( ! $credential ) { + return new WP_Error( 'invalid_credential' ); + } + + $access_token = $credential->get_access_token(); + if ( ! $access_token ) { + return new WP_Error( 'invalid_credential' ); + } + + $response = http_bridge_get( + 'https://www.googleapis.com/drive/v3/files', + array( 'q' => "mimeType = 'application/vnd.google-apps.spreadsheet'" ), + array( + 'Authorization' => "Bearer {$access_token}", + 'Accept' => 'application/json', + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response; + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint Concatenation of spreadsheet ID and tab name. + * @param string $backend Backend name. + * + * @return array List of fields and content type of the endpoint. + */ + public function get_endpoint_schema( $endpoint, $backend ) { + $bridges = FBAPI::get_addon_bridges( self::NAME ); + foreach ( $bridges as $candidate ) { + $data = $candidate->data(); + if ( ! $data ) { + continue; + } + + if ( + $data['endpoint'] === $endpoint && + $data['backend'] === $backend + ) { + $bridge = $candidate; + } + } + + if ( ! isset( $bridge ) ) { + return array(); + } + + $headers = $bridge->get_headers(); + + if ( is_wp_error( $headers ) ) { + return array(); + } + + $fields = array(); + foreach ( $headers as $header ) { + $fields[] = array( + 'name' => $header, + 'schema' => array( 'type' => 'string' ), + ); + } + + return $fields; + } +} + +Google_Sheets_Addon::setup(); diff --git a/forms-bridge/addons/gsheets/class-gsheets-form-bridge.php b/forms-bridge/addons/gsheets/class-gsheets-form-bridge.php new file mode 100644 index 00000000..0b216cff --- /dev/null +++ b/forms-bridge/addons/gsheets/class-gsheets-form-bridge.php @@ -0,0 +1,234 @@ +tab ); + + if ( empty( $values ) ) { + return $range; + } + + $abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $len = strlen( $abc ); + + $columns = array(); + for ( $row = 0; $row < count( $values ); $row++ ) { + $rowcols = array(); + $i = -1; + + for ( $col = 0; $col < count( $values[ $row ] ); $col++ ) { + if ( $col > 0 && $col % $len === 0 ) { + ++$i; + } + + if ( $col >= $len ) { + $index = $col % $len; + $rowcols[] = $abc[ $i ] . $abc[ $index ]; + } else { + $rowcols[] = $abc[ $col ]; + } + } + + if ( count( $rowcols ) > count( $columns ) ) { + $columns = $rowcols; + } + } + + $range .= '!' . $columns[0] . '1'; + $range .= ':' . $columns[ count( $columns ) - 1 ] . $row; + + return $range; + } + + public function get_headers( $backend = null ) { + if ( ! $this->is_valid ) { + return new WP_Error( 'invalid_bridge' ); + } + + if ( ! $backend ) { + $backend = $this->backend; + } + + $range = rawurlencode( $this->tab ) . '!1:1'; + + $response = $backend->get( $this->endpoint . '/values/' . $range ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response['data']['values'][0] ?? array(); + } + + private function add_sheet( $index, $title, $backend ) { + $response = $backend->post( + $this->endpoint . ':batchUpdate', + array( + 'requests' => array( + array( + 'addSheet' => array( + 'properties' => array( + 'sheetId' => time(), + 'index' => $index, + 'title' => $title, + 'sheetType' => 'GRID', + 'gridProperties' => array( + 'rowCount' => 1000, + 'columnCount' => 26, + ), + 'hidden' => false, + ), + ), + ), + ), + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response['data']; + } + + private function get_sheets( $backend ) { + $response = $backend->get( $this->endpoint ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $sheets = array(); + foreach ( $response['data']['sheets'] as $sheet ) { + $sheets[] = strtolower( $sheet['properties']['title'] ); + } + + return $sheets; + } + + /** + * + * @param array $payload Submission data. + * @param array $attachments Submission's attached files. Will be ignored. + * + * @return array|WP_Error Http request response. + */ + public function submit( $payload = array(), $attachments = array() ) { + if ( ! $this->is_valid ) { + return new WP_Error( 'invalid_bridge' ); + } + + $backend = $this->backend; + + $sheets = $this->get_sheets( $backend ); + if ( is_wp_error( $sheets ) ) { + return $sheets; + } + + if ( ! in_array( strtolower( $this->tab ), $sheets, true ) ) { + $result = $this->add_sheet( count( $sheets ), $this->tab, $backend ); + if ( is_wp_error( $result ) ) { + return $result; + } + } + + $endpoint = $this->endpoint . '/values/' . rawurlencode( $this->tab ); + $method = $this->method; + + if ( $method === 'POST' || $method === 'PUT' ) { + $endpoint .= '!A1:Z:append/?valueInputOption=USER_ENTERED'; + + $headers = $this->get_headers( $backend ); + if ( is_wp_error( $headers ) ) { + return $headers; + } + + $payload = self::flatten_payload( $payload ); + $values = array(); + + if ( empty( $headers ) ) { + $headers = array_keys( $payload ); + $values[] = $headers; + } + + $row = array(); + foreach ( $headers as $header ) { + if ( isset( $payload[ $header ] ) ) { + $row[] = $payload[ $header ] ?? ''; + } else { + $row[] = ''; + } + } + + $values[] = $row; + + $payload = array( + // 'range' => $this->value_range($values), + 'majorDimension' => 'ROWS', + 'values' => $values, + ); + } + + return $this->backend->$method( $endpoint, $payload ); + } + + /** + * Sheets are flat, if payload has nested arrays, flattens it and concatenate its keys + * as field names. + * + * @param array $payload Submission payload. + * @param string $path Prefix to prepend to the field name. + * + * @return array Flattened payload. + */ + private static function flatten_payload( $payload, $path = '' ) { + $flat = array(); + foreach ( $payload as $field => $value ) { + $key = $path . $field; + $value = self::flatten_value( $value, $key ); + + if ( ! is_array( $value ) ) { + $flat[ $key ] = $value; + } else { + foreach ( $value as $_key => $_val ) { + $flat[ $_key ] = $_val; + } + } + } + + return $flat; + } + + private static function flatten_value( $value, $path = '' ) { + if ( ! is_array( $value ) ) { + return $value; + } + + if ( wp_is_numeric_array( $value ) ) { + $simple_items = array_filter( $value, fn( $item ) => ! is_array( $item ) ); + + if ( count( $simple_items ) === count( $value ) ) { + return implode( ',', $value ); + } + } + + return self::flatten_payload( $value, $path . '.' ); + } +} diff --git a/forms-bridge/addons/gsheets/hooks.php b/forms-bridge/addons/gsheets/hooks.php new file mode 100644 index 00000000..35d7c754 --- /dev/null +++ b/forms-bridge/addons/gsheets/hooks.php @@ -0,0 +1,194 @@ + __( 'Name of the spreadsheet tab', 'forms-bridge' ), + 'type' => 'string', + 'minLength' => 1, + 'required' => true, + 'default' => 'Sheet1', + ); + + return $schema; + }, + 10, + 2 +); + +add_filter( + 'forms_bridge_template_defaults', + function ( $defaults, $addon, $schema ) { + if ( $addon !== 'gsheets' ) { + return $defaults; + } + + $defaults = wpct_plugin_merge_object( + array( + 'fields' => array( + array( + 'ref' => '#credential', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'schema', + 'type' => 'text', + 'value' => 'Bearer', + ), + array( + 'ref' => '#credential', + 'name' => 'oauth_url', + 'label' => __( 'Authorization URL', 'forms-bridge' ), + 'type' => 'text', + 'value' => 'https://accounts.google.com/o/oauth2/v2', + ), + array( + 'ref' => '#credential', + 'name' => 'client_id', + 'label' => __( 'Client ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_secret', + 'label' => __( 'Client secret', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'scope', + 'label' => __( 'Scope', 'forms-bridge' ), + 'type' => 'text', + 'value' => + 'https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/spreadsheets', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'label' => __( 'Spreadsheet', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/drive/v3/files', + 'finger' => array( + 'value' => 'files[].id', + 'label' => 'files[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + array( + 'ref' => '#bridge', + 'name' => 'tab', + 'label' => __( 'Tab', 'forms-bridge' ), + 'type' => 'text', + 'default' => '', + ), + array( + 'ref' => '#backend', + 'name' => 'name', + 'default' => 'Sheets API', + ), + array( + 'ref' => '#backend', + 'name' => 'base_url', + 'value' => 'https://sheets.googleapis.com', + ), + ), + 'backend' => array( + 'name' => 'Sheets API', + 'base_url' => 'https://sheets.googleapis.com', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'bridge' => array( + 'backend' => 'Sheets API', + 'endpoint' => '', + 'tab' => '', + ), + 'credential' => array( + 'name' => '', + 'schema' => 'Bearer', + 'oauth_url' => 'https://accounts.google.com/o/oauth2/v2', + 'scope' => + 'https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/spreadsheets', + 'client_id' => '', + 'client_secret' => '', + 'access_token' => '', + 'expires_at' => 0, + 'refresh_token' => '', + ), + ), + $defaults, + $schema + ); + + return $defaults; + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'gsheets-' ) !== 0 ) { + return $data; + } + + $data['bridge']['endpoint'] = + '/v4/spreadsheets/' . $data['bridge']['endpoint']; + return $data; + }, + 10, + 2 +); + +add_filter( + 'http_bridge_oauth_url', + function ( $url, $verb ) { + if ( strpos( $url, 'accounts.google.com' ) === false ) { + return $url; + } + + if ( $verb === 'auth' ) { + return $url; + } + + return "https://oauth2.googleapis.com/{$verb}"; + }, + 10, + 2 +); diff --git a/addons/gsheets/templates/spreadsheet-contacts.json b/forms-bridge/addons/gsheets/templates/spreadsheet-contacts.json similarity index 100% rename from addons/gsheets/templates/spreadsheet-contacts.json rename to forms-bridge/addons/gsheets/templates/spreadsheet-contacts.json diff --git a/forms-bridge/addons/gsheets/templates/woo-orders.php b/forms-bridge/addons/gsheets/templates/woo-orders.php new file mode 100644 index 00000000..8f847f85 --- /dev/null +++ b/forms-bridge/addons/gsheets/templates/woo-orders.php @@ -0,0 +1,322 @@ + __( 'WC Orders', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into new lines on a Google Spreadsheet with order and customer information.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + ), + 'bridge' => array( + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'Order ID', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'Name[][0]', + 'cast' => 'string', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'Name[][1]', + 'cast' => 'string', + ), + array( + 'from' => 'Name[]', + 'to' => 'Name[]', + 'cast' => 'concat', + ), + array( + 'from' => 'Name', + 'to' => 'Name', + 'cast' => 'csv', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'Date', + 'cast' => 'string', + ), + array( + 'from' => 'total', + 'to' => 'Total', + 'cast' => 'number', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines[].name', + 'to' => 'Shipping', + 'cast' => 'string', + ), + array( + 'from' => 'Shipping', + 'to' => 'Shipping', + 'cast' => 'csv', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'Shipping Total', + 'cast' => 'number', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines[].code', + 'to' => 'Coupons', + 'cast' => 'string', + ), + array( + 'from' => 'Coupons', + 'to' => 'Coupons', + 'cast' => 'csv', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'Discount Total', + 'cast' => 'number', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'First Name', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'Last Name', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'Email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'Phone', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'Address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_2', + 'to' => 'Address 2', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'City', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'Postal Code', + 'cast' => 'string', + ), + array( + 'from' => '?billing.state', + 'to' => 'State', + 'cast' => 'string', + ), + array( + 'from' => '?billing.country', + 'to' => 'Country', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'Payment Method', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'Note', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/api.php b/forms-bridge/addons/holded/api.php new file mode 100644 index 00000000..529d4087 --- /dev/null +++ b/forms-bridge/addons/holded/api.php @@ -0,0 +1,129 @@ +patch( + array( + 'name' => 'holded-search-contact-query', + 'endpoint' => '/api/invoicing/v1/contacts', + 'method' => 'GET', + ) + ) + ->submit( $query ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + if ( empty( $response['data'] ) ) { + return; + } + + return $response['data'][0]; +} + +function forms_bridge_holded_update_contact( $payload, $bridge ) { + return forms_bridge_holded_create_contact( $payload, $bridge, true ); +} + +function forms_bridge_holded_create_contact( $payload, $bridge, $update = false ) { + if ( ! $update ) { + $contact = forms_bridge_holded_search_contact( $payload, $bridge ); + + if ( ! is_wp_error( $contact ) && isset( $contact['id'] ) ) { + $payload['id'] = $contact['id']; + return forms_bridge_holded_update_contact( $payload, $bridge ); + } + } + + $contact = array( + 'name' => $payload['name'], + ); + + $contact_fields = array( + 'tradeName', + 'email', + 'mobile', + 'phone', + 'type', + 'code', + 'vatnumber', + 'iban', + 'swift', + 'billAddress', + 'defaults', + 'tags', + 'note', + 'isperson', + 'contactPersons', + 'shippingAddresses', + 'CustomId', + ); + + foreach ( $contact_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $contact[ $field ] = $payload[ $field ]; + } + } + + $method = 'POST'; + $endpoint = '/api/invoicing/v1/contacts'; + + if ( $update && isset( $payload['id'] ) ) { + $method = 'PUT'; + $endpoint = $endpoint . '/' . $payload['id']; + } + + $response = $bridge + ->patch( + array( + 'name' => 'holded-create-contact', + 'endpoint' => $endpoint, + 'method' => $method, + ) + ) + ->submit( $contact ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $contact_id = $response['data']['id']; + + $response = $bridge + ->patch( + array( + 'name' => 'holded-get-contact-by-id', + 'endpoint' => '/api/invoicing/v1/contacts/' . $contact_id, + 'method' => 'GET', + ) + ) + ->submit(); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response['data']; +} diff --git a/addons/holded/assets/logo.png b/forms-bridge/addons/holded/assets/logo.png similarity index 100% rename from addons/holded/assets/logo.png rename to forms-bridge/addons/holded/assets/logo.png diff --git a/forms-bridge/addons/holded/class-holded-addon.php b/forms-bridge/addons/holded/class-holded-addon.php new file mode 100644 index 00000000..00087bf6 --- /dev/null +++ b/forms-bridge/addons/holded/class-holded-addon.php @@ -0,0 +1,177 @@ + '__holded-' . time(), + 'endpoint' => '/api/invoicing/v1/contacts', + 'method' => 'GET', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit( array( 'limit' => 1 ) ); + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge = new Holded_Form_Bridge( + array( + 'name' => '__holded-' . time(), + 'endpoint' => $endpoint, + 'backend' => $backend, + 'method' => 'GET', + ) + ); + + return $bridge->submit(); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array List of fields and content type of the endpoint. + */ + public function get_endpoint_schema( $endpoint, $backend ) { + $chunks = array_values( array_filter( explode( '/', $endpoint ) ) ); + if ( empty( $chunks ) ) { + return array(); + } + + $api_base = $chunks[0]; + if ( 'api' !== $api_base ) { + array_unshift( $chunks, 'api' ); + } + + [, $module, $version, $resource] = $chunks; + + if ( + ! in_array( + $module, + array( + 'invoicing', + 'crm', + 'projects', + 'team', + 'accounting', + ), + true + ) || + 'v1' !== $version + ) { + return array(); + } + + $path = plugin_dir_path( __FILE__ ) . "/data/swagger/{$module}.json"; + if ( ! is_file( $path ) ) { + return array(); + } + + $file_content = file_get_contents( $path ); + try { + $paths = json_decode( $file_content, true ); + } catch ( TypeError ) { + return array(); + } + + $path = '/' . $resource; + if ( 'documents' === $resource ) { + $path .= '/{docType}'; + } + + if ( ! isset( $paths[ $path ] ) ) { + return array(); + } + + $schema = $paths[ $path ]; + if ( ! isset( $schema['post'] ) ) { + return array(); + } + + $schema = $schema['post']; + + $fields = array(); + if ( isset( $schema['parameters'] ) ) { + foreach ( $schema['parameters'] as $param ) { + $fields[] = array( + 'name' => $param['name'], + 'schema' => $param['schema'], + ); + } + } elseif ( + isset( + $schema['requestBody']['content']['application/json']['schema']['properties'] + ) + ) { + $properties = + $schema['requestBody']['content']['application/json']['schema']['properties']; + foreach ( $properties as $name => $schema ) { + $fields[] = array( + 'name' => $name, + 'schema' => $schema, + ); + } + } + + return $fields; + } +} + +Holded_Addon::setup(); diff --git a/forms-bridge/addons/holded/class-holded-form-bridge.php b/forms-bridge/addons/holded/class-holded-form-bridge.php new file mode 100644 index 00000000..f58bd756 --- /dev/null +++ b/forms-bridge/addons/holded/class-holded-form-bridge.php @@ -0,0 +1,17 @@ + array( + array( + 'ref' => '#backend', + 'name' => 'name', + 'description' => __( + 'Label of the Holded API backend connection', + 'forms-bridge' + ), + 'default' => 'Holded API', + ), + array( + 'ref' => '#backend', + 'name' => 'base_url', + 'value' => 'https://api.holded.com', + ), + array( + 'ref' => '#backend/headers[]', + 'name' => 'key', + 'label' => __( 'API Key', 'forms-bridge' ), + 'description' => __( + 'Get it from your account', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + ), + 'bridge' => array( + 'method' => 'POST', + ), + 'backend' => array( + 'base_url' => 'https://api.holded.com', + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'holded-' ) !== 0 ) { + return $data; + } + + $index = array_search( + 'tags', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = &$data['bridge']['custom_fields'][ $index ]; + + if ( ! empty( $field['value'] ) ) { + $tags = array_filter( + array_map( 'trim', explode( ',', strval( $field['value'] ) ) ) + ); + + for ( $i = 0; $i < count( $tags ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "tags[{$i}]", + 'value' => $tags[ $i ], + ); + } + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/holded/jobs/appointment-dates.php b/forms-bridge/addons/holded/jobs/appointment-dates.php new file mode 100644 index 00000000..fd4b2a4a --- /dev/null +++ b/forms-bridge/addons/holded/jobs/appointment-dates.php @@ -0,0 +1,51 @@ +getTimestamp(); + $payload['startDate'] = $timestamp; + $payload['duration'] = floatval( $payload['duration'] ?? 1 ); + + return $payload; +} + +return array( + 'title' => __( 'Appointment dates', 'forms-bridge' ), + 'description' => __( + 'Sets appointment start time and duration from datetime and duration fields', + 'forms-bridge' + ), + 'method' => 'forms_bridge_holded_appointment_dates', + 'input' => array( + array( + 'name' => 'date', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'duration', + 'schema' => array( 'type' => 'number' ), + ), + ), + 'output' => array( + array( + 'name' => 'startDate', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'duration', + 'schema' => array( 'type' => 'number' ), + ), + ), +); diff --git a/forms-bridge/addons/holded/jobs/contact-id.php b/forms-bridge/addons/holded/jobs/contact-id.php new file mode 100644 index 00000000..55332fac --- /dev/null +++ b/forms-bridge/addons/holded/jobs/contact-id.php @@ -0,0 +1,152 @@ + __( 'Contact ID', 'forms-bridge' ), + 'description' => __( + 'Creates a new contact and sets its ID as the contactId field of the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_holded_contact_id', + 'input' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'CustomId', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'tradeName', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'type', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vatnumber', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'iban', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'swift', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'billAddress', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'address' => array( 'type' => 'string' ), + 'postalCode' => array( 'type' => 'string' ), + 'city' => array( 'type' => 'string' ), + 'countryCode' => array( 'type' => 'string' ), + ), + 'additionalProperties' => true, + ), + ), + array( + 'name' => 'defaults', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'language' => array( 'type' => 'string' ), + ), + 'additionalProperties' => true, + ), + ), + array( + 'name' => 'tags', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'additionalItems' => true, + ), + ), + array( + 'name' => 'note', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'isperson', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'contactPersons', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( 'type' => 'string' ), + 'email' => array( 'type' => 'string' ), + 'phone' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + ), + array( + 'name' => 'shippingAddresses', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( 'type' => 'string' ), + 'address' => array( 'type' => 'string' ), + 'city' => array( 'type' => 'string' ), + 'postalCode' => array( 'type' => 'string' ), + 'province' => array( 'type' => 'string' ), + 'country' => array( 'type' => 'string' ), + 'note' => array( 'type' => 'string' ), + 'privateNote' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + ), + ), + 'output' => array( + array( + 'name' => 'contactId', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/holded/jobs/prefix-vatnumber.php b/forms-bridge/addons/holded/jobs/prefix-vatnumber.php new file mode 100644 index 00000000..c733892c --- /dev/null +++ b/forms-bridge/addons/holded/jobs/prefix-vatnumber.php @@ -0,0 +1,84 @@ + __( 'Vatnumber prefix', 'forms-bridge' ), + 'description' => __( + 'Prefix the vat with country code, or the current locale, if it isn\'t prefixed', + 'forms-bridge' + ), + 'method' => 'forms_bridge_holded_prefix_vatnumber', + 'input' => array( + array( + 'name' => 'vatnumber', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'countryCode', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'vatnumber', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'vatnumber' ), + ), + array( + 'name' => 'countryCode', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'countryCode' ), + ), + ), +); diff --git a/forms-bridge/addons/holded/jobs/skip-if-contact-exists.php b/forms-bridge/addons/holded/jobs/skip-if-contact-exists.php new file mode 100644 index 00000000..c53b3ef2 --- /dev/null +++ b/forms-bridge/addons/holded/jobs/skip-if-contact-exists.php @@ -0,0 +1,66 @@ + __( 'Skip if contact exists', 'forms-bridge' ), + 'description' => __( + 'Search for a contact and skip submission if it exists', + 'forms-bridge' + ), + 'method' => 'forms_bridge_holded_skip_contact', + 'input' => array( + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'customId', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'phone' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'mobile' ), + ), + array( + 'name' => 'customId', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'customId' ), + ), + ), +); diff --git a/forms-bridge/addons/holded/jobs/sync-products-by-sku.php b/forms-bridge/addons/holded/jobs/sync-products-by-sku.php new file mode 100644 index 00000000..e30685d6 --- /dev/null +++ b/forms-bridge/addons/holded/jobs/sync-products-by-sku.php @@ -0,0 +1,139 @@ + __( 'Sync woo products', 'forms-bridge' ), + 'description' => __( + 'Search for products from the WooCommerce order by sku on Holded and creates new ones if someone does not exists', + 'forms-bridge' + ), + 'method' => 'forms_bridge_holded_sync_products_by_sku', + 'input' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + 'required' => array( 'sku', 'name', 'price' ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_holded_sync_products_by_sku( $payload, $bridge ) { + $product_refs = array(); + foreach ( $payload['line_items'] as $line_item ) { + if ( empty( $line_item['product']['sku'] ) ) { + return new WP_Error( + "SKU is required on product {$line_item['product']['name']}" + ); + } + + $product_refs[] = $line_item['product']['sku']; + } + + $response = $bridge + ->patch( + array( + 'name' => 'holded-search-products', + 'endpoint' => '/api/invoicing/v1/products', + 'method' => 'GET', + ) + ) + ->submit(); + + if ( is_wp_error( $response ) ) { + return $response; + } + + foreach ( $payload['line_items'] as $line_item ) { + $product = null; + foreach ( $response['data'] as $candidate ) { + if ( $candidate['sku'] === $line_item['product']['sku'] ) { + $product = $candidate; + break; + } + } + + if ( ! $product ) { + $product_response = $bridge + ->patch( + array( + 'name' => 'holded-sync-product-by-sku', + 'endpoint' => '/api/invoicing/v1/products', + 'method' => 'POST', + ) + ) + ->submit( + array( + 'kind' => 'simple', + 'name' => $line_item['product']['name'], + 'price' => $line_item['product']['price'], + 'sku' => $line_item['product']['sku'], + ) + ); + + if ( is_wp_error( $product_response ) ) { + return $product_response; + } + } + } + return $payload; +} diff --git a/forms-bridge/addons/holded/templates/appointments.php b/forms-bridge/addons/holded/templates/appointments.php new file mode 100644 index 00000000..5f882f6e --- /dev/null +++ b/forms-bridge/addons/holded/templates/appointments.php @@ -0,0 +1,433 @@ + __( 'Appointments', 'forms-bridge' ), + 'description' => __( + 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Appointments', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/crm/v1/events', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'event_name', + 'label' => __( 'Event name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => 'Web appointment', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'kind', + 'label' => __( 'Event type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'value' => 'meeting', + 'label' => __( 'Meeting', 'forms-bridge' ), + ), + array( + 'value' => 'call', + 'label' => __( 'Call', 'forms-bridge' ), + ), + array( + 'value' => 'lunch', + 'label' => __( 'Lunch', 'forms-bridge' ), + ), + array( + 'value' => 'dinner', + 'label' => __( 'Dinner', 'forms-bridge' ), + ), + ), + 'required' => true, + 'default' => 'meeting', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'duration', + 'label' => __( 'Duration (Hours)', 'forms-bridge' ), + 'type' => 'number', + 'default' => 1, + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'type', + 'label' => __( 'Contact type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Unspecified', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'Client', 'forms-bridge' ), + 'value' => 'client', + ), + array( + 'label' => __( 'Lead', 'forms-bridge' ), + 'value' => 'lead', + ), + array( + 'label' => __( 'Supplier', 'forms-bridge' ), + 'value' => 'supplier', + ), + array( + 'label' => __( 'Debtor', 'forms-bridge' ), + 'value' => 'debtor', + ), + array( + 'label' => __( 'Creditor', 'forms-bridge' ), + 'value' => 'creditor', + ), + ), + 'required' => true, + 'default' => '0', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/crm/v1/events', + 'custom_fields' => array( + array( + 'name' => 'isperson', + 'value' => '1', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'isperson', + 'to' => 'isperson', + 'cast' => 'integer', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => '?tags', + 'to' => 'event_tags', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'datetime', + 'to' => 'date', + 'cast' => 'string', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'country', + 'cast' => 'null', + ), + array( + 'from' => 'country_code', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'event_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?event_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( + 'date-fields-to-date', + 'appointment-dates', + 'iso2-country-code', + 'prefix-vatnumber', + 'contact-id', + ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'name' => 'date', + 'label' => __( 'Date', 'forms-bridge' ), + 'type' => 'date', + 'required' => true, + ), + array( + 'name' => 'hour', + 'label' => __( 'Hour', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => __( '1 AM', 'forms-bridge' ), + 'value' => '01', + ), + array( + 'label' => __( '2 AM', 'forms-bridge' ), + 'value' => '02', + ), + array( + 'label' => __( '3 AM', 'forms-bridge' ), + 'value' => '03', + ), + array( + 'label' => __( '4 AM', 'forms-bridge' ), + 'value' => '04', + ), + array( + 'label' => __( '5 AM', 'forms-bridge' ), + 'value' => '05', + ), + array( + 'label' => __( '6 AM', 'forms-bridge' ), + 'value' => '06', + ), + array( + 'label' => __( '7 AM', 'forms-bridge' ), + 'value' => '07', + ), + array( + 'label' => __( '8 AM', 'forms-bridge' ), + 'value' => '08', + ), + array( + 'label' => __( '9 AM', 'forms-bridge' ), + 'value' => '09', + ), + array( + 'label' => __( '10 AM', 'forms-bridge' ), + 'value' => '10', + ), + array( + 'label' => __( '11 AM', 'forms-bridge' ), + 'value' => '11', + ), + array( + 'label' => __( '12 AM', 'forms-bridge' ), + 'value' => '12', + ), + array( + 'label' => __( '1 PM', 'forms-bridge' ), + 'value' => '13', + ), + array( + 'label' => __( '2 PM', 'forms-bridge' ), + 'value' => '14', + ), + array( + 'label' => __( '3 PM', 'forms-bridge' ), + 'value' => '15', + ), + array( + 'label' => __( '4 PM', 'forms-bridge' ), + 'value' => '16', + ), + array( + 'label' => __( '5 PM', 'forms-bridge' ), + 'value' => '17', + ), + array( + 'label' => __( '6 PM', 'forms-bridge' ), + 'value' => '18', + ), + array( + 'label' => __( '7 PM', 'forms-bridge' ), + 'value' => '19', + ), + array( + 'label' => __( '8 PM', 'forms-bridge' ), + 'value' => '20', + ), + array( + 'label' => __( '9 PM', 'forms-bridge' ), + 'value' => '21', + ), + array( + 'label' => __( '10 PM', 'forms-bridge' ), + 'value' => '22', + ), + array( + 'label' => __( '11 PM', 'forms-bridge' ), + 'value' => '23', + ), + array( + 'label' => __( '12 PM', 'forms-bridge' ), + 'value' => '24', + ), + ), + ), + array( + 'name' => 'minute', + 'label' => __( 'Minute', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => '00', + 'value' => '00.0', + ), + array( + 'label' => '05', + 'value' => '05', + ), + array( + 'label' => '10', + 'value' => '10', + ), + array( + 'label' => '15', + 'value' => '15', + ), + array( + 'label' => '20', + 'value' => '20', + ), + array( + 'label' => '25', + 'value' => '25', + ), + array( + 'label' => '30', + 'value' => '30', + ), + array( + 'label' => '35', + 'value' => '35', + ), + array( + 'label' => '40', + 'value' => '40', + ), + array( + 'label' => '45', + 'value' => '45', + ), + array( + 'label' => '50', + 'value' => '50', + ), + array( + 'label' => '55', + 'value' => '55', + ), + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/company-contacts.php b/forms-bridge/addons/holded/templates/company-contacts.php new file mode 100644 index 00000000..81437061 --- /dev/null +++ b/forms-bridge/addons/holded/templates/company-contacts.php @@ -0,0 +1,206 @@ + __( 'Company Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form for companies template. The resulting bridge will convert form submissions into new companies linked to contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Contacts', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/contacts', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'type', + 'label' => __( 'Contact type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Unspecified', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'Client', 'forms-bridge' ), + 'value' => 'client', + ), + array( + 'label' => __( 'Lead', 'forms-bridge' ), + 'value' => 'lead', + ), + array( + 'label' => __( 'Supplier', 'forms-bridge' ), + 'value' => 'supplier', + ), + array( + 'label' => __( 'Debtor', 'forms-bridge' ), + 'value' => 'debtor', + ), + array( + 'label' => __( 'Creditor', 'forms-bridge' ), + 'value' => 'creditor', + ), + ), + 'required' => true, + 'default' => '0', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/contacts', + 'custom_fields' => array( + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => 'contact_name', + 'to' => 'contactPersons[0].name', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'contactPersons[0].email', + 'cast' => 'copy', + ), + array( + 'from' => 'phone', + 'to' => 'contactPersons[0].phone', + 'cast' => 'copy', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( + 'skip-if-contact-exists', + 'iso2-country-code', + 'prefix-vatnumber', + ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Company', 'forms-bridge' ), + 'name' => 'company_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/company-leads.php b/forms-bridge/addons/holded/templates/company-leads.php new file mode 100644 index 00000000..a7e95abe --- /dev/null +++ b/forms-bridge/addons/holded/templates/company-leads.php @@ -0,0 +1,217 @@ + __( 'Company Leads', 'forms-bridge' ), + 'description' => __( + 'Lead form template. The resulting bridge will convert form submissions into leads linked to new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Leads', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/crm/v1/leads', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'funnelId', + 'label' => __( 'Funnel', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/crm/v1/funnels', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'value', + 'label' => __( 'Lead value', 'forms-bridge' ), + 'description' => __( + 'Estimated deal value in currency units', + 'forms-bridge' + ), + 'type' => 'number', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'potential', + 'description' => __( + 'Deal potential as a percentage', + 'forms-bridge' + ), + 'label' => __( 'Lead potential (%)', 'forms-bridge' ), + 'type' => 'number', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Contact tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/crm/v1/leads', + 'custom_fields' => array( + array( + 'name' => 'type', + 'value' => 'lead', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => 'contact_name', + 'to' => 'contactPersons[0].name', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'contactPersons[0].email', + 'cast' => 'copy', + ), + array( + 'from' => 'phone', + 'to' => 'contactPersons[0].phone', + 'cast' => 'copy', + ), + array( + 'from' => '?tags', + 'to' => 'lead_tags', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => '?lead_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'prefix-vatnumber', 'contact-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Company', 'forms-bridge' ), + 'name' => 'company_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/contacts.php b/forms-bridge/addons/holded/templates/contacts.php new file mode 100644 index 00000000..d71bd6f4 --- /dev/null +++ b/forms-bridge/addons/holded/templates/contacts.php @@ -0,0 +1,194 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Contacts', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/contacts', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'type', + 'label' => __( 'Contact type', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Unspecified', 'forms-bridge' ), + 'value' => '0', + ), + array( + 'label' => __( 'Client', 'forms-bridge' ), + 'value' => 'client', + ), + array( + 'label' => __( 'Lead', 'forms-bridge' ), + 'value' => 'lead', + ), + array( + 'label' => __( 'Supplier', 'forms-bridge' ), + 'value' => 'supplier', + ), + array( + 'label' => __( 'Debtor', 'forms-bridge' ), + 'value' => 'debtor', + ), + array( + 'label' => __( 'Creditor', 'forms-bridge' ), + 'value' => 'creditor', + ), + ), + 'required' => true, + 'default' => '0', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/contacts', + 'custom_fields' => array( + array( + 'name' => 'isperson', + 'value' => '1', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'isperson', + 'to' => 'isperson', + 'cast' => 'integer', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + ), + array(), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( + 'skip-if-contact-exists', + 'iso2-country-code', + 'prefix-vatnumber', + ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/leads.php b/forms-bridge/addons/holded/templates/leads.php new file mode 100644 index 00000000..ad077e46 --- /dev/null +++ b/forms-bridge/addons/holded/templates/leads.php @@ -0,0 +1,207 @@ + __( 'Leads', 'forms-bridge' ), + 'description' => __( + 'Lead form template. The resulting bridge will convert form submissions into leads linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Leads', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/crm/v1/leads', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'funnelId', + 'label' => __( 'Funnel', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/crm/v1/funnels', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'value', + 'label' => __( 'Lead value', 'forms-bridge' ), + 'description' => __( + 'Estimated deal value in currency units', + 'forms-bridge' + ), + 'type' => 'number', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'potential', + 'description' => __( + 'Deal potential as a percentage', + 'forms-bridge' + ), + 'label' => __( 'Lead potential (%)', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + 'max' => 100, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Contact tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/crm/v1/leads', + 'custom_fields' => array( + array( + 'name' => 'isperson', + 'value' => '1', + ), + array( + 'name' => 'type', + 'value' => 'lead', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'isperson', + 'to' => 'isperson', + 'cast' => 'integer', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => '?tags', + 'to' => 'lead_tags', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => '?lead_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'prefix-vatnumber', 'contact-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/product-company-quotations.php b/forms-bridge/addons/holded/templates/product-company-quotations.php new file mode 100644 index 00000000..44748346 --- /dev/null +++ b/forms-bridge/addons/holded/templates/product-company-quotations.php @@ -0,0 +1,222 @@ + __( 'Product Company Quotations', 'forms-bridge' ), + 'description' => __( + 'Product quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Product Company Quotations', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/documents/estimate', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'sku', + 'label' => __( 'Product', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/invoicing/v1/products', + 'finger' => array( + 'value' => '[].sku', + 'label' => '[].name', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/documents/estimate', + 'custom_fields' => array( + array( + 'name' => 'type', + 'value' => 'client', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => '?tags', + 'to' => 'quotation_tags', + 'cast' => 'inherit', + ), + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => 'sku', + 'to' => 'items[0].sku', + 'cast' => 'string', + ), + array( + 'from' => 'quantity', + 'to' => 'items[0].units', + 'cast' => 'integer', + ), + array( + 'from' => 'contact_name', + 'to' => 'contactPersons[0].name', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'contactPersons[0].email', + 'cast' => 'copy', + ), + array( + 'from' => 'phone', + 'to' => 'contactPersons[0].phone', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => '?quotation_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'prefix-vatnumber', 'contact-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Quantity', 'forms-bridge' ), + 'name' => 'quantity', + 'type' => 'number', + 'default' => 1, + 'min' => 0, + 'required' => true, + ), + array( + 'label' => __( 'Company', 'forms-bridge' ), + 'name' => 'company_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/product-quotations.php b/forms-bridge/addons/holded/templates/product-quotations.php new file mode 100644 index 00000000..a772eff9 --- /dev/null +++ b/forms-bridge/addons/holded/templates/product-quotations.php @@ -0,0 +1,207 @@ + __( 'Product Quotations', 'forms-bridge' ), + 'description' => __( + 'Product quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Product Quotations', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/documents/estimate', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'sku', + 'label' => __( 'Product', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/invoicing/v1/products', + 'finger' => array( + 'value' => '[].sku', + 'label' => '[].name', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/documents/estimate', + 'custom_fields' => array( + array( + 'name' => 'type', + 'value' => 'client', + ), + array( + 'name' => 'isperson', + 'value' => '1', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'isperson', + 'to' => 'isperson', + 'cast' => 'integer', + ), + array( + 'from' => '?tags', + 'to' => 'quotation_tags', + 'cast' => 'inherit', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => 'sku', + 'to' => 'items[0].sku', + 'cast' => 'string', + ), + array( + 'from' => 'quantity', + 'to' => 'items[0].units', + 'cast' => 'integer', + ), + ), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => '?quotation_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'prefix-vatnumber', 'contact-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Quantity', 'forms-bridge' ), + 'name' => 'quantity', + 'type' => 'number', + 'default' => 1, + 'min' => 0, + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/service-company-quotations.php b/forms-bridge/addons/holded/templates/service-company-quotations.php new file mode 100644 index 00000000..a4086ca2 --- /dev/null +++ b/forms-bridge/addons/holded/templates/service-company-quotations.php @@ -0,0 +1,222 @@ + __( 'Service Company Quotations', 'forms-bridge' ), + 'description' => __( + 'Service quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Service Company Quotations', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/documents/estimate', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'serviceId', + 'label' => __( 'Service', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/invoicing/v1/services', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].name', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/documents/estimate', + 'custom_fields' => array( + array( + 'name' => 'type', + 'value' => 'client', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => '?tags', + 'to' => 'quotation_tags', + 'cast' => 'inherit', + ), + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => 'serviceId', + 'to' => 'items[0].serviceId', + 'cast' => 'string', + ), + array( + 'from' => 'quantity', + 'to' => 'items[0].units', + 'cast' => 'integer', + ), + array( + 'from' => 'contact_name', + 'to' => 'contactPersons[0].name', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'contactPersons[0].email', + 'cast' => 'copy', + ), + array( + 'from' => 'phone', + 'to' => 'contactPersons[0].phone', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => '?quotation_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'prefix-vatnumber', 'contact-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Quantity', 'forms-bridge' ), + 'name' => 'quantity', + 'type' => 'number', + 'default' => 1, + 'min' => 0, + 'required' => true, + ), + array( + 'label' => __( 'Company', 'forms-bridge' ), + 'name' => 'company_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/service-quotations.php b/forms-bridge/addons/holded/templates/service-quotations.php new file mode 100644 index 00000000..3ac09ab1 --- /dev/null +++ b/forms-bridge/addons/holded/templates/service-quotations.php @@ -0,0 +1,207 @@ + __( 'Service Quotations', 'forms-bridge' ), + 'description' => __( + 'Service quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Service Quotations', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/documents/estimate', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'serviceId', + 'label' => __( 'Service', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/invoicing/v1/services', + 'finger' => array( + 'value' => '[].id', + 'label' => '[].name', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/documents/estimate', + 'custom_fields' => array( + array( + 'name' => 'type', + 'value' => 'client', + ), + array( + 'name' => 'isperson', + 'value' => '1', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'isperson', + 'to' => 'isperson', + 'cast' => 'integer', + ), + array( + 'from' => '?tags', + 'to' => 'quotation_tags', + 'cast' => 'inherit', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'code', + 'to' => 'vatnumber', + 'cast' => 'copy', + ), + array( + 'from' => 'address', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => 'postalCode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => 'city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => 'serviceId', + 'to' => 'items[0].serviceId', + 'cast' => 'string', + ), + array( + 'from' => 'quantity', + 'to' => 'items[0].units', + 'cast' => 'integer', + ), + ), + array( + array( + 'from' => 'country', + 'to' => 'countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'countryCode', + 'to' => 'billAddress.countryCode', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => '?quotation_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'prefix-vatnumber', 'contact-id' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Quantity', 'forms-bridge' ), + 'name' => 'quantity', + 'type' => 'number', + 'default' => 1, + 'min' => 0, + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'code', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'address', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'postalCode', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/holded/templates/woo-product-orders-sync.php b/forms-bridge/addons/holded/templates/woo-product-orders-sync.php new file mode 100644 index 00000000..4bbc7425 --- /dev/null +++ b/forms-bridge/addons/holded/templates/woo-product-orders-sync.php @@ -0,0 +1,378 @@ + __( 'Product Orders + Sync', 'forms-bridge' ), + 'description' => __( + 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Holded by product SKUs.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/documents/salesorder', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/documents/salesorder', + 'custom_fields' => array( + array( + 'name' => 'type', + 'value' => 'client', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + array( + 'name' => 'approveDoc', + 'value' => '1', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'approveDoc', + 'to' => 'approveDoc', + 'cast' => 'boolean', + ), + array( + 'from' => 'id', + 'to' => 'customFields.wp_order_id', + 'cast' => 'integer', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'CustomId', + 'cast' => 'string', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => '?billing.state', + 'to' => 'billAddress.province', + 'cast' => 'string', + ), + array( + 'from' => '?billing.country', + 'to' => 'billAddress.country', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'shippingAddresses[0].address', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'shippingAddresses[0].postalCode', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'shippingAddresses[0].city', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.state', + 'to' => 'shippingAddresses[0].province', + 'cast' => 'string', + ), + array( + 'from' => 'shipping.country', + 'to' => 'shippingAddresses[0].country', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'notes', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].name', + 'to' => 'items[].name', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'items[].subtotal', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].total_tax.percentage', + 'to' => 'items[].tax', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'items[].units', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'items[].sku', + 'cast' => 'copy', + ), + array( + 'from' => '?tags', + 'to' => 'order_tags', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => '?order_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'sync-products-by-sku', 'contact-id' ), + ), +); diff --git a/forms-bridge/addons/holded/templates/woo-product-orders.php b/forms-bridge/addons/holded/templates/woo-product-orders.php new file mode 100644 index 00000000..62e1a153 --- /dev/null +++ b/forms-bridge/addons/holded/templates/woo-product-orders.php @@ -0,0 +1,376 @@ + __( 'Product Orders', 'forms-bridge' ), + 'description' => __( + 'Product sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. To work propertly, the bridge needs that your WooCommerce product sku values matches with the holded ones.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/invoicing/v1/documents/salesorder', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Tags', 'forms-bridge' ), + 'description' => __( 'Tags separated by commas', 'forms-bridge' ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'endpoint' => '/api/invoicing/v1/documents/salesorder', + 'custom_fields' => array( + array( + 'name' => 'type', + 'value' => 'client', + ), + array( + 'name' => 'defaults.language', + 'value' => '$locale', + ), + array( + 'name' => 'approveDoc', + 'value' => '1', + ), + array( + 'name' => 'date', + 'value' => '$timestamp', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'approveDoc', + 'to' => 'approveDoc', + 'cast' => 'boolean', + ), + array( + 'from' => 'id', + 'to' => 'customFields.wp_order_id', + 'cast' => 'integer', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'CustomId', + 'cast' => 'string', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'billAddress.address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'billAddress.city', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'billAddress.postalCode', + 'cast' => 'string', + ), + array( + 'from' => '?billing.state', + 'to' => 'billAddress.province', + 'cast' => 'string', + ), + array( + 'from' => '?billing.country', + 'to' => 'billAddress.country', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'shippingAddresses[0].address', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'shippingAddresses[0].postalCode', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'shippingAddresses[0].city', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.state', + 'to' => 'shippingAddresses[0].province', + 'cast' => 'string', + ), + array( + 'from' => 'shipping.country', + 'to' => 'shippingAddresses[0].country', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'notes', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].name', + 'to' => 'items[].name', + 'cast' => 'string', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'items[].subtotal', + 'cast' => 'number', + ), + array( + 'from' => 'line_items[].total_tax.percentage', + 'to' => 'items[].tax', + 'cast' => 'number', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'items[].units', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'items[].sku', + 'cast' => 'string', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => '?tags', + 'to' => 'order_tags', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => '?order_tags', + 'to' => 'tags', + 'cast' => 'inherit', + ), + ), + ), + 'workflow' => array( 'contact-id' ), + ), +); diff --git a/addons/listmonk/assets/logo.png b/forms-bridge/addons/listmonk/assets/logo.png similarity index 100% rename from addons/listmonk/assets/logo.png rename to forms-bridge/addons/listmonk/assets/logo.png diff --git a/forms-bridge/addons/listmonk/class-listmonk-addon.php b/forms-bridge/addons/listmonk/class-listmonk-addon.php new file mode 100644 index 00000000..ecf26105 --- /dev/null +++ b/forms-bridge/addons/listmonk/class-listmonk-addon.php @@ -0,0 +1,135 @@ + '__listmonk-' . time(), + 'endpoint' => '/api/lists', + 'method' => 'GET', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit(); + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge = new Listmonk_Form_Bridge( + array( + 'name' => '__listmonk-' . time(), + 'method' => 'GET', + 'endpoint' => $endpoint, + 'backend' => $backend, + ) + ); + + return $bridge->submit(); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array + */ + public function get_endpoint_schema( $endpoint, $backend ) { + if ( '/api/subscribers' === $endpoint ) { + return array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'status', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'lists', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'number' ), + ), + ), + array( + 'name' => 'preconfirm_subscriptions', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'attribs', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + ), + ); + } + + return array(); + } +} + +Listmonk_Addon::setup(); diff --git a/forms-bridge/addons/listmonk/class-listmonk-form-bridge.php b/forms-bridge/addons/listmonk/class-listmonk-form-bridge.php new file mode 100644 index 00000000..95aeab68 --- /dev/null +++ b/forms-bridge/addons/listmonk/class-listmonk-form-bridge.php @@ -0,0 +1,75 @@ +get_error_data()['response'] ?? null; + + $code = $error_response['response']['code'] ?? null; + if ( $code !== 409 ) { + return $response; + } + + if ( + ! isset( $payload['email'] ) || + $this->endpoint !== '/api/subscribers' + ) { + return $response; + } + + $get_response = $this->patch( + array( + 'name' => 'listmonk-get-subscriber-by-email', + 'method' => 'GET', + ) + )->submit( + array( + 'per_page' => '1', + 'query' => "subscribers.email = '{$payload['email']}'", + ) + ); + + if ( is_wp_error( $get_response ) ) { + return $response; + } + + $subscriber_id = $get_response['data']['data']['results'][0]['id']; + + return $this->patch( + array( + 'name' => 'listmonk-update-subscriber', + 'method' => 'PUT', + 'endpoint' => $this->endpoint . '/' . $subscriber_id, + ) + )->submit( $payload ); + } + + return $response; + } +} diff --git a/forms-bridge/addons/listmonk/hooks.php b/forms-bridge/addons/listmonk/hooks.php new file mode 100644 index 00000000..e798c336 --- /dev/null +++ b/forms-bridge/addons/listmonk/hooks.php @@ -0,0 +1,148 @@ + array( + array( + 'ref' => '#backend', + 'name' => 'name', + 'description' => __( + 'Label of the Listmonk API backend connection', + 'forms-bridge' + ), + 'default' => 'Listmonk API', + ), + array( + 'ref' => '#credential', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'schema', + 'type' => 'text', + 'value' => 'Token', + ), + array( + 'ref' => '#credential', + 'name' => 'client_id', + 'label' => __( 'API user', 'forms-bridge' ), + 'description' => __( + 'You have to generate an API user on your listmonk instance. See the documentation for more information', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_secret', + 'label' => __( 'API token', 'forms-bridge' ), + 'description' => __( + 'Token of the API user. The token will be shown only once on user creation time, be sure to copy its value and store it in a save place', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'lists', + 'label' => __( 'Mailing lists', 'forms-bridge' ), + 'description' => __( + 'Select, at least, one list that users will subscribe to', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/api/lists', + 'finger' => array( + 'value' => 'data.results[].id', + 'label' => 'data.results[].name', + ), + ), + 'is_multi' => true, + 'required' => true, + ), + ), + 'bridge' => array( + 'backend' => 'Listmonk API', + 'endpoint' => '', + 'method' => 'POST', + ), + 'backend' => array( + 'name' => 'Listmonk', + ), + 'credential' => array( + 'name' => '', + 'schema' => 'Token', + 'client_id' => '', + 'client_secret' => '', + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'listmonk-' ) !== 0 ) { + return $data; + } + + $index = array_search( + 'lists', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = &$data['bridge']['custom_fields'][ $index ]; + if ( is_array( $field['value'] ) ) { + for ( $i = 0; $i < count( $field['value'] ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "lists[{$i}]", + 'value' => (int) $field['value'][ $i ], + ); + + $data['bridge']['mutations'][0][] = array( + 'from' => "lists[{$i}]", + 'to' => "lists[{$i}]", + 'cast' => 'integer', + ); + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + $data['bridge']['custom_fields'] = array_values( + $data['bridge']['custom_fields'] + ); + } + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/listmonk/jobs/skip-subscription.php b/forms-bridge/addons/listmonk/jobs/skip-subscription.php new file mode 100644 index 00000000..43e998c5 --- /dev/null +++ b/forms-bridge/addons/listmonk/jobs/skip-subscription.php @@ -0,0 +1,30 @@ + __( 'Skip subscription', 'forms-bridge' ), + 'description' => __( + 'Skip subscription if the listmonk field is not true', + 'forms-bridge' + ), + 'method' => 'forms_bridge_listmonk_skip_subscription', + 'input' => array( + array( + 'name' => 'listmonk', + 'schema' => array( 'type' => 'boolean' ), + 'required' => true, + ), + ), + 'output' => array(), +); + +function forms_bridge_listmonk_skip_subscription( $payload ) { + if ( $payload['listmonk'] != true ) { + return; + } + + return $payload; +} diff --git a/forms-bridge/addons/listmonk/templates/optin-subscriptions.php b/forms-bridge/addons/listmonk/templates/optin-subscriptions.php new file mode 100644 index 00000000..c963e531 --- /dev/null +++ b/forms-bridge/addons/listmonk/templates/optin-subscriptions.php @@ -0,0 +1,103 @@ + __( 'Opt-in Subscriptions', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions with a double opt-in confirmation check.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/subscribers', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'status', + 'label' => __( 'Subscription status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Enabled', 'forms-bridge' ), + 'value' => 'enabled', + ), + array( + 'label' => __( 'Disabled', 'forms-bridge' ), + 'value' => 'blocklisted', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Opt-in subscriptions', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Opt-in subscriptions', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'your-email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'your-name', + 'label' => __( 'Your name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'backend' => array( + 'headers' => array( + array( + 'name' => 'Content-Type', + 'value' => 'application/json', + ), + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/api/subscribers', + 'custom_fields' => array( + array( + 'name' => 'attribs.locale', + 'value' => '$locale', + ), + array( + 'name' => 'preconfirm_subscriptions', + 'value' => '0', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'your-email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'preconfirm_subscriptions', + 'to' => 'preconfirm_subscriptions', + 'cast' => 'boolean', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/listmonk/templates/subscriptions.php b/forms-bridge/addons/listmonk/templates/subscriptions.php new file mode 100644 index 00000000..ac082e09 --- /dev/null +++ b/forms-bridge/addons/listmonk/templates/subscriptions.php @@ -0,0 +1,108 @@ + __( 'Subscriptions', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/subscribers', + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'status', + 'label' => __( 'Subscription status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Enabled', 'forms-bridge' ), + 'value' => 'enabled', + ), + array( + 'label' => __( 'Disabled', 'forms-bridge' ), + 'value' => 'blocklisted', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Subscriptions', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Subscriptions', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'your-email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'your-name', + 'label' => __( 'Your name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'backend' => array( + 'headers' => array( + array( + 'name' => 'Content-Type', + 'value' => 'application/json', + ), + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/api/subscribers', + 'custom_fields' => array( + array( + 'name' => 'attribs.locale', + 'value' => '$locale', + ), + array( + 'name' => 'preconfirm_subscriptions', + 'value' => '1', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'your-email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'preconfirm_subscriptions', + 'to' => 'preconfirm_subscriptions', + 'cast' => 'boolean', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/listmonk/templates/woo-optin-subscriptions.php b/forms-bridge/addons/listmonk/templates/woo-optin-subscriptions.php new file mode 100644 index 00000000..71165eb9 --- /dev/null +++ b/forms-bridge/addons/listmonk/templates/woo-optin-subscriptions.php @@ -0,0 +1,296 @@ + __( 'Opt-in Subscriptions', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list with a double opt in check.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/subscribers', + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'status', + 'label' => __( 'Subscription status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Enabled', 'forms-bridge' ), + 'value' => 'enabled', + ), + array( + 'label' => __( 'Disabled', 'forms-bridge' ), + 'value' => 'blocklisted', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/api/subscribers', + 'custom_fields' => array( + array( + 'name' => 'attribs.locale', + 'value' => '$locale', + ), + array( + 'name' => 'preconfirm_subscriptions', + 'value' => '0', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'preconfirm_subscriptions', + 'to' => 'preconfirm_subscriptions', + 'cast' => 'boolean', + ), + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'attribs.wp_customer_id', + 'cast' => 'string', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => 'billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/listmonk/templates/woo-subscriptions.php b/forms-bridge/addons/listmonk/templates/woo-subscriptions.php new file mode 100644 index 00000000..0fb49349 --- /dev/null +++ b/forms-bridge/addons/listmonk/templates/woo-subscriptions.php @@ -0,0 +1,296 @@ + __( 'Subscriptions', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given email list.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/api/subscribers', + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'status', + 'label' => __( 'Subscription status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Enabled', 'forms-bridge' ), + 'value' => 'enabled', + ), + array( + 'label' => __( 'Disabled', 'forms-bridge' ), + 'value' => 'blocklisted', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/api/subscribers', + 'custom_fields' => array( + array( + 'name' => 'attribs.locale', + 'value' => '$locale', + ), + array( + 'name' => 'preconfirm_subscriptions', + 'value' => '1', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'preconfirm_subscriptions', + 'to' => 'preconfirm_subscriptions', + 'cast' => 'boolean', + ), + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'attribs.wp_customer_id', + 'cast' => 'string', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => 'billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + ), + ), +); diff --git a/addons/mailchimp/assets/logo.png b/forms-bridge/addons/mailchimp/assets/logo.png similarity index 100% rename from addons/mailchimp/assets/logo.png rename to forms-bridge/addons/mailchimp/assets/logo.png diff --git a/forms-bridge/addons/mailchimp/class-mailchimp-addon.php b/forms-bridge/addons/mailchimp/class-mailchimp-addon.php new file mode 100644 index 00000000..1ef380f8 --- /dev/null +++ b/forms-bridge/addons/mailchimp/class-mailchimp-addon.php @@ -0,0 +1,209 @@ + '__mailchimp-' . time(), + 'endpoint' => '/3.0/lists', + 'method' => 'GET', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit(); + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge = new Mailchimp_Form_Bridge( + array( + 'name' => '__mailchimp-' . time(), + 'method' => 'GET', + 'endpoint' => $endpoint, + 'backend' => $backend, + ) + ); + + return $bridge->submit(); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array + */ + public function get_endpoint_schema( $endpoint, $backend ) { + if ( strstr( $endpoint, '/lists/' ) !== false ) { + $fields = array( + array( + 'name' => 'email_address', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'status', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'email_type', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'interests', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + ), + array( + 'name' => 'language', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vip', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'location', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'latitude' => array( 'type' => 'number' ), + 'longitude' => array( 'type' => 'number' ), + ), + ), + ), + array( + 'name' => 'marketing_permissions', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'marketing_permission_id' => array( + 'type' => 'string', + ), + 'enabled' => array( 'type' => 'boolean' ), + ), + ), + ), + ), + array( + 'name' => 'ip_signup', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'ip_opt', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'timestamp_opt', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'tags', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + ), + ), + array( + 'name' => 'merge_fields', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + ), + ), + ); + + $fields_endpoint = str_replace( + '/members', + '/merge-fields', + $endpoint + ); + + $bridge = new Mailchimp_Form_Bridge( + array( + 'name' => '__mailchimp-' . time(), + 'endpoint' => $fields_endpoint, + 'method' => 'GET', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit(); + + if ( is_wp_error( $response ) ) { + return array(); + } + + foreach ( $response['data']['merge_fields'] as $field ) { + $fields[] = array( + 'name' => 'merge_fields.' . $field['tag'], + 'schema' => array( 'type' => 'string' ), + ); + } + + return $fields; + } + + return array(); + } +} + +Mailchimp_Addon::setup(); diff --git a/forms-bridge/addons/mailchimp/class-mailchimp-form-bridge.php b/forms-bridge/addons/mailchimp/class-mailchimp-form-bridge.php new file mode 100644 index 00000000..4fd4f423 --- /dev/null +++ b/forms-bridge/addons/mailchimp/class-mailchimp-form-bridge.php @@ -0,0 +1,109 @@ +get_error_data()['response'] ?? null; + + $code = $error_response['response']['code'] ?? null; + if ( $code !== 400 ) { + return $response; + } + + try { + $body = json_decode( $error_response['body'] ?? '', true ); + $title = $body['title'] ?? null; + } catch ( TypeError ) { + return $response; + } + + if ( $title === 'Member Exists' ) { + if ( + ! preg_match( + '/(?<=lists\/).+(?=\/members)/', + $this->endpoint, + $matches + ) + ) { + return $response; + } + + $list_id = $matches[0]; + + $search_response = $this->patch( + array( + 'name' => 'mailchimp-search-member', + 'method' => 'GET', + 'endpoint' => '/3.0/search-members', + ) + )->submit( + array( + 'fiels' => 'exact_matches.members.id', + 'list_id' => $list_id, + 'query' => $payload['email_address'], + ) + ); + + if ( is_wp_error( $search_response ) ) { + return $response; + } + + $member_id = + $search_response['data']['exact_matches']['members'][0]['id'] ?? null; + + if ( ! $member_id ) { + return $response; + } + + $update_endpoint = "/3.0/lists/{$list_id}/members/{$member_id}"; + if ( + strstr( $this->endpoint, 'skip_merge_validation' ) !== false + ) { + $update_endpoint .= '?skip_merge_validation=true'; + } + + $update_response = $this->patch( + array( + 'name' => 'mailchimp-update-subscription', + 'method' => 'PUT', + 'endpoint' => $update_endpoint, + ) + )->submit( $payload ); + + if ( is_wp_error( $update_response ) ) { + return $response; + } + + return $update_response; + } + } + + return $response; + } +} diff --git a/forms-bridge/addons/mailchimp/hooks.php b/forms-bridge/addons/mailchimp/hooks.php new file mode 100644 index 00000000..3567821a --- /dev/null +++ b/forms-bridge/addons/mailchimp/hooks.php @@ -0,0 +1,198 @@ + array( + array( + 'ref' => '#backend', + 'name' => 'name', + 'description' => __( + 'Label of the MailChimp API backend connection', + 'forms-bridge' + ), + 'default' => 'MailChimp API', + ), + array( + 'ref' => '#backend', + 'name' => 'base_url', + 'description' => __( + 'If needed, replace the datacenter param from the URL to match your account servers.', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array_map( + function ( $i ) { + return array( + 'value' => "https://us{$i}.api.mailchimp.com", + 'label' => "us{$i}.api.mailchimp.com", + ); + }, + array_keys( array_fill( 1, 12, null ) ) + ), + 'default' => 'https://us1.api.mailchimp.com', + ), + array( + 'ref' => '#credential', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'schema', + 'type' => 'text', + 'value' => 'Basic', + ), + array( + 'ref' => '#credential', + 'name' => 'client_id', + 'type' => 'text', + 'value' => 'forms-bridge', + ), + array( + 'ref' => '#credential', + 'name' => 'client_secret', + 'label' => __( 'API key', 'forms-bridge' ), + 'description' => __( + 'Get it from your account', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'list_id', + 'label' => __( 'Audience', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/3.0/lists', + 'finger' => array( + 'value' => 'lists[].id', + 'label' => 'lists[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'skip_merge_validation', + 'label' => __( + 'Skip merge fields validation', + 'forms-bridge' + ), + 'type' => 'boolean', + 'default' => false, + ), + ), + 'bridge' => array( + 'backend' => '', + 'endpoint' => '', + 'method' => 'POST', + ), + 'backend' => array( + 'name' => 'Mailchimp API', + 'base_url' => 'https://{dc}.api.mailchimp.com', + ), + 'credential' => array( + 'name' => '', + 'schema' => 'Basic', + 'client_id' => '', + 'client_secret' => '', + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'mailchimp-' ) !== 0 ) { + return $data; + } + + $index = array_search( + 'skip_merge_validation', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $endpoint = $data['bridge']['endpoint']; + $parsed = wp_parse_url( $endpoint ); + + $path = $parsed['path'] ?? ''; + + $query = array(); + wp_parse_str( $parsed['query'] ?? '', $query ); + $query['skip_merge_validation'] = 'true'; + $querystr = http_build_query( $query ); + + $data['bridge']['endpoint'] = $path . '?' . $querystr; + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'list_id', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $list_id = $data['bridge']['custom_fields'][ $index ]['value']; + $data['bridge']['endpoint'] = preg_replace( + '/\{list_id\}/', + $list_id, + $data['bridge']['endpoint'] + ); + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'tags', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = &$data['bridge']['custom_fields'][ $index ]; + + $tags = array_filter( + array_map( 'trim', explode( ',', strval( $field['value'] ) ) ) + ); + for ( $i = 0; $i < count( $tags ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "tags[{$i}]", + 'value' => $tags[ $i ], + ); + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/mailchimp/jobs/list-contact.php b/forms-bridge/addons/mailchimp/jobs/list-contact.php new file mode 100644 index 00000000..6c94f353 --- /dev/null +++ b/forms-bridge/addons/mailchimp/jobs/list-contact.php @@ -0,0 +1,146 @@ + __( 'Audience subscription', 'forms-bridge' ), + 'description' => __( 'Subscribe a new user to an audience', 'forms-bridge' ), + 'method' => 'forms_bridge_mailchimp_audience_subscription', + 'input' => array( + array( + 'name' => 'list_id', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email_address', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'status', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email_type', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'interests', + 'schema' => array( + 'type' => 'object', + 'properties' => array(), + 'additionalProperties' => true, + ), + ), + array( + 'name' => 'language', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vip', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'location', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'latitude' => array( 'type' => 'number' ), + 'longitude' => array( 'type' => 'number' ), + ), + 'additionalProperties' => false, + ), + ), + array( + 'name' => 'marketing_permissions', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'marketing_permission_id' => array( 'type' => 'string' ), + 'enabled' => array( 'type' => 'boolean' ), + ), + ), + 'additionalItems' => true, + ), + ), + array( + 'name' => 'ip_signup', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'timestamp_signup', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'ip_opt', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'timestamp_opt', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'tags', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'additionalItems' => true, + ), + ), + ), + 'output' => array( + array( + 'name' => 'email_address', + 'schema' => array( 'type' => 'string' ), + ), + ), +); + +function forms_bridge_mailchimp_audience_subscription( $payload, $bridge ) { + $contact = array( + 'email_address' => $payload['email_address'], + ); + + $contact_fields = array( + 'status', + 'email_type', + 'interests', + 'language', + 'vip', + 'location', + 'marketing_permissions', + 'ip_signup', + 'ip_opt', + 'timestamp_opt', + 'tags', + 'merge_fields', + ); + + foreach ( $contact_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $contact[ $field ] = $payload[ $field ]; + } + } + + $response = $bridge + ->patch( + array( + 'endpoint' => "/3.0/lists/{$payload['list_id']}/members", + 'method' => 'POST', + 'name' => 'mailchimp-subscribe-member-to-list', + ) + ) + ->submit( $contact ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $payload['email_address'] = $response['data']['email_address']; + return $payload; +} diff --git a/forms-bridge/addons/mailchimp/jobs/skip-subscription.php b/forms-bridge/addons/mailchimp/jobs/skip-subscription.php new file mode 100644 index 00000000..4ddbf914 --- /dev/null +++ b/forms-bridge/addons/mailchimp/jobs/skip-subscription.php @@ -0,0 +1,30 @@ + __( 'Skip subscription', 'forms-bridge' ), + 'description' => __( + 'Skip subscription if the mailchimp field is not true', + 'forms-bridge' + ), + 'method' => 'forms_bridge_mailchimp_skip_subscription', + 'input' => array( + array( + 'name' => 'mailchimp', + 'schema' => array( 'type' => 'boolean' ), + 'required' => true, + ), + ), + 'output' => array(), +); + +function forms_bridge_mailchimp_skip_subscription( $payload ) { + if ( $payload['mailchimp'] != true ) { + return; + } + + return $payload; +} diff --git a/forms-bridge/addons/mailchimp/templates/subscription.php b/forms-bridge/addons/mailchimp/templates/subscription.php new file mode 100644 index 00000000..f78fafbf --- /dev/null +++ b/forms-bridge/addons/mailchimp/templates/subscription.php @@ -0,0 +1,118 @@ + __( 'Subscription', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will convert form submissions into new list subscriptions.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/3.0/lists/{list_id}/members', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'status', + 'label' => __( 'Subscription status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Subscribed', 'forms-bridge' ), + 'value' => 'subscribed', + ), + array( + 'label' => __( 'Unsubscribed', 'forms-bridge' ), + 'value' => 'unsubscribed', + ), + array( + 'label' => __( 'Pending', 'forms-bridge' ), + 'value' => 'pending', + ), + array( + 'label' => __( 'Cleaned', 'forms-bridge' ), + 'value' => 'cleand', + ), + array( + 'label' => __( 'Transactional', 'forms-bridge' ), + 'value' => 'transactional', + ), + ), + 'default' => 'subscribed', + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Subscription tags', 'forms-bridge' ), + 'description' => __( + 'Tag names separated by commas', + 'forms-bridge' + ), + 'type' => 'text', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Subscription', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => __( 'Subscription', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'email_address', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'fname', + 'label' => __( 'Your first name', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'lname', + 'label' => __( 'Your last name', 'forms-bridge' ), + 'type' => 'text', + ), + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/3.0/lists/{list_id}/members', + 'custom_fields' => array( + array( + 'name' => 'language', + 'value' => '$locale', + ), + array( + 'name' => 'ip_signup', + 'value' => '$ip_address', + ), + array( + 'name' => 'timestamp_signup', + 'value' => '$iso_date', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'fname', + 'to' => 'merge_fields.FNAME', + 'cast' => 'string', + ), + array( + 'from' => 'lname', + 'to' => 'merge_fields.LNAME', + 'cast' => 'string', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/mailchimp/templates/woo-subscription.php b/forms-bridge/addons/mailchimp/templates/woo-subscription.php new file mode 100644 index 00000000..7b4f22b0 --- /dev/null +++ b/forms-bridge/addons/mailchimp/templates/woo-subscription.php @@ -0,0 +1,304 @@ + __( 'Subscription', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will subscribe woocommerce customers to a given mailchimp audience.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/3.0/lists/{list_id}/members', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'status', + 'label' => __( 'Subscription status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Subscribed', 'forms-bridge' ), + 'value' => 'subscribed', + ), + array( + 'label' => __( 'Unsubscribed', 'forms-bridge' ), + 'value' => 'unsubscribed', + ), + array( + 'label' => __( 'Pending', 'forms-bridge' ), + 'value' => 'pending', + ), + array( + 'label' => __( 'Cleaned', 'forms-bridge' ), + 'value' => 'cleand', + ), + array( + 'label' => __( 'Transactional', 'forms-bridge' ), + 'value' => 'transactional', + ), + ), + 'default' => 'subscribed', + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tags', + 'label' => __( 'Subscription tags', 'forms-bridge' ), + 'description' => __( + 'Tag names separated by commas', + 'forms-bridge' + ), + 'type' => 'text', + ), + ), + 'bridge' => array( + 'method' => 'POST', + 'endpoint' => '/3.0/lists/{list_id}/members', + 'custom_fields' => array( + array( + 'name' => 'language', + 'value' => '$locale', + ), + array( + 'name' => 'timestamp_signup', + 'value' => '$iso_date', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'merge_fields.FNAME', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'merge_fields.LNAME', + 'cast' => 'string', + ), + array( + 'from' => 'billing.email', + 'to' => 'email_address', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'string', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + ), + ), +); diff --git a/addons/nextcloud/assets/logo.png b/forms-bridge/addons/nextcloud/assets/logo.png similarity index 100% rename from addons/nextcloud/assets/logo.png rename to forms-bridge/addons/nextcloud/assets/logo.png diff --git a/forms-bridge/addons/nextcloud/class-nextcloud-addon.php b/forms-bridge/addons/nextcloud/class-nextcloud-addon.php new file mode 100644 index 00000000..d905a391 --- /dev/null +++ b/forms-bridge/addons/nextcloud/class-nextcloud-addon.php @@ -0,0 +1,145 @@ +addon ) { + return false; + } + + return $prune; + }, + 5, + 2 + ); + } + /** + * Performs a request against the backend to check the connexion status. + * + * @param string $backend Target backend name. + * + * @return boolean + */ + public function ping( $backend ) { + $backend = FBAPI::get_backend( $backend ); + + if ( ! $backend ) { + return false; + } + + $credential = $backend->credential; + if ( ! $credential ) { + return false; + } + + $response = $backend->get( + '/remote.php/dav/files/' . rawurlencode( $credential->client_id ) + ); + + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend model and retrive the response data. + * + * @param string $endpoint Target model name. + * @param string $backend Target backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + return array(); + } + + /** + * Performs an introspection of the backend model and returns API fields + * and accepted content type. + * + * @param string $filepath Filepath. + * @param string $backend Backend name. + * + * @return array List of fields and content type of the model. + */ + public function get_endpoint_schema( $filepath, $backend ) { + $bridge = new Nextcloud_Form_Bridge( + array( + 'name' => '__nextcloud-' . time(), + 'endpoint' => $filepath, + 'backend' => $backend, + ) + ); + + $headers = $bridge->table_headers(); + if ( is_wp_error( $headers ) || ! $headers ) { + return array(); + } + + $fields = array(); + foreach ( $headers as $header ) { + $fields[] = array( + 'name' => $header, + 'schema' => array( 'type' => 'string' ), + ); + } + + return $fields; + } +} + +Nextcloud_Addon::setup(); + +add_filter( + 'http_request_args', + function ( $args ) { + $args['timeout'] = 30; + return $args; + } +); diff --git a/forms-bridge/addons/nextcloud/class-nextcloud-form-bridge.php b/forms-bridge/addons/nextcloud/class-nextcloud-form-bridge.php new file mode 100644 index 00000000..f5245880 --- /dev/null +++ b/forms-bridge/addons/nextcloud/class-nextcloud-form-bridge.php @@ -0,0 +1,363 @@ +data['endpoint'] ); + $name = str_replace( '/', '-', $endpoint ); + $filepath = $uploads . '/' . $name; + + if ( ! is_file( $filepath ) ) { + $touched = true; + $result = touch( $filepath ); + + if ( ! $result ) { + return new WP_Error( 'file_permission_error' ); + } + } + + return $filepath; + } + + /** + * Returns the bridge table headers. + * + * @return array|null + */ + public function table_headers() { + $filepath = $this->filepath(); + + if ( is_wp_error( $filepath ) ) { + return $filepath; + } + + $stream = fopen( $filepath, 'r' ); + $line = fgets( $stream ); + fclose( $stream ); + + if ( false === $line ) { + return; + } + + return $this->decode_row( $line ); + } + + /** + * Returns the remote file modification date. + * + * @param Backend $backend Bridge backend instance. + * + * @return integer|null + */ + private function get_dav_modified_date( $backend ) { + $response = $backend->head( $this->endpoint ); + + if ( is_wp_error( $response ) ) { + $error_data = $response->get_error_data(); + + $code = $error_data['response']['response']['code'] ?? null; + if ( 404 !== $code ) { + return $response; + } + + return; + } + + $last_modified = $response['headers']['last-modified'] ?? null; + if ( ! $last_modified ) { + return; + } + + return strtotime( $last_modified ); + } + + /** + * Generates a heaaders csv row from a payload. + * + * @param array $payload Bridge payload. + * + * @return string + */ + private function payload_to_headers( $payload ) { + $payload = $this->flatten_payload( $payload ); + return $this->encode_row( array_keys( $payload ) ); + } + + /** + * Encode the payload as a csv row following the sheet headers columns order. + * + * @param array $payload Bridge payload. + * + * @return string + */ + private function payload_to_row( $payload ) { + $headers = $this->table_headers(); + if ( ! is_array( $headers ) ) { + $headers = array_keys( $payload ); + } + + $row = array(); + foreach ( $headers as $header ) { + $row[] = $payload[ $header ] ?? ''; + } + + return $this->encode_row( $row ); + } + + /** + * Returns a list of values as a comma separated values string. + * + * @param array $row List of values. + * + * @return string + */ + private function encode_row( $row ) { + return implode( + ',', + array_map( + fn( $value ) => json_encode( + $value, + JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE + ), + $row + ) + ); + } + + /** + * Returns a csv row as a list of values. + * + * @param string $row Comma separated values string. + * + * @return array + */ + private function decode_row( $row ) { + $row = preg_replace( '/\n+/', '', $row ); + return array_map( + function ( $value ) { + $decoded = json_decode( $value ); + if ( $decoded ) { + return $decoded; + } + + return $value; + }, + explode( ',', $row ) + ); + } + + /** + * Adds a row to the local sheet file. + * + * @param array $payload Bridge payload. + */ + private function add_row( $payload ) { + $row = $this->payload_to_row( $payload ); + + $filepath = $this->filepath(); + $sock = fopen( $filepath, 'r' ); + $cursor = -1; + fseek( $sock, $cursor, SEEK_END ); + $char = fgetc( $sock ); + fclose( $sock ); + + if ( "\n" !== $char && "\r" !== $char ) { + $row = "\n" . $row; + } + + file_put_contents( $filepath, $row, FILE_APPEND ); + } + + /** + * Submits submission to the backend. + * + * @param array $payload Submission data. + * @param array $attachments Submission attachments. + * + * @return array|WP_Error Http + */ + public function submit( $payload = array(), $attachments = array() ) { + if ( ! $this->is_valid ) { + return new WP_Error( 'invalid_bridge' ); + } + + $backend = $this->backend; + + if ( ! $backend ) { + return new WP_Error( 'invalid_bridge' ); + } + + $payload = self::flatten_payload( $payload ); + + add_filter( + 'http_bridge_backend_url', + function ( $url, $backend ) { + if ( $backend->name === $this->data['backend'] ) { + $credential = $backend->credential; + if ( ! $credential ) { + return; + } + + $user = $credential->client_id; + [$pre] = explode( $this->endpoint, $url ); + $url = + preg_replace( '/\/+$/', '', $pre ) . + "/remote.php/dav/files/{$user}/" . + preg_replace( '/^\/+/', '', $this->endpoint ); + } + + return $url; + }, + 10, + 2 + ); + + $filepath = $this->filepath( $touched ); + + if ( is_wp_error( $filepath ) ) { + return $filepath; + } + + $dav_modified = $this->get_dav_modified_date( $backend ); + if ( is_wp_error( $dav_modified ) ) { + return $dav_modified; + } + + if ( ! $dav_modified ) { + $headers = $this->payload_to_headers( $payload ); + $row = $this->payload_to_row( $payload ); + $csv = implode( "\n", array( $headers, $row ) ); + + file_put_contents( $filepath, $csv ); + $response = parent::submit( $csv ); + } elseif ( $touched ) { + $headers = $this->payload_to_headers( $payload ); + $row = $this->payload_to_row( $payload ); + $csv = implode( "\n", array( $headers, $row ) ); + + file_put_contents( $filepath, $csv ); + $response = parent::submit( $csv ); + } else { + $local_modified = filemtime( $filepath ); + + if ( $dav_modified > $local_modified ) { + $response = $backend->get( + $this->endpoint, + array(), + array(), + array( + 'stream' => true, + 'filename' => $filepath, + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + } + + $this->add_row( $payload ); + + $csv = file_get_contents( $filepath ); + $response = parent::submit( $csv ); + } + + if ( is_wp_error( $response ) ) { + return $response; + } + + touch( $filepath, time() ); + return $response; + } + + /** + * Sheets are flat, if payload has nested arrays, flattens it and concatenate its keys + * as field names. + * + * @param array $payload Submission payload. + * @param string $path Prefix to prepend to the field name. + * + * @return array Flattened payload. + */ + private static function flatten_payload( $payload, $path = '' ) { + $flat = array(); + foreach ( $payload as $field => $value ) { + $key = $path . $field; + $value = self::flatten_value( $value, $key ); + + if ( ! is_array( $value ) ) { + $flat[ $key ] = $value; + } else { + foreach ( $value as $_key => $_val ) { + $flat[ $_key ] = $_val; + } + } + } + + return $flat; + } + + /** + * Returns array values as a flat vector of play key values. + * + * @param mixed $value Payload value. + * @param string $path Hierarchical path to the value. + * + * @return mixed + */ + private static function flatten_value( $value, $path = '' ) { + if ( ! is_array( $value ) ) { + return $value; + } + + if ( wp_is_numeric_array( $value ) ) { + $simple_items = array_filter( $value, fn( $item ) => ! is_array( $item ) ); + + if ( count( $simple_items ) === count( $value ) ) { + return implode( ',', $value ); + } + } + + return self::flatten_payload( $value, $path . '.' ); + } +} diff --git a/forms-bridge/addons/nextcloud/hooks.php b/forms-bridge/addons/nextcloud/hooks.php new file mode 100644 index 00000000..0793fd4b --- /dev/null +++ b/forms-bridge/addons/nextcloud/hooks.php @@ -0,0 +1,149 @@ + array( + array( + 'ref' => '#backend', + 'name' => 'name', + 'default' => 'Nextcloud', + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'label' => __( 'Filepath', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'pattern' => '.+.csv$', + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'label' => __( 'Method', 'forms-bridge' ), + 'type' => 'text', + 'value' => 'PUT', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'label' => __( 'Filepath', 'forms-bridge' ), + 'pattern' => '.+\.csv$', + ), + array( + 'ref' => '#credential', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'schema', + 'type' => 'text', + 'value' => 'Basic', + ), + array( + 'ref' => '#credential', + 'name' => 'client_id', + 'label' => __( 'User login', 'forms-bridge' ), + 'description' => __( + 'Either, a user name or email', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_secret', + 'label' => __( 'Password', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + ), + 'bridge' => array( + 'backend' => 'Nextcloud', + ), + 'backend' => array( + 'name' => 'Nextcloud', + 'headers' => array( + array( + 'name' => 'Content-Type', + 'value' => 'application/octet-stream', + ), + ), + ), + 'credential' => array( + 'name' => '', + 'schema' => 'Basic', + 'client_id' => '', + 'client_secret' => '', + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'nextcloud-' ) !== 0 ) { + return $data; + } + + if ( ! preg_match( '/\.csv$/i', $data['bridge']['endpoint'] ) ) { + $data['bridge']['endpoint'] .= '.csv'; + } + + return $data; + }, + 10, + 2 +); diff --git a/addons/nextcloud/templates/spreadsheet-contacts.json b/forms-bridge/addons/nextcloud/templates/spreadsheet-contacts.json similarity index 100% rename from addons/nextcloud/templates/spreadsheet-contacts.json rename to forms-bridge/addons/nextcloud/templates/spreadsheet-contacts.json diff --git a/forms-bridge/addons/nextcloud/templates/woo-orders.php b/forms-bridge/addons/nextcloud/templates/woo-orders.php new file mode 100644 index 00000000..8dfc4291 --- /dev/null +++ b/forms-bridge/addons/nextcloud/templates/woo-orders.php @@ -0,0 +1,327 @@ + __( 'WC Orders', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into new lines on a Nextcloud CSV with order and customer information.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + ), + 'bridge' => array( + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'Order ID', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'Name[][0]', + 'cast' => 'string', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'Name[][1]', + 'cast' => 'string', + ), + array( + 'from' => 'Name[]', + 'to' => 'Name[]', + 'cast' => 'concat', + ), + array( + 'from' => 'Name', + 'to' => 'Name', + 'cast' => 'csv', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'Date', + 'cast' => 'string', + ), + array( + 'from' => 'total', + 'to' => 'Total', + 'cast' => 'number', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines[].name', + 'to' => 'Shipping', + 'cast' => 'string', + ), + array( + 'from' => 'Shipping', + 'to' => 'Shipping', + 'cast' => 'csv', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'Shipping Total', + 'cast' => 'number', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines[].code', + 'to' => 'Coupons', + 'cast' => 'string', + ), + array( + 'from' => 'Coupons', + 'to' => 'Coupons', + 'cast' => 'csv', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'Discount Total', + 'cast' => 'number', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'First Name', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'Last Name', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'Email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'Phone', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'Address', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_2', + 'to' => 'Address 2', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'City', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'Postal Code', + 'cast' => 'string', + ), + array( + 'from' => '?billing.state', + 'to' => 'State', + 'cast' => 'string', + ), + array( + 'from' => '?billing.country', + 'to' => 'Country', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'Payment Method', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'Note', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/api.php b/forms-bridge/addons/odoo/api.php new file mode 100644 index 00000000..b4bc49a5 --- /dev/null +++ b/forms-bridge/addons/odoo/api.php @@ -0,0 +1,174 @@ + false, + 'name' => $payload['name'], + ); + + $partner_fields = array( + 'title', + 'parent_id', + 'lang', + 'vat', + 'website', + 'employee', + 'function', + 'street', + 'street2', + 'zip', + 'city', + 'country_code', + 'country_id', + 'email', + 'phone', + 'mobile', + 'is_public', + 'additional_info', + ); + + foreach ( $partner_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $partner[ $field ] = $payload[ $field ]; + } + } + + $query = array( array( 'name', '=', $partner['name'] ) ); + + if ( isset( $partner['email'] ) ) { + $query[] = array( 'email', '=', $partner['email'] ); + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-search-partner', + 'method' => 'search_read', + 'endpoint' => 'res.partner', + ) + ) + ->submit( $query ); + + if ( ! is_wp_error( $response ) ) { + return $response['data']['result'][0]; + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-create-partner', + 'method' => 'create', + 'endpoint' => 'res.partner', + ) + ) + ->submit( $partner ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-get-partner-data', + 'method' => 'read', + 'endpoint' => 'res.partner', + ) + ) + ->submit( array( $response['data']['result'] ) ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response['data']['result'][0]; +} + +function forms_bridge_odoo_create_company( $payload, $bridge ) { + $company = array( + 'is_company' => true, + 'name' => $payload['name'], + ); + + $company_fields = array( + 'lang', + 'vat', + 'website', + 'street', + 'street2', + 'zip', + 'city', + 'country_code', + 'country_id', + 'email', + 'phone', + 'mobile', + 'is_public', + 'additional_info', + ); + + foreach ( $company_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $company[ $field ] = $payload[ $field ]; + } + } + + $query = array( array( 'name', '=', $company['name'] ) ); + + if ( isset( $company['email'] ) ) { + $query[] = array( 'email', '=', $company['email'] ); + } + + if ( isset( $company['vat'] ) ) { + $query[] = array( 'vat', '=', $company['vat'] ); + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-search-company', + 'template' => null, + 'method' => 'search_read', + 'endpoint' => 'res.partner', + ) + ) + ->submit( $query ); + + if ( ! is_wp_error( $response ) ) { + return $response['data']['result'][0]; + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-create-company', + 'method' => 'create', + 'endpoint' => 'res.partner', + ) + ) + ->submit( $company ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-get-company-data', + 'method' => 'read', + 'endpoint' => 'res.partner', + ) + ) + ->submit( array( $response['data']['result'] ) ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $response['data']['result'][0]; +} diff --git a/addons/odoo/assets/logo.png b/forms-bridge/addons/odoo/assets/logo.png similarity index 100% rename from addons/odoo/assets/logo.png rename to forms-bridge/addons/odoo/assets/logo.png diff --git a/forms-bridge/addons/odoo/class-odoo-addon.php b/forms-bridge/addons/odoo/class-odoo-addon.php new file mode 100644 index 00000000..5c4deea2 --- /dev/null +++ b/forms-bridge/addons/odoo/class-odoo-addon.php @@ -0,0 +1,144 @@ + '__odoo-' . time(), + 'method' => 'search', + 'endpoint' => 'res.users', + 'backend' => $backend, + ) + ); + + $response = $bridge->submit(); + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend model and retrive the response data. + * + * @param string $endpoint Target model name. + * @param string $backend Target backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge = new Odoo_Form_Bridge( + array( + 'name' => '__odoo-' . time(), + 'method' => 'search_read', + 'endpoint' => $endpoint, + 'backend' => $backend, + ) + ); + + return $bridge->submit( array(), array( 'id', 'name' ) ); + } + + /** + * Performs an introspection of the backend model and returns API fields + * and accepted content type. + * + * @param string $model Target model name. + * @param string $backend Target backend name. + * + * @return array List of fields and content type of the model. + */ + public function get_endpoint_schema( $model, $backend ) { + $bridge = new Odoo_Form_Bridge( + array( + 'name' => '__odoo-' . time(), + 'method' => 'fields_get', + 'endpoint' => $model, + 'backend' => $backend, + ) + ); + + $response = $bridge->submit(); + + if ( is_wp_error( $response ) ) { + return array(); + } + + $fields = array(); + foreach ( $response['data']['result'] as $name => $spec ) { + if ( $spec['readonly'] ) { + continue; + } + + if ( 'char' === $spec['type'] || 'html' === $spec['type'] ) { + $schema = array( 'type' => 'string' ); + } elseif ( 'float' === $spec['type'] ) { + $schema = array( 'type' => 'number' ); + } elseif ( + in_array( + $spec['type'], + array( 'one2many', 'many2one', 'many2many' ), + true + ) + ) { + $schema = array( + 'type' => 'array', + 'items' => array( array( 'type' => 'integer' ), array( 'type' => 'string' ) ), + 'additionalItems' => false, + ); + } else { + $schema = array( 'type' => $spec['type'] ); + } + + $schema['required'] = $spec['required']; + + $fields[] = array( + 'name' => $name, + 'schema' => $schema, + ); + } + + return $fields; + } +} + +Odoo_Addon::setup(); diff --git a/forms-bridge/addons/odoo/class-odoo-form-bridge.php b/forms-bridge/addons/odoo/class-odoo-form-bridge.php new file mode 100644 index 00000000..5e6a9189 --- /dev/null +++ b/forms-bridge/addons/odoo/class-odoo-form-bridge.php @@ -0,0 +1,253 @@ + '2.0', + 'method' => 'call', + 'id' => $session_id, + 'params' => array( + 'service' => $service, + 'method' => $method, + 'args' => $args, + ), + ); + } + + /** + * Handle RPC responses and catch errors on the application layer. + * + * @param array $response Request response. + * @param boolean $is_single Should the result be an entity or an array. + * + * @return mixed|WP_Error Request result. + */ + public static function rpc_response( $res ) { + if ( is_wp_error( $res ) ) { + return $res; + } + + if ( empty( $res['data'] ) ) { + $content_type = + Http_Client::get_content_type( $res['headers'] ) ?? 'undefined'; + + return new WP_Error( + 'unkown_content_type', + /* translators: %s: Content-Type header value */ + sprintf( + __( 'Unkown HTTP response content type %s', 'forms-bridge' ), + sanitize_text_field( $content_type ) + ), + $res + ); + } + + if ( isset( $res['data']['error'] ) ) { + $error = new WP_Error( + 'response_code_' . $res['data']['error']['code'], + $res['data']['error']['message'], + $res['data']['error']['data'] + ); + + $error_data = array( 'response' => $res ); + if ( self::$request ) { + $error_data['request'] = self::$request; + } + + $error->add_data( $error_data ); + return $error; + } + + $data = $res['data']; + + if ( empty( $data['result'] ) ) { + $error = new WP_Error( 'not_found' ); + + $error_data = array( 'response' => $res ); + if ( self::$request ) { + $error_data['request'] = self::$request; + } + + $error->add_data( $error_data ); + return $error; + } + + return $data['result']; + } + + /** + * JSON RPC login request. + * + * @param [string, string, string] $login + * @param Backend $backend + * + * @return array|WP_Error Tuple with RPC session id and user id. + */ + private static function rpc_login( $login, $backend ) { + if ( self::$session ) { + return self::$session; + } + + $session_id = 'forms-bridge-' . time(); + + $payload = self::rpc_payload( $session_id, 'common', 'login', $login ); + + $response = $backend->post( self::endpoint, $payload ); + + $user_id = self::rpc_response( $response ); + + if ( is_wp_error( $user_id ) ) { + return $user_id; + } + + self::$session = array( $session_id, $user_id ); + return self::$session; + } + + public function __construct( $data, $addon = null ) { + parent::__construct( $data, 'odoo' ); + } + + /** + * Submits submission to the backend. + * + * @param array $payload Submission data. + * @param array|null $more_args Additional RPC call params. + * + * @return array|WP_Error Http + */ + public function submit( $payload = array(), $more_args = array() ) { + if ( ! $this->is_valid ) { + return new WP_Error( 'invalid_bridge' ); + } + + $backend = $this->backend(); + + if ( ! $backend ) { + return new WP_Error( + 'invalid_backend', + 'The bridge does not have a valid backend' + ); + } + + $credential = $backend->credential; + if ( ! $credential ) { + return new WP_Error( + 'invalid_credential', + 'The bridge does not have a valid credential' + ); + } + + add_filter( + 'http_bridge_request', + static function ( $request ) { + self::$request = $request; + return $request; + }, + 10, + 1 + ); + + $backend_name = $backend->name; + + add_filter( + 'http_bridge_backend_headers', + static function ( $headers, $backend ) use ( $backend_name ) { + if ( $backend->name !== $backend_name ) { + return $headers; + } + + $locale = get_locale(); + if ( ! $locale ) { + return $headers; + } + + if ( 'ca' === $locale ) { + $locale = 'ca_ES'; + } + + $headers['Accept-Language'] = $locale; + return $headers; + }, + 20, + 2 + ); + + $login = $credential->authorization(); + $session = self::rpc_login( $login, $backend ); + + if ( is_wp_error( $session ) ) { + return $session; + } + + [$sid, $uid] = $session; + $login[1] = $uid; + + $payload = self::rpc_payload( + $sid, + 'object', + 'execute', + array_merge( $login, array( $this->endpoint, $this->method, $payload ) ), + $more_args + ); + + $response = $backend->post( self::endpoint, $payload ); + + $result = self::rpc_response( $response ); + if ( is_wp_error( $result ) ) { + return $result; + } + + return $response; + } +} diff --git a/forms-bridge/addons/odoo/data/state-codes.php b/forms-bridge/addons/odoo/data/state-codes.php new file mode 100644 index 00000000..5c26fee3 --- /dev/null +++ b/forms-bridge/addons/odoo/data/state-codes.php @@ -0,0 +1,801 @@ + __( 'Ajman', 'forms-bridge' ), + 'AZ' => __( 'Abu Dhabi', 'forms-bridge' ), + 'DU' => __( 'Dubai', 'forms-bridge' ), + 'FU' => __( 'Fujairah', 'forms-bridge' ), + 'RK' => __( 'Ras al-Khaimah', 'forms-bridge' ), + 'SH' => __( 'Sharjah', 'forms-bridge' ), + 'UQ' => __( 'Umm al-Quwain', 'forms-bridge' ), +); + +global $forms_bridge_odoo_ar_states; +$forms_bridge_odoo_ar_states = array( + 'A' => __( 'Salta', 'forms-bridge' ), + 'B' => __( 'Buenos Aires', 'forms-bridge' ), + 'C' => __( 'Ciudad Autónoma de Buenos Aires', 'forms-bridge' ), + 'D' => __( 'San Luis', 'forms-bridge' ), + 'E' => __( 'Entre Ríos', 'forms-bridge' ), + 'F' => __( 'La Rioja', 'forms-bridge' ), + 'G' => __( 'Santiago Del Estero', 'forms-bridge' ), + 'H' => __( 'Chaco', 'forms-bridge' ), + 'J' => __( 'San Juan', 'forms-bridge' ), + 'K' => __( 'Catamarca', 'forms-bridge' ), + 'L' => __( 'La Pampa', 'forms-bridge' ), + 'M' => __( 'Mendoza', 'forms-bridge' ), + 'N' => __( 'Misiones', 'forms-bridge' ), + 'P' => __( 'Formosa', 'forms-bridge' ), + 'Q' => __( 'Neuquén', 'forms-bridge' ), + 'R' => __( 'Río Negro', 'forms-bridge' ), + 'S' => __( 'Santa Fe', 'forms-bridge' ), + 'T' => __( 'Tucumán', 'forms-bridge' ), + 'U' => __( 'Chubut', 'forms-bridge' ), + 'V' => __( 'Tierra del Fuego', 'forms-bridge' ), + 'W' => __( 'Corrientes', 'forms-bridge' ), + 'X' => __( 'Córdoba', 'forms-bridge' ), + 'Y' => __( 'Jujuy', 'forms-bridge' ), + 'Z' => __( 'Santa Cruz', 'forms-bridge' ), +); + +global $forms_bridge_odoo_au_states; +$forms_bridge_odoo_au_states = array( + 'ACT' => __( 'Australian Capital Territory', 'forms-bridge' ), + 'NSW' => __( 'New South Wales', 'forms-bridge' ), + 'NT' => __( 'Northern Territory', 'forms-bridge' ), + 'QLD' => __( 'Queensland', 'forms-bridge' ), + 'SA' => __( 'South Australia', 'forms-bridge' ), + 'TAS' => __( 'Tasmania', 'forms-bridge' ), + 'VIC' => __( 'Victoria', 'forms-bridge' ), + 'WA' => __( 'Western Australia', 'forms-bridge' ), +); + +global $forms_bridge_odoo_br_states; +$forms_bridge_odoo_br_states = array( + 'AC' => __( 'Acre', 'forms-bridge' ), + 'AL' => __( 'Alagoas', 'forms-bridge' ), + 'AM' => __( 'Amazonas', 'forms-bridge' ), + 'AP' => __( 'Amapá', 'forms-bridge' ), + 'BA' => __( 'Bahia', 'forms-bridge' ), + 'CE' => __( 'Ceará', 'forms-bridge' ), + 'DF' => __( 'Distrito Federal', 'forms-bridge' ), + 'ES' => __( 'Espírito Santo', 'forms-bridge' ), + 'GO' => __( 'Goiás', 'forms-bridge' ), + 'MA' => __( 'Maranhão', 'forms-bridge' ), + 'MG' => __( 'Minas Gerais', 'forms-bridge' ), + 'MS' => __( 'Mato Grosso do Sul', 'forms-bridge' ), + 'MT' => __( 'Mato Grosso', 'forms-bridge' ), + 'PA' => __( 'Pará', 'forms-bridge' ), + 'PB' => __( 'Paraíba', 'forms-bridge' ), + 'PE' => __( 'Pernambuco', 'forms-bridge' ), + 'PI' => __( 'Piauí', 'forms-bridge' ), + 'PR' => __( 'Paraná', 'forms-bridge' ), + 'RJ' => __( 'Rio de Janeiro', 'forms-bridge' ), + 'RN' => __( 'Rio Grande do Norte', 'forms-bridge' ), + 'RO' => __( 'Rondônia', 'forms-bridge' ), + 'RR' => __( 'Roraima', 'forms-bridge' ), + 'RS' => __( 'Rio Grande do Sul', 'forms-bridge' ), + 'SC' => __( 'Santa Catarina', 'forms-bridge' ), + 'SE' => __( 'Sergipe', 'forms-bridge' ), + 'SP' => __( 'São Paulo', 'forms-bridge' ), + 'TO' => __( 'Tocantins', 'forms-bridge' ), +); + +global $forms_bridge_odoo_ca_states; +$forms_bridge_odoo_ca_states = array( + 'AB' => __( 'Alberta', 'forms-bridge' ), + 'BC' => __( 'British Columbia', 'forms-bridge' ), + 'MB' => __( 'Manitoba', 'forms-bridge' ), + 'NB' => __( 'New Brunswick', 'forms-bridge' ), + 'NL' => __( 'Newfoundland and Labrador', 'forms-bridge' ), + 'NS' => __( 'Nova Scotia', 'forms-bridge' ), + 'NT' => __( 'Northwest Territories', 'forms-bridge' ), + 'NU' => __( 'Nunavut', 'forms-bridge' ), + 'ON' => __( 'Ontario', 'forms-bridge' ), + 'PE' => __( 'Prince Edward Island', 'forms-bridge' ), + 'QC' => __( 'Quebec', 'forms-bridge' ), + 'SK' => __( 'Saskatchewan', 'forms-bridge' ), + 'YT' => __( 'Yukon', 'forms-bridge' ), +); + +global $forms_bridge_odoo_co_states; +$forms_bridge_odoo_co_states = array( + 'AMA' => __( 'Amazonas', 'forms-bridge' ), + 'ANT' => __( 'Antioquia', 'forms-bridge' ), + 'ARA' => __( 'Arauca', 'forms-bridge' ), + 'ATL' => __( 'Atlántico', 'forms-bridge' ), + 'BOL' => __( 'Bolívar', 'forms-bridge' ), + 'BOY' => __( 'Boyacá', 'forms-bridge' ), + 'CAL' => __( 'Caldas', 'forms-bridge' ), + 'CAQ' => __( 'Caquetá', 'forms-bridge' ), + 'CAS' => __( 'Casanare', 'forms-bridge' ), + 'CAU' => __( 'Cauca', 'forms-bridge' ), + 'CES' => __( 'Cesar', 'forms-bridge' ), + 'CHO' => __( 'Chocó', 'forms-bridge' ), + 'COR' => __( 'Córdoba', 'forms-bridge' ), + 'CUN' => __( 'Cundinamarca', 'forms-bridge' ), + 'DC' => __( 'D.C.', 'forms-bridge' ), + 'GUA' => __( 'Guainía', 'forms-bridge' ), + 'GUV' => __( 'Guaviare', 'forms-bridge' ), + 'HUI' => __( 'Huila', 'forms-bridge' ), + 'LAG' => __( 'La Guajira', 'forms-bridge' ), + 'MAG' => __( 'Magdalena', 'forms-bridge' ), + 'MET' => __( 'Meta', 'forms-bridge' ), + 'NAR' => __( 'Nariño', 'forms-bridge' ), + 'NSA' => __( 'Norte de Santander', 'forms-bridge' ), + 'PUT' => __( 'Putumayo', 'forms-bridge' ), + 'QUI' => __( 'Quindio', 'forms-bridge' ), + 'RIS' => __( 'Risaralda', 'forms-bridge' ), + 'SAN' => __( 'Santander', 'forms-bridge' ), + 'SAP' => __( + 'Archipiélago de San Andrés, Providencia y Santa Catalina', + 'forms-bridge' + ), + 'SUC' => __( 'Sucre', 'forms-bridge' ), + 'TOL' => __( 'Tolima', 'forms-bridge' ), + 'VAC' => __( 'Valle del Cauca', 'forms-bridge' ), + 'VAU' => __( 'Vaupés', 'forms-bridge' ), + 'VID' => __( 'Vichada', 'forms-bridge' ), +); + +global $forms_bridge_odoo_eg_states; +$forms_bridge_odoo_eg_states = array( + 'ALX' => __( 'Alexandria', 'forms-bridge' ), + 'ASN' => __( 'Aswan', 'forms-bridge' ), + 'AST' => __( 'Asyut', 'forms-bridge' ), + 'BA' => __( 'Red Sea', 'forms-bridge' ), + 'BH' => __( 'Beheira', 'forms-bridge' ), + 'BNS' => __( 'Beni Suef', 'forms-bridge' ), + 'C' => __( 'Cairo', 'forms-bridge' ), + 'DK' => __( 'Dakahlia', 'forms-bridge' ), + 'DT' => __( 'Damietta', 'forms-bridge' ), + 'FYM' => __( 'Faiyum', 'forms-bridge' ), + 'GH' => __( 'Gharbia', 'forms-bridge' ), + 'GZ' => __( 'Giza', 'forms-bridge' ), + 'HU' => __( 'Helwan', 'forms-bridge' ), + 'IS' => __( 'Ismailia', 'forms-bridge' ), + 'JS' => __( 'South Sinai', 'forms-bridge' ), + 'KB' => __( 'Qalyubia', 'forms-bridge' ), + 'KFS' => __( 'Kafr el-Sheikh', 'forms-bridge' ), + 'KN' => __( 'Qena', 'forms-bridge' ), + 'LX' => __( 'Luxor', 'forms-bridge' ), + 'MN' => __( 'Minya', 'forms-bridge' ), + 'MNF' => __( 'Monufia', 'forms-bridge' ), + 'MT' => __( 'Matrouh', 'forms-bridge' ), + 'PTS' => __( 'Port Said', 'forms-bridge' ), + 'SHG' => __( 'Sohag', 'forms-bridge' ), + 'SHR' => __( 'Al Sharqia', 'forms-bridge' ), + 'SIN' => __( 'North Sinai', 'forms-bridge' ), + 'SU' => __( '6th of October', 'forms-bridge' ), + 'SUZ' => __( 'Suez', 'forms-bridge' ), + 'WAD' => __( 'New Valley', 'forms-bridge' ), +); + +global $forms_bridge_odoo_es_states; +$forms_bridge_odoo_es_states = array( + 'A' => __( 'Alacant (Alicante)', 'forms-bridge' ), + 'AB' => __( 'Albacete', 'forms-bridge' ), + 'AL' => __( 'Almería', 'forms-bridge' ), + 'AV' => __( 'Ávila', 'forms-bridge' ), + 'B' => __( 'Barcelona', 'forms-bridge' ), + 'BA' => __( 'Badajoz', 'forms-bridge' ), + 'BI' => __( 'Bizkaia (Vizcaya)', 'forms-bridge' ), + 'BU' => __( 'Burgos', 'forms-bridge' ), + 'C' => __( 'A Coruña (La Coruña)', 'forms-bridge' ), + 'CA' => __( 'Cádiz', 'forms-bridge' ), + 'CC' => __( 'Cáceres', 'forms-bridge' ), + 'CE' => __( 'Ceuta', 'forms-bridge' ), + 'CO' => __( 'Córdoba', 'forms-bridge' ), + 'CR' => __( 'Ciudad Real', 'forms-bridge' ), + 'CS' => __( 'Castelló (Castellón)', 'forms-bridge' ), + 'CU' => __( 'Cuenca', 'forms-bridge' ), + 'GC' => __( 'Las Palmas', 'forms-bridge' ), + 'GI' => __( 'Girona (Gerona)', 'forms-bridge' ), + 'GR' => __( 'Granada', 'forms-bridge' ), + 'GU' => __( 'Guadalajara', 'forms-bridge' ), + 'H' => __( 'Huelva', 'forms-bridge' ), + 'HU' => __( 'Huesca', 'forms-bridge' ), + 'J' => __( 'Jaén', 'forms-bridge' ), + 'L' => __( 'Lleida (Lérida)', 'forms-bridge' ), + 'LE' => __( 'León', 'forms-bridge' ), + 'LO' => __( 'La Rioja', 'forms-bridge' ), + 'LU' => __( 'Lugo', 'forms-bridge' ), + 'M' => __( 'Madrid', 'forms-bridge' ), + 'MA' => __( 'Málaga', 'forms-bridge' ), + 'ME' => __( 'Melilla', 'forms-bridge' ), + 'MU' => __( 'Murcia', 'forms-bridge' ), + 'NA' => __( 'Navarra (Nafarroa)', 'forms-bridge' ), + 'O' => __( 'Asturias', 'forms-bridge' ), + 'OR' => __( 'Ourense (Orense)', 'forms-bridge' ), + 'P' => __( 'Palencia', 'forms-bridge' ), + 'PM' => __( 'Illes Balears (Islas Baleares)', 'forms-bridge' ), + 'PO' => __( 'Pontevedra', 'forms-bridge' ), + 'S' => __( 'Cantabria', 'forms-bridge' ), + 'SA' => __( 'Salamanca', 'forms-bridge' ), + 'SE' => __( 'Sevilla', 'forms-bridge' ), + 'SG' => __( 'Segovia', 'forms-bridge' ), + 'SO' => __( 'Soria', 'forms-bridge' ), + 'SS' => __( 'Gipuzkoa (Guipúzcoa)', 'forms-bridge' ), + 'T' => __( 'Tarragona', 'forms-bridge' ), + 'TE' => __( 'Teruel', 'forms-bridge' ), + 'TF' => __( 'Santa Cruz de Tenerife', 'forms-bridge' ), + 'TO' => __( 'Toledo', 'forms-bridge' ), + 'V' => __( 'València (Valencia)', 'forms-bridge' ), + 'VA' => __( 'Valladolid', 'forms-bridge' ), + 'VI' => __( 'Araba/Álava', 'forms-bridge' ), + 'Z' => __( 'Zaragoza', 'forms-bridge' ), + 'ZA' => __( 'Zamora', 'forms-bridge' ), +); + +global $forms_bridge_odoo_gt_states; +$forms_bridge_odoo_gt_states = array( + 'AVE' => __( 'Alta Verapaz', 'forms-bridge' ), + 'BVE' => __( 'Baja Verapaz', 'forms-bridge' ), + 'CMT' => __( 'Chimaltenango', 'forms-bridge' ), + 'CQM' => __( 'Chiquimula', 'forms-bridge' ), + 'EPR' => __( 'El Progreso', 'forms-bridge' ), + 'ESC' => __( 'Escuintla', 'forms-bridge' ), + 'GUA' => __( 'Guatemala', 'forms-bridge' ), + 'HUE' => __( 'Huehuetenango', 'forms-bridge' ), + 'IZA' => __( 'Izabal', 'forms-bridge' ), + 'JAL' => __( 'Jalapa', 'forms-bridge' ), + 'JUT' => __( 'Jutiapa', 'forms-bridge' ), + 'PET' => __( 'Petén', 'forms-bridge' ), + 'QUE' => __( 'Quetzaltenango', 'forms-bridge' ), + 'QUI' => __( 'Quiché', 'forms-bridge' ), + 'RET' => __( 'Retalhuleu', 'forms-bridge' ), + 'SAC' => __( 'Sacatepéquez', 'forms-bridge' ), + 'SMA' => __( 'San Marcos', 'forms-bridge' ), + 'SOL' => __( 'Sololá', 'forms-bridge' ), + 'SRO' => __( 'Santa Rosa', 'forms-bridge' ), + 'SUC' => __( 'Suchitepéquez', 'forms-bridge' ), + 'TOT' => __( 'Totonicapán', 'forms-bridge' ), + 'ZAC' => __( 'Zacapa', 'forms-bridge' ), +); + +global $forms_bridge_odoo_id_states; +$forms_bridge_odoo_id_states = array( + 'AC' => __( 'Aceh', 'forms-bridge' ), + 'BA' => __( 'Bali', 'forms-bridge' ), + 'BB' => __( 'Bangka Belitung', 'forms-bridge' ), + 'BE' => __( 'Bengkulu', 'forms-bridge' ), + 'BT' => __( 'Banten', 'forms-bridge' ), + 'GO' => __( 'Gorontalo', 'forms-bridge' ), + 'JA' => __( 'Jambi', 'forms-bridge' ), + 'JB' => __( 'Jawa Barat', 'forms-bridge' ), + 'JI' => __( 'Jawa Timur', 'forms-bridge' ), + 'JK' => __( 'Jakarta', 'forms-bridge' ), + 'JT' => __( 'Jawa Tengah', 'forms-bridge' ), + 'KB' => __( 'Kalimantan Barat', 'forms-bridge' ), + 'KI' => __( 'Kalimantan Timur', 'forms-bridge' ), + 'KR' => __( 'Kepulauan Riau', 'forms-bridge' ), + 'KS' => __( 'Kalimantan Selatan', 'forms-bridge' ), + 'KT' => __( 'Kalimantan Tengah', 'forms-bridge' ), + 'KU' => __( 'Kalimantan Utara', 'forms-bridge' ), + 'LA' => __( 'Lampung', 'forms-bridge' ), + 'MA' => __( 'Maluku', 'forms-bridge' ), + 'MU' => __( 'Maluku Utara', 'forms-bridge' ), + 'NB' => __( 'Nusa Tenggara Barat', 'forms-bridge' ), + 'NT' => __( 'Nusa Tenggara Timur', 'forms-bridge' ), + 'PA' => __( 'Papua', 'forms-bridge' ), + 'PB' => __( 'Papua Barat', 'forms-bridge' ), + 'RI' => __( 'Riau', 'forms-bridge' ), + 'SA' => __( 'Sulawesi Utara', 'forms-bridge' ), + 'SB' => __( 'Sumatra Barat', 'forms-bridge' ), + 'SG' => __( 'Sulawesi Tenggara', 'forms-bridge' ), + 'SN' => __( 'Sulawesi Selatan', 'forms-bridge' ), + 'SR' => __( 'Sulawesi Barat', 'forms-bridge' ), + 'SS' => __( 'Sumatra Selatan', 'forms-bridge' ), + 'ST' => __( 'Sulawesi Tengah', 'forms-bridge' ), + 'SU' => __( 'Sumatra Utara', 'forms-bridge' ), + 'YO' => __( 'Yogyakarta', 'forms-bridge' ), +); + +global $forms_bridge_odoo_in_states; +$forms_bridge_odoo_in_states = array( + 'AN' => __( 'Andaman and Nicobar', 'forms-bridge' ), + 'AP' => __( 'Andhra Pradesh', 'forms-bridge' ), + 'AR' => __( 'Arunachal Pradesh', 'forms-bridge' ), + 'AS' => __( 'Assam', 'forms-bridge' ), + 'BR' => __( 'Bihar', 'forms-bridge' ), + 'CG' => __( 'Chattisgarh', 'forms-bridge' ), + 'CH' => __( 'Chandigarh', 'forms-bridge' ), + 'DD' => __( 'Daman and Diu', 'forms-bridge' ), + 'DL' => __( 'Delhi', 'forms-bridge' ), + 'DN' => __( 'Dadra and Nagar Haveli', 'forms-bridge' ), + 'GA' => __( 'Goa', 'forms-bridge' ), + 'GJ' => __( 'Gujarat', 'forms-bridge' ), + 'HP' => __( 'Himachal Pradesh', 'forms-bridge' ), + 'HR' => __( 'Haryana', 'forms-bridge' ), + 'JH' => __( 'Jharkhand', 'forms-bridge' ), + 'JK' => __( 'Jammu and Kashmir', 'forms-bridge' ), + 'KA' => __( 'Karnataka', 'forms-bridge' ), + 'KL' => __( 'Kerala', 'forms-bridge' ), + 'LD' => __( 'Lakshadweep', 'forms-bridge' ), + 'MH' => __( 'Maharashtra', 'forms-bridge' ), + 'ML' => __( 'Meghalaya', 'forms-bridge' ), + 'MN' => __( 'Manipur', 'forms-bridge' ), + 'MP' => __( 'Madhya Pradesh', 'forms-bridge' ), + 'MZ' => __( 'Mizoram', 'forms-bridge' ), + 'NL' => __( 'Nagaland', 'forms-bridge' ), + 'OR' => __( 'Orissa', 'forms-bridge' ), + 'PB' => __( 'Punjab', 'forms-bridge' ), + 'PY' => __( 'Puducherry', 'forms-bridge' ), + 'RJ' => __( 'Rajasthan', 'forms-bridge' ), + 'SK' => __( 'Sikkim', 'forms-bridge' ), + 'TN' => __( 'Tamil Nadu', 'forms-bridge' ), + 'TR' => __( 'Tripura', 'forms-bridge' ), + 'TS' => __( 'Telangana', 'forms-bridge' ), + 'UK' => __( 'Uttarakhand', 'forms-bridge' ), + 'UP' => __( 'Uttar Pradesh', 'forms-bridge' ), + 'WB' => __( 'West Bengal', 'forms-bridge' ), +); + +global $forms_bridge_odoo_it_states; +$forms_bridge_odoo_it_states = array( + 'AG' => __( 'Agrigento', 'forms-bridge' ), + 'AL' => __( 'Alessandria', 'forms-bridge' ), + 'AN' => __( 'Ancona', 'forms-bridge' ), + 'AO' => __( 'Aosta', 'forms-bridge' ), + 'AP' => __( 'Ascoli Piceno', 'forms-bridge' ), + 'AQ' => __( 'L\'Aquila', 'forms-bridge' ), + 'AR' => __( 'Arezzo', 'forms-bridge' ), + 'AT' => __( 'Asti', 'forms-bridge' ), + 'AV' => __( 'Avellino', 'forms-bridge' ), + 'BA' => __( 'Bari', 'forms-bridge' ), + 'BG' => __( 'Bergamo', 'forms-bridge' ), + 'BI' => __( 'Biella', 'forms-bridge' ), + 'BL' => __( 'Belluno', 'forms-bridge' ), + 'BN' => __( 'Benevento', 'forms-bridge' ), + 'BO' => __( 'Bologna', 'forms-bridge' ), + 'BR' => __( 'Brindisi', 'forms-bridge' ), + 'BS' => __( 'Brescia', 'forms-bridge' ), + 'BT' => __( 'Barletta-Andria-Trani', 'forms-bridge' ), + 'BZ' => __( 'Bolzano', 'forms-bridge' ), + 'CA' => __( 'Cagliari', 'forms-bridge' ), + 'CB' => __( 'Campobasso', 'forms-bridge' ), + 'CE' => __( 'Caserta', 'forms-bridge' ), + 'CH' => __( 'Chieti', 'forms-bridge' ), + 'CI' => __( 'Carbonia-Iglesias', 'forms-bridge' ), + 'CL' => __( 'Caltanissetta', 'forms-bridge' ), + 'CN' => __( 'Cuneo', 'forms-bridge' ), + 'CO' => __( 'Como', 'forms-bridge' ), + 'CR' => __( 'Cremona', 'forms-bridge' ), + 'CS' => __( 'Cosenza', 'forms-bridge' ), + 'CT' => __( 'Catania', 'forms-bridge' ), + 'CZ' => __( 'Catanzaro', 'forms-bridge' ), + 'EN' => __( 'Enna', 'forms-bridge' ), + 'FC' => __( 'Forlì-Cesena', 'forms-bridge' ), + 'FE' => __( 'Ferrara', 'forms-bridge' ), + 'FG' => __( 'Foggia', 'forms-bridge' ), + 'FI' => __( 'Firenze', 'forms-bridge' ), + 'FM' => __( 'Fermo', 'forms-bridge' ), + 'FR' => __( 'Frosinone', 'forms-bridge' ), + 'GE' => __( 'Genova', 'forms-bridge' ), + 'GO' => __( 'Gorizia', 'forms-bridge' ), + 'GR' => __( 'Grosseto', 'forms-bridge' ), + 'IM' => __( 'Imperia', 'forms-bridge' ), + 'IS' => __( 'Isernia', 'forms-bridge' ), + 'KR' => __( 'Crotone', 'forms-bridge' ), + 'LC' => __( 'Lecco', 'forms-bridge' ), + 'LE' => __( 'Lecce', 'forms-bridge' ), + 'LI' => __( 'Livorno', 'forms-bridge' ), + 'LO' => __( 'Lodi', 'forms-bridge' ), + 'LT' => __( 'Latina', 'forms-bridge' ), + 'LU' => __( 'Lucca', 'forms-bridge' ), + 'MB' => __( 'Monza e Brianza', 'forms-bridge' ), + 'MC' => __( 'Macerata', 'forms-bridge' ), + 'ME' => __( 'Messina', 'forms-bridge' ), + 'MI' => __( 'Milano', 'forms-bridge' ), + 'MN' => __( 'Mantova', 'forms-bridge' ), + 'MO' => __( 'Modena', 'forms-bridge' ), + 'MS' => __( 'Massa-Carrara', 'forms-bridge' ), + 'MT' => __( 'Matera', 'forms-bridge' ), + 'NA' => __( 'Napoli', 'forms-bridge' ), + 'NO' => __( 'Novara', 'forms-bridge' ), + 'NU' => __( 'Nuoro', 'forms-bridge' ), + 'OG' => __( 'Ogliastra', 'forms-bridge' ), + 'OR' => __( 'Oristano', 'forms-bridge' ), + 'OT' => __( 'Olbia-Tempio', 'forms-bridge' ), + 'PA' => __( 'Palermo', 'forms-bridge' ), + 'PC' => __( 'Piacenza', 'forms-bridge' ), + 'PD' => __( 'Padova', 'forms-bridge' ), + 'PE' => __( 'Pescara', 'forms-bridge' ), + 'PG' => __( 'Perugia', 'forms-bridge' ), + 'PI' => __( 'Pisa', 'forms-bridge' ), + 'PN' => __( 'Pordenone', 'forms-bridge' ), + 'PO' => __( 'Prato', 'forms-bridge' ), + 'PR' => __( 'Parma', 'forms-bridge' ), + 'PT' => __( 'Pistoia', 'forms-bridge' ), + 'PU' => __( 'Pesaro e Urbino', 'forms-bridge' ), + 'PV' => __( 'Pavia', 'forms-bridge' ), + 'PZ' => __( 'Potenza', 'forms-bridge' ), + 'RA' => __( 'Ravenna', 'forms-bridge' ), + 'RC' => __( 'Reggio Calabria', 'forms-bridge' ), + 'RE' => __( 'Reggio Emilia', 'forms-bridge' ), + 'RG' => __( 'Ragusa', 'forms-bridge' ), + 'RI' => __( 'Rieti', 'forms-bridge' ), + 'RM' => __( 'Roma', 'forms-bridge' ), + 'RN' => __( 'Rimini', 'forms-bridge' ), + 'RO' => __( 'Rovigo', 'forms-bridge' ), + 'SA' => __( 'Salerno', 'forms-bridge' ), + 'SI' => __( 'Siena', 'forms-bridge' ), + 'SO' => __( 'Sondrio', 'forms-bridge' ), + 'SP' => __( 'La Spezia', 'forms-bridge' ), + 'SR' => __( 'Siracusa', 'forms-bridge' ), + 'SS' => __( 'Sassari', 'forms-bridge' ), + 'SU' => __( 'Sud Sardegna', 'forms-bridge' ), + 'SV' => __( 'Savona', 'forms-bridge' ), + 'TA' => __( 'Taranto', 'forms-bridge' ), + 'TE' => __( 'Teramo', 'forms-bridge' ), + 'TN' => __( 'Trento', 'forms-bridge' ), + 'TO' => __( 'Torino', 'forms-bridge' ), + 'TP' => __( 'Trapani', 'forms-bridge' ), + 'TR' => __( 'Terni', 'forms-bridge' ), + 'TS' => __( 'Trieste', 'forms-bridge' ), + 'TV' => __( 'Treviso', 'forms-bridge' ), + 'UD' => __( 'Udine', 'forms-bridge' ), + 'VA' => __( 'Varese', 'forms-bridge' ), + 'VB' => __( 'Verbano-Cusio-Ossola', 'forms-bridge' ), + 'VC' => __( 'Vercelli', 'forms-bridge' ), + 'VE' => __( 'Venezia', 'forms-bridge' ), + 'VI' => __( 'Vicenza', 'forms-bridge' ), + 'VR' => __( 'Verona', 'forms-bridge' ), + 'VS' => __( 'Medio Campidano', 'forms-bridge' ), + 'VT' => __( 'Viterbo', 'forms-bridge' ), + 'VV' => __( 'Vibo Valentia', 'forms-bridge' ), +); + +global $forms_bridge_odoo_jp_states; +$forms_bridge_odoo_jp_states = array( + '10' => __( 'Gunma', 'forms-bridge' ), + '11' => __( 'Saitama', 'forms-bridge' ), + '12' => __( 'Chiba', 'forms-bridge' ), + '13' => __( 'Tokyo', 'forms-bridge' ), + '14' => __( 'Kanagawa', 'forms-bridge' ), + '15' => __( 'Niigata', 'forms-bridge' ), + '16' => __( 'Toyama', 'forms-bridge' ), + '17' => __( 'Ishikawa', 'forms-bridge' ), + '18' => __( 'Fukui', 'forms-bridge' ), + '19' => __( 'Yamanashi', 'forms-bridge' ), + '20' => __( 'Nagano', 'forms-bridge' ), + '21' => __( 'Gifu', 'forms-bridge' ), + '22' => __( 'Shizuoka', 'forms-bridge' ), + '23' => __( 'Aichi', 'forms-bridge' ), + '24' => __( 'Mie', 'forms-bridge' ), + '25' => __( 'Shiga', 'forms-bridge' ), + '26' => __( 'Kyoto', 'forms-bridge' ), + '27' => __( 'Osaka', 'forms-bridge' ), + '28' => __( 'Hyogo', 'forms-bridge' ), + '29' => __( 'Nara', 'forms-bridge' ), + '30' => __( 'Wakayama', 'forms-bridge' ), + '31' => __( 'Tottori', 'forms-bridge' ), + '32' => __( 'Shimane', 'forms-bridge' ), + '33' => __( 'Okayama', 'forms-bridge' ), + '34' => __( 'Hiroshima', 'forms-bridge' ), + '35' => __( 'Yamaguchi', 'forms-bridge' ), + '36' => __( 'Tokushima', 'forms-bridge' ), + '37' => __( 'Kagawa', 'forms-bridge' ), + '38' => __( 'Ehime', 'forms-bridge' ), + '39' => __( 'Kochi', 'forms-bridge' ), + '40' => __( 'Fukuoka', 'forms-bridge' ), + '41' => __( 'Saga', 'forms-bridge' ), + '42' => __( 'Nagasaki', 'forms-bridge' ), + '43' => __( 'Kumamoto', 'forms-bridge' ), + '44' => __( 'Oita', 'forms-bridge' ), + '45' => __( 'Miyazaki', 'forms-bridge' ), + '46' => __( 'Kagoshima', 'forms-bridge' ), + '47' => __( 'Okinawa', 'forms-bridge' ), + '01' => __( 'Hokkaido', 'forms-bridge' ), + '02' => __( 'Aomori', 'forms-bridge' ), + '03' => __( 'Iwate', 'forms-bridge' ), + '04' => __( 'Miyagi', 'forms-bridge' ), + '05' => __( 'Akita', 'forms-bridge' ), + '06' => __( 'Yamagata', 'forms-bridge' ), + '07' => __( 'Fukushima', 'forms-bridge' ), + '08' => __( 'Ibaraki', 'forms-bridge' ), + '09' => __( 'Tochigi', 'forms-bridge' ), +); + +global $forms_bridge_odoo_mn_states; +$forms_bridge_odoo_mn_states = array( + '10' => __( 'Өвөрхангай', 'forms-bridge' ), + '11' => __( 'Өмнөговь', 'forms-bridge' ), + '12' => __( 'Сүхбаатар', 'forms-bridge' ), + '13' => __( 'Сэлэнгэ', 'forms-bridge' ), + '14' => __( 'Төв', 'forms-bridge' ), + '15' => __( 'Увс', 'forms-bridge' ), + '16' => __( 'Ховд', 'forms-bridge' ), + '17' => __( 'Хөвсгөл', 'forms-bridge' ), + '18' => __( 'Хэнтий', 'forms-bridge' ), + '19' => __( 'Дархан-Уул', 'forms-bridge' ), + '20' => __( 'Орхон', 'forms-bridge' ), + '23' => __( 'УБ - Хан Уул', 'forms-bridge' ), + '24' => __( 'УБ - Баянзүрх', 'forms-bridge' ), + '25' => __( 'УБ - Сүхбаатар', 'forms-bridge' ), + '26' => __( 'УБ - Баянгол', 'forms-bridge' ), + '27' => __( 'УБ - Багануур', 'forms-bridge' ), + '28' => __( 'УБ - Багахангай', 'forms-bridge' ), + '29' => __( 'УБ - Налайх', 'forms-bridge' ), + '32' => __( 'Говьсүмбэр', 'forms-bridge' ), + '34' => __( 'УБ - Сонгино Хайрхан', 'forms-bridge' ), + '35' => __( 'УБ - Чингэлтэй', 'forms-bridge' ), + '01' => __( 'Архангай', 'forms-bridge' ), + '02' => __( 'Баян-Өлгий', 'forms-bridge' ), + '03' => __( 'Баянхонгор', 'forms-bridge' ), + '04' => __( 'Булган', 'forms-bridge' ), + '05' => __( 'Говь-Алтай', 'forms-bridge' ), + '06' => __( 'Дорноговь', 'forms-bridge' ), + '07' => __( 'Дорнод', 'forms-bridge' ), + '08' => __( 'Дундговь', 'forms-bridge' ), + '09' => __( 'Завхан', 'forms-bridge' ), +); + +global $forms_bridge_odoo_mx_states; +$forms_bridge_odoo_mx_states = array( + 'AGU' => __( 'Aguascalientes', 'forms-bridge' ), + 'BCN' => __( 'Baja California', 'forms-bridge' ), + 'BCS' => __( 'Baja California Sur', 'forms-bridge' ), + 'CAM' => __( 'Campeche', 'forms-bridge' ), + 'CHH' => __( 'Chihuahua', 'forms-bridge' ), + 'CHP' => __( 'Chiapas', 'forms-bridge' ), + 'COA' => __( 'Coahuila', 'forms-bridge' ), + 'COL' => __( 'Colima', 'forms-bridge' ), + 'DIF' => __( 'Ciudad de México', 'forms-bridge' ), + 'DUR' => __( 'Durango', 'forms-bridge' ), + 'GRO' => __( 'Guerrero', 'forms-bridge' ), + 'GUA' => __( 'Guanajuato', 'forms-bridge' ), + 'HID' => __( 'Hidalgo', 'forms-bridge' ), + 'JAL' => __( 'Jalisco', 'forms-bridge' ), + 'MEX' => __( 'México', 'forms-bridge' ), + 'MIC' => __( 'Michoacán', 'forms-bridge' ), + 'MOR' => __( 'Morelos', 'forms-bridge' ), + 'NAY' => __( 'Nayarit', 'forms-bridge' ), + 'NLE' => __( 'Nuevo León', 'forms-bridge' ), + 'OAX' => __( 'Oaxaca', 'forms-bridge' ), + 'PUE' => __( 'Puebla', 'forms-bridge' ), + 'QUE' => __( 'Querétaro', 'forms-bridge' ), + 'ROO' => __( 'Quintana Roo', 'forms-bridge' ), + 'SIN' => __( 'Sinaloa', 'forms-bridge' ), + 'SLP' => __( 'San Luis Potosí', 'forms-bridge' ), + 'SON' => __( 'Sonora', 'forms-bridge' ), + 'TAB' => __( 'Tabasco', 'forms-bridge' ), + 'TAM' => __( 'Tamaulipas', 'forms-bridge' ), + 'TLA' => __( 'Tlaxcala', 'forms-bridge' ), + 'VER' => __( 'Veracruz', 'forms-bridge' ), + 'YUC' => __( 'Yucatán', 'forms-bridge' ), + 'ZAC' => __( 'Zacatecas', 'forms-bridge' ), +); + +global $forms_bridge_odoo_my_states; +$forms_bridge_odoo_my_states = array( + 'JHR' => __( 'Johor', 'forms-bridge' ), + 'KDH' => __( 'Kedah', 'forms-bridge' ), + 'KTN' => __( 'Kelantan', 'forms-bridge' ), + 'KUL' => __( 'Kuala Lumpur', 'forms-bridge' ), + 'LBN' => __( 'Labuan', 'forms-bridge' ), + 'MLK' => __( 'Melaka', 'forms-bridge' ), + 'NSN' => __( 'Negeri Sembilan', 'forms-bridge' ), + 'PHG' => __( 'Pahang', 'forms-bridge' ), + 'PJY' => __( 'Putrajaya', 'forms-bridge' ), + 'PLS' => __( 'Perlis', 'forms-bridge' ), + 'PNG' => __( 'Pulau Pinang', 'forms-bridge' ), + 'PRK' => __( 'Perak', 'forms-bridge' ), + 'SBH' => __( 'Sabah', 'forms-bridge' ), + 'SGR' => __( 'Selangor', 'forms-bridge' ), + 'SWK' => __( 'Sarawak', 'forms-bridge' ), + 'TRG' => __( 'Terengganu', 'forms-bridge' ), +); + +global $forms_bridge_odoo_nz_states; +$forms_bridge_odoo_nz_states = array( + 'AUK' => __( 'Auckland', 'forms-bridge' ), + 'BOP' => __( 'Bay of Plenty', 'forms-bridge' ), + 'CAN' => __( 'Canterbury', 'forms-bridge' ), + 'GIS' => __( 'Gisborne', 'forms-bridge' ), + 'HKB' => __( 'Hawke\'s Bay', 'forms-bridge' ), + 'MBH' => __( 'Marlborough', 'forms-bridge' ), + 'MWT' => __( 'Manawatu-Wanganui', 'forms-bridge' ), + 'NSN' => __( 'Nelson', 'forms-bridge' ), + 'NTL' => __( 'Northland', 'forms-bridge' ), + 'OTA' => __( 'Otago', 'forms-bridge' ), + 'STL' => __( 'Southland', 'forms-bridge' ), + 'TAS' => __( 'Tasman', 'forms-bridge' ), + 'TKI' => __( 'Taranaki', 'forms-bridge' ), + 'WGN' => __( 'Wellington', 'forms-bridge' ), + 'WKO' => __( 'Waikato', 'forms-bridge' ), + 'WTC' => __( 'West Coast', 'forms-bridge' ), +); + +global $forms_bridge_odoo_pt_states; +$forms_bridge_odoo_pt_states = array( + '10' => __( 'Leiria', 'forms-bridge' ), + '11' => __( 'Lisboa', 'forms-bridge' ), + '12' => __( 'Portalegre', 'forms-bridge' ), + '13' => __( 'Porto', 'forms-bridge' ), + '14' => __( 'Santarém', 'forms-bridge' ), + '15' => __( 'Setúbal', 'forms-bridge' ), + '16' => __( 'Viana do Castelo', 'forms-bridge' ), + '17' => __( 'Vila Real', 'forms-bridge' ), + '18' => __( 'Viseu', 'forms-bridge' ), + '20' => __( 'Açores', 'forms-bridge' ), + '30' => __( 'Madeira', 'forms-bridge' ), + '01' => __( 'Aveiro', 'forms-bridge' ), + '02' => __( 'Beja', 'forms-bridge' ), + '03' => __( 'Braga', 'forms-bridge' ), + '04' => __( 'Bragança', 'forms-bridge' ), + '05' => __( 'Castelo Branco', 'forms-bridge' ), + '06' => __( 'Coimbra', 'forms-bridge' ), + '07' => __( 'Évora', 'forms-bridge' ), + '08' => __( 'Faro', 'forms-bridge' ), + '09' => __( 'Guarda', 'forms-bridge' ), +); + +global $forms_bridge_odoo_ru_states; +$forms_bridge_odoo_ru_states = array( + 'AD' => __( 'Republic of Adygeya', 'forms-bridge' ), + 'AL' => __( 'Altai Republic', 'forms-bridge' ), + 'ALT' => __( 'Altai Krai', 'forms-bridge' ), + 'AMU' => __( 'Amur Oblast', 'forms-bridge' ), + 'ARK' => __( 'Arkhangelsk Oblast', 'forms-bridge' ), + 'AST' => __( 'Astrakhan Oblast', 'forms-bridge' ), + 'BA' => __( 'Republic of Bashkortostan', 'forms-bridge' ), + 'BEL' => __( 'Belgorod Oblast', 'forms-bridge' ), + 'BRY' => __( 'Bryansk Oblast', 'forms-bridge' ), + 'BU' => __( 'Republic of Buryatia', 'forms-bridge' ), + 'CE' => __( 'Chechen Republic', 'forms-bridge' ), + 'CHE' => __( 'Chelyabinsk Oblast', 'forms-bridge' ), + 'CHU' => __( 'Chukotka Autonomous Okrug', 'forms-bridge' ), + 'CU' => __( 'Chuvash Republic', 'forms-bridge' ), + 'DA' => __( 'Republic of Dagestan', 'forms-bridge' ), + 'IN' => __( 'Republic of Ingushetia', 'forms-bridge' ), + 'IRK' => __( 'Irkutsk Oblast', 'forms-bridge' ), + 'IVA' => __( 'Ivanovo Oblast', 'forms-bridge' ), + 'KAM' => __( 'Kamchatka Krai', 'forms-bridge' ), + 'KB' => __( 'Kabardino-Balkarian Republic', 'forms-bridge' ), + 'KC' => __( 'Karachay–Cherkess Republic', 'forms-bridge' ), + 'KDA' => __( 'Krasnodar Krai', 'forms-bridge' ), + 'KEM' => __( 'Kemerovo Oblast', 'forms-bridge' ), + 'KGD' => __( 'Kaliningrad Oblast', 'forms-bridge' ), + 'KGN' => __( 'Kurgan Oblast', 'forms-bridge' ), + 'KHA' => __( 'Khabarovsk Krai', 'forms-bridge' ), + 'KHM' => __( 'Khanty-Mansi Autonomous Okrug', 'forms-bridge' ), + 'KIR' => __( 'Kirov Oblast', 'forms-bridge' ), + 'KK' => __( 'Republic of Khakassia', 'forms-bridge' ), + 'KL' => __( 'Republic of Kalmykia', 'forms-bridge' ), + 'KLU' => __( 'Kaluga Oblast', 'forms-bridge' ), + 'KO' => __( 'Komi Republic', 'forms-bridge' ), + 'KOS' => __( 'Kostroma Oblast', 'forms-bridge' ), + 'KR' => __( 'Republic of Karelia', 'forms-bridge' ), + 'KRS' => __( 'Kursk Oblast', 'forms-bridge' ), + 'KYA' => __( 'Krasnoyarsk Krai', 'forms-bridge' ), + 'LEN' => __( 'Leningrad Oblast', 'forms-bridge' ), + 'LIP' => __( 'Lipetsk Oblast', 'forms-bridge' ), + 'MAG' => __( 'Magadan Oblast', 'forms-bridge' ), + 'ME' => __( 'Mari El Republic', 'forms-bridge' ), + 'MO' => __( 'Republic of Mordovia', 'forms-bridge' ), + 'MOS' => __( 'Moscow Oblast', 'forms-bridge' ), + 'MOW' => __( 'Moscow', 'forms-bridge' ), + 'MUR' => __( 'Murmansk Oblast', 'forms-bridge' ), + 'NGR' => __( 'Novgorod Oblast', 'forms-bridge' ), + 'NIZ' => __( 'Nizhny Novgorod Oblast', 'forms-bridge' ), + 'NVS' => __( 'Novosibirsk Oblast', 'forms-bridge' ), + 'OMS' => __( 'Omsk Oblast', 'forms-bridge' ), + 'ORE' => __( 'Orenburg Oblast', 'forms-bridge' ), + 'ORL' => __( 'Oryol Oblast', 'forms-bridge' ), + 'PER' => __( 'Perm Krai', 'forms-bridge' ), + 'PNZ' => __( 'Penza Oblast', 'forms-bridge' ), + 'PRI' => __( 'Primorsky Krai', 'forms-bridge' ), + 'PSK' => __( 'Pskov Oblast', 'forms-bridge' ), + 'ROS' => __( 'Rostov Oblast', 'forms-bridge' ), + 'RYA' => __( 'Ryazan Oblast', 'forms-bridge' ), + 'SA' => __( 'Sakha Republic (Yakutia)', 'forms-bridge' ), + 'SAK' => __( 'Sakhalin Oblast', 'forms-bridge' ), + 'SAM' => __( 'Samara Oblast', 'forms-bridge' ), + 'SAR' => __( 'Saratov Oblast', 'forms-bridge' ), + 'SE' => __( 'Republic of North Ossetia–Alania', 'forms-bridge' ), + 'SMO' => __( 'Smolensk Oblast', 'forms-bridge' ), + 'SPE' => __( 'Saint Petersburg', 'forms-bridge' ), + 'STA' => __( 'Stavropol Krai', 'forms-bridge' ), + 'SVE' => __( 'Sverdlovsk Oblast', 'forms-bridge' ), + 'TA' => __( 'Republic of Tatarstan', 'forms-bridge' ), + 'TAM' => __( 'Tambov Oblast', 'forms-bridge' ), + 'TOM' => __( 'Tomsk Oblast', 'forms-bridge' ), + 'TUL' => __( 'Tula Oblast', 'forms-bridge' ), + 'TVE' => __( 'Tver Oblast', 'forms-bridge' ), + 'TY' => __( 'Tyva Republic', 'forms-bridge' ), + 'TYU' => __( 'Tyumen Oblast', 'forms-bridge' ), + 'UD' => __( 'Udmurtia', 'forms-bridge' ), + 'ULY' => __( 'Ulyanovsk Oblast', 'forms-bridge' ), + 'VGG' => __( 'Volgograd Oblast', 'forms-bridge' ), + 'VLA' => __( 'Vladimir Oblast', 'forms-bridge' ), + 'VLG' => __( 'Vologda Oblast', 'forms-bridge' ), + 'VOR' => __( 'Voronezh Oblast', 'forms-bridge' ), + 'YAN' => __( 'Yamalo-Nenets Autonomous Okrug', 'forms-bridge' ), + 'YAR' => __( 'Yaroslavl Oblast', 'forms-bridge' ), + 'YEV' => __( 'Jewish Autonomous Oblast', 'forms-bridge' ), +); + +global $forms_bridge_odoo_us_states; +$forms_bridge_odoo_us_states = array( + 'AA' => __( 'Armed Forces Americas', 'forms-bridge' ), + 'AE' => __( 'Armed Forces Europe', 'forms-bridge' ), + 'AK' => __( 'Alaska', 'forms-bridge' ), + 'AL' => __( 'Alabama', 'forms-bridge' ), + 'AP' => __( 'Armed Forces Pacific', 'forms-bridge' ), + 'AR' => __( 'Arkansas', 'forms-bridge' ), + 'AS' => __( 'American Samoa', 'forms-bridge' ), + 'AZ' => __( 'Arizona', 'forms-bridge' ), + 'CA' => __( 'California', 'forms-bridge' ), + 'CO' => __( 'Colorado', 'forms-bridge' ), + 'CT' => __( 'Connecticut', 'forms-bridge' ), + 'DC' => __( 'District of Columbia', 'forms-bridge' ), + 'DE' => __( 'Delaware', 'forms-bridge' ), + 'FL' => __( 'Florida', 'forms-bridge' ), + 'FM' => __( 'Federated States of Micronesia', 'forms-bridge' ), + 'GA' => __( 'Georgia', 'forms-bridge' ), + 'GU' => __( 'Guam', 'forms-bridge' ), + 'HI' => __( 'Hawaii', 'forms-bridge' ), + 'IA' => __( 'Iowa', 'forms-bridge' ), + 'ID' => __( 'Idaho', 'forms-bridge' ), + 'IL' => __( 'Illinois', 'forms-bridge' ), + 'IN' => __( 'Indiana', 'forms-bridge' ), + 'KS' => __( 'Kansas', 'forms-bridge' ), + 'KY' => __( 'Kentucky', 'forms-bridge' ), + 'LA' => __( 'Louisiana', 'forms-bridge' ), + 'MA' => __( 'Massachusetts', 'forms-bridge' ), + 'MD' => __( 'Maryland', 'forms-bridge' ), + 'ME' => __( 'Maine', 'forms-bridge' ), + 'MH' => __( 'Marshall Islands', 'forms-bridge' ), + 'MI' => __( 'Michigan', 'forms-bridge' ), + 'MN' => __( 'Minnesota', 'forms-bridge' ), + 'MO' => __( 'Missouri', 'forms-bridge' ), + 'MP' => __( 'Northern Mariana Islands', 'forms-bridge' ), + 'MS' => __( 'Mississippi', 'forms-bridge' ), + 'MT' => __( 'Montana', 'forms-bridge' ), + 'NC' => __( 'North Carolina', 'forms-bridge' ), + 'ND' => __( 'North Dakota', 'forms-bridge' ), + 'NE' => __( 'Nebraska', 'forms-bridge' ), + 'NH' => __( 'New Hampshire', 'forms-bridge' ), + 'NJ' => __( 'New Jersey', 'forms-bridge' ), + 'NM' => __( 'New Mexico', 'forms-bridge' ), + 'NV' => __( 'Nevada', 'forms-bridge' ), + 'NY' => __( 'New York', 'forms-bridge' ), + 'OH' => __( 'Ohio', 'forms-bridge' ), + 'OK' => __( 'Oklahoma', 'forms-bridge' ), + 'OR' => __( 'Oregon', 'forms-bridge' ), + 'PA' => __( 'Pennsylvania', 'forms-bridge' ), + 'PR' => __( 'Puerto Rico', 'forms-bridge' ), + 'PW' => __( 'Palau', 'forms-bridge' ), + 'RI' => __( 'Rhode Island', 'forms-bridge' ), + 'SC' => __( 'South Carolina', 'forms-bridge' ), + 'SD' => __( 'South Dakota', 'forms-bridge' ), + 'TN' => __( 'Tennessee', 'forms-bridge' ), + 'TX' => __( 'Texas', 'forms-bridge' ), + 'UT' => __( 'Utah', 'forms-bridge' ), + 'VA' => __( 'Virginia', 'forms-bridge' ), + 'VI' => __( 'Virgin Islands', 'forms-bridge' ), + 'VT' => __( 'Vermont', 'forms-bridge' ), + 'WA' => __( 'Washington', 'forms-bridge' ), + 'WI' => __( 'Wisconsin', 'forms-bridge' ), + 'WV' => __( 'West Virginia', 'forms-bridge' ), + 'WY' => __( 'Wyoming', 'forms-bridge' ), +); + +global $forms_bridge_odoo_za_states; +$forms_bridge_odoo_za_states = array( + 'EC' => __( 'Eastern Cape', 'forms-bridge' ), + 'FS' => __( 'Free State', 'forms-bridge' ), + 'GT' => __( 'Gauteng', 'forms-bridge' ), + 'LP' => __( 'Limpopo', 'forms-bridge' ), + 'MP' => __( 'Mpumalanga', 'forms-bridge' ), + 'NC' => __( 'Northern Cape', 'forms-bridge' ), + 'NL' => __( 'KwaZulu-Natal', 'forms-bridge' ), + 'NW' => __( 'North West', 'forms-bridge' ), + 'WC' => __( 'Western Cape', 'forms-bridge' ), +); diff --git a/forms-bridge/addons/odoo/hooks.php b/forms-bridge/addons/odoo/hooks.php new file mode 100644 index 00000000..49cc450c --- /dev/null +++ b/forms-bridge/addons/odoo/hooks.php @@ -0,0 +1,268 @@ + array( + array( + 'ref' => '#credential', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'schema', + 'type' => 'text', + 'value' => 'RPC', + ), + array( + 'ref' => '#credential', + 'name' => 'database', + 'label' => __( 'Database', 'forms-bridge' ), + 'description' => __( + 'Name of the database', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_id', + 'label' => __( 'User', 'forms-bridge' ), + 'description' => __( + 'User name or email', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_secret', + 'description' => __( 'User password', 'forms-bridge' ), + 'label' => __( 'Password', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#backend', + 'name' => 'name', + 'default' => 'Odoo', + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'label' => __( 'Model', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'label' => __( 'Method', 'forms-bridge' ), + 'type' => 'text', + 'value' => 'create', + 'required' => true, + ), + ), + 'bridge' => array( + 'name' => '', + 'form_id' => '', + 'backend' => '', + 'endpoint' => '', + ), + 'backend' => array( + 'name' => 'Odoo', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + 'credential' => array( + 'name' => '', + 'schema' => 'RPC', + 'client_id' => '', + 'client_secret' => '', + 'database' => '', + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'odoo-' ) !== 0 ) { + return $data; + } + + $index = array_search( + 'tag_ids', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = $data['bridge']['custom_fields'][ $index ]; + $tags = $field['value'] ?? array(); + + for ( $i = 0; $i < count( $tags ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "tag_ids[{$i}]", + 'value' => $tags[ $i ], + ); + + $data['bridge']['mutations'][0][] = array( + 'from' => "tag_ids[{$i}]", + 'to' => "tag_ids[{$i}]", + 'cast' => 'integer', + ); + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'categ_ids', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = $data['bridge']['custom_fields'][ $index ]; + $tags = $field['value'] ?? array(); + + for ( $i = 0; $i < count( $tags ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "categ_ids[{$i}]", + 'value' => $tags[ $i ], + ); + + $data['bridge']['mutations'][0][] = array( + 'from' => "categ_ids[{$i}]", + 'to' => "categ_ids[{$i}]", + 'cast' => 'integer', + ); + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'list_ids', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $field = $data['bridge']['custom_fields'][ $index ]; + $lists = $field['value'] ?? array(); + + for ( $i = 0; $i < count( $lists ); $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "list_ids[{$i}]", + 'value' => $lists[ $i ], + ); + + $data['bridge']['mutations'][0][] = array( + 'from' => "list_ids[{$i}]", + 'to' => "list_ids[{$i}]", + 'cast' => 'integer', + ); + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'allday', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + $data['form']['fields'] = array_filter( + $data['form']['fields'], + function ( $field ) { + return ! in_array( + $field['name'], + array( + 'hour', + 'minute', + __( 'Hour', 'forms-bridge' ), + __( 'Minute', 'forms-bridge' ), + ), + true + ); + } + ); + + $index = array_search( + 'duration', + array_column( $data['bridge']['custom_fields'], 'name' ) + ); + + if ( $index !== false ) { + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/odoo/jobs/appointment-attendee.php b/forms-bridge/addons/odoo/jobs/appointment-attendee.php new file mode 100644 index 00000000..214e4bdc --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/appointment-attendee.php @@ -0,0 +1,135 @@ +patch( + array( + 'name' => 'odoo-get-user-by-id', + 'endpoint' => 'res.users', + 'method' => 'read', + ) + ) + ->submit( array( $payload['user_id'] ) ); + + if ( is_wp_error( $user_response ) ) { + return $user_response; + } + + $payload['partner_ids'][] = + $user_response['data']['result'][0]['partner_id'][0]; + } + + return $payload; +} + +return array( + 'title' => __( 'Appointment attendees', 'forms-bridge' ), + 'description' => __( + 'Search for partner by email or creates a new one and sets it as the appointment attendee. If user_id, also adds user as attendee.', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_appointment_attendees', + 'input' => array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'title', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'lang', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'employee', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'function', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'website', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street2', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'city', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'parent_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'additional_info', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'is_public', + 'schema' => array( 'type' => 'boolean' ), + ), + ), + 'output' => array( + array( + 'name' => 'user_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'partner_ids', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + 'additionalItems' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/appointment-dates.php b/forms-bridge/addons/odoo/jobs/appointment-dates.php new file mode 100644 index 00000000..7c2c7d35 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/appointment-dates.php @@ -0,0 +1,56 @@ +getTimestamp(); + + $duration = floatval( $payload['duration'] ?? 1 ); + + $payload['start'] = date( 'Y-m-d H:i:s', $timestamp ); + + $end = $duration * 3600 + $timestamp; + $payload['stop'] = date( 'Y-m-d H:i:s', $end ); + + return $payload; +} + +return array( + 'title' => __( 'Appointment dates', 'forms-bridge' ), + 'description' => __( + 'Sets appointment start and stop time from "timestamp" and "duration" fields.', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_appointment_dates', + 'input' => array( + array( + 'name' => 'date', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'duration', + 'schema' => array( 'type' => 'number' ), + ), + ), + 'output' => array( + array( + 'name' => 'start', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'stop', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/attachments.php b/forms-bridge/addons/odoo/jobs/attachments.php new file mode 100644 index 00000000..5c1586e8 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/attachments.php @@ -0,0 +1,93 @@ + __( 'Attachments', 'forms-bridge' ), + 'description' => __( + 'Gets submission uploads and sets them as attached files to the newly created model', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_bridge_attachments', + 'input' => array(), + 'output' => array(), +); + +function forms_bridge_odoo_bridge_attachments( $payload, $bridge ) { + if ( ! in_array( $bridge->method, array( 'write', 'create' ), true ) ) { + return $payload; + } + + $uploads = FBAPI::get_uploads(); + + if ( empty( $uploads ) ) { + return $payload; + } + + $attachments = array(); + + foreach ( $uploads as $name => $upload ) { + if ( $upload['is_multi'] ) { + $len = count( $uploads[ $name ]['path'] ); + for ( $i = 1; $i <= $len; ++$i ) { + $attachments[ $name . '_' . $i ] = $upload['path'][ $i - 1 ]; + } + } else { + $attachments[ $name ] = $upload['path']; + } + } + + add_action( + 'forms_bridge_after_submission', + function ( $bridge, $response ) use ( $payload, $attachments ) { + $res_id = $response['data']['result']; + $res_model = $bridge->endpoint; + + foreach ( $attachments as $filename => $path ) { + $content_field = $filename; + $name_field = $filename . '_filename'; + $mimetype = mime_content_type( $path ); + + if ( ! isset( $payload[ $content_field ], $payload[ $name_field ] ) ) { + continue; + } + + $response = $bridge + ->patch( + array( + 'name' => '__odoo-ir-attachments', + 'endpoint' => 'ir.attachment', + 'method' => 'create', + ) + ) + ->submit( + array( + 'name' => $payload[ $name_field ], + 'datas' => $payload[ $content_field ], + 'res_id' => $res_id, + 'res_model' => $res_model, + 'mimetype' => $mimetype, + ) + ); + + if ( is_wp_error( $response ) ) { + do_action( + 'forms_bridge_on_failure', + $response, + $bridge, + $payload, + $attachments + ); + + return; + } + } + }, + 20, + 2 + ); + + return $payload; +} diff --git a/forms-bridge/addons/odoo/jobs/candidate.php b/forms-bridge/addons/odoo/jobs/candidate.php new file mode 100644 index 00000000..7cdf73ce --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/candidate.php @@ -0,0 +1,105 @@ + __( 'Candidate', 'forms-bridge' ), + 'description' => __( + 'Creates a recruitement candidate and sets its ID as the candidate_id field of the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_create_candidate', + 'input' => array( + array( + 'name' => 'partner_name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'partner_phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email_from', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'user_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'type_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'partner_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), + 'output' => array( + array( + 'name' => 'candidate_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); + +function forms_bridge_odoo_create_candidate( $payload, $bridge ) { + $candidate = array( + 'partner_name' => $payload['partner_name'], + ); + + $fields = array( + 'partner_id', + 'partner_phone', + 'email_from', + 'user_id', + 'type_id', + ); + + foreach ( $fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $candidate[ $field ] = $payload[ $field ]; + } + } + + $query = array( array( 'partner_name', '=', $candidate['partner_name'] ) ); + + if ( isset( $candidate['email_from'] ) ) { + $query[] = array( 'email_from', '=', $candidate['email_from'] ); + } + + $response = $bridge + ->patch( + array( + 'name' => '__odoo-search-candidate', + 'endpoint' => 'hr.candidate', + 'method' => 'search', + ) + ) + ->submit( $query ); + + if ( ! is_wp_error( $response ) ) { + $payload['candidate_id'] = (int) $response['data']['result'][0]; + return $payload; + } + + $response = $bridge + ->patch( + array( + 'name' => '__odoo-create-candidate', + 'endpoint' => 'hr.candidate', + ) + ) + ->submit( $candidate ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $payload['candidate_id'] = (int) $response['data']['result']; + + return $payload; +} diff --git a/forms-bridge/addons/odoo/jobs/contact-company.php b/forms-bridge/addons/odoo/jobs/contact-company.php new file mode 100644 index 00000000..0b7070b7 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/contact-company.php @@ -0,0 +1,94 @@ + __( 'Contact\'s company', 'forms-bridge' ), + 'description' => __( + 'Creates a company and sets its ID as the parent_id of the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_contact_company', + 'input' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'lang', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'website', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street2', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'city', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'additional_info', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'is_public', + 'schema' => array( 'type' => 'boolean' ), + ), + ), + 'output' => array( + array( + 'name' => 'parent_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/contact.php b/forms-bridge/addons/odoo/jobs/contact.php new file mode 100644 index 00000000..9c2a0678 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/contact.php @@ -0,0 +1,106 @@ + __( 'Contact', 'forms-bridge' ), + 'description' => __( + 'Creates a contact and sets its ID as the partner_id field of the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_contact_id', + 'input' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'title', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'lang', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'website', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street2', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'city', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'additional_info', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'is_public', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'parent_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'function', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'partner_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/country-id.php b/forms-bridge/addons/odoo/jobs/country-id.php new file mode 100644 index 00000000..7837b2c6 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/country-id.php @@ -0,0 +1,61 @@ +patch( + array( + 'name' => 'odoo-get-country-id', + 'endpoint' => 'res.country', + 'method' => 'search', + ) + ) + ->submit( array( array( 'code', '=', $payload['country'] ) ) ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $payload['country_id'] = $response['data']['result'][0]; + return $payload; +} + +return array( + 'title' => __( 'Country ID from code', 'forms-bridge' ), + 'description' => __( + 'Given a iso2 code code gets the internal country ID', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_country_id_from_code', + 'input' => array( + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'country_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/crm-contact.php b/forms-bridge/addons/odoo/jobs/crm-contact.php new file mode 100644 index 00000000..cf70cf1a --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/crm-contact.php @@ -0,0 +1,118 @@ + __( 'CRM lead contact', 'forms-bridge' ), + 'description' => __( + 'Creates a new contact and sets its email as the email_from and its ID as the partner_id on the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_crm_lead_contact', + 'input' => array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'title', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'lang', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'employee', + 'schema' => array( 'type' => 'boolean' ), + ), + array( + 'name' => 'function', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'website', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street2', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'city', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'parent_id', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'additional_info', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'is_public', + 'schema' => array( 'type' => 'boolean' ), + ), + ), + 'output' => array( + array( + 'name' => 'email_from', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'partner_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/delivery-address.php b/forms-bridge/addons/odoo/jobs/delivery-address.php new file mode 100644 index 00000000..df85fd17 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/delivery-address.php @@ -0,0 +1,129 @@ + __( 'Shipping address', 'forms-bridge' ), + 'description' => __( + 'Creates a shipping address linked to a contact.', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_shipping_address', + 'input' => array( + array( + 'name' => 'partner_id', + 'schema' => array( 'type' => 'integer' ), + 'required' => true, + ), + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'street2', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'city', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'zip', + 'schema' => array( 'type' => 'string' ), + ), + // [ + // 'name' => 'state', + // 'schema' => ['type' => 'string'], + // ], + // [ + // 'name' => 'country', + // 'schema' => ['type' => 'string'], + // ], + array( + 'name' => 'comment', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array(), +); + +function forms_bridge_odoo_shipping_address( $payload, $bridge ) { + $query = array( + array( 'type', '=', 'delivery' ), + array( 'parent_id', '=', $payload['partner_id'] ), + array( 'name', '=', $payload['name'] ), + ); + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-search-address', + 'method' => 'search', + 'endpoint' => 'res.partner', + ) + ) + ->submit( $query ); + + if ( ! is_wp_error( $response ) ) { + return $payload; + } + + $address = array(); + foreach ( $query as $filter ) { + $address[ $filter[0] ] = $filter[2]; + } + + $address_fields = array( + 'email', + 'phone', + 'mobile', + 'street', + 'street2', + 'city', + 'zip', + // 'state', + // 'country', + 'comment', + ); + + foreach ( $address_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $address[ $field ] = $payload[ $field ]; + } + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-delivery-address', + 'endpoint' => 'res.partner', + 'method' => 'create', + ) + ) + ->submit( $address ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return $payload; +} diff --git a/forms-bridge/addons/odoo/jobs/mailing-contact.php b/forms-bridge/addons/odoo/jobs/mailing-contact.php new file mode 100644 index 00000000..9e219c9a --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/mailing-contact.php @@ -0,0 +1,73 @@ +patch( + array( + 'name' => 'odoo-search-mailing-contact-by-email', + 'template' => null, + 'method' => 'search', + 'endpoint' => 'mailing.contact', + ) + ) + ->submit( array( array( 'email', '=', $payload['email'] ) ) ); + + if ( ! is_wp_error( $response ) ) { + $contact_id = $response['data']['result'][0]; + $list_ids = $payload['list_ids']; + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-update-mailing-contact-subscriptions', + 'template' => null, + 'endpoint' => 'mailing.contact', + 'method' => 'write', + ) + ) + ->submit( array( $contact_id ), array( 'list_ids' => $list_ids ) ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return; + } + + return $payload; +} + +return array( + 'title' => __( 'Skip subscription', 'forms-bridge' ), + 'description' => __( + 'Search for a subscribed mailing contact, updates its subscriptions and skips if succeed', + 'forms-bridge' + ), + 'method' => 'forms_brige_odoo_update_mailing_contact', + 'input' => array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/products-by-ref.php b/forms-bridge/addons/odoo/jobs/products-by-ref.php new file mode 100644 index 00000000..a19d584e --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/products-by-ref.php @@ -0,0 +1,81 @@ + __( 'Products by reference', 'forms-bridge' ), + 'description' => __( + 'Search for products on Odoo based on a list of internal references and returns its IDs.', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_search_products_by_ref', + 'input' => array( + array( + 'name' => 'internal_refs', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'additionalItems' => true, + ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'product_ids', + 'schema' => array( + 'type' => 'array', + 'items' => array( 'type' => 'integer' ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_odoo_search_products_by_ref( $payload, $bridge ) { + $response = $bridge + ->patch( + array( + 'name' => 'odoo-search-products-by-ref', + 'endpoint' => 'product.product', + 'method' => 'search_read', + ) + ) + ->submit( + array( array( 'default_code', 'in', $payload['internal_refs'] ) ), + array( 'id', 'default_code' ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $product_ids = array(); + foreach ( $payload['internal_refs'] as $ref ) { + foreach ( $response['data']['result'] as $product ) { + if ( $product['default_code'] === $ref ) { + $product_ids[] = $product['id']; + break; + } + } + } + + if ( count( $product_ids ) !== count( $payload['internal_refs'] ) ) { + return new WP_Error( + 'product_search_error', + __( + 'Inconsistencies between amount of found products and search references', + 'forms-bridge' + ), + array( + 'response' => $response, + 'internal_refs' => $payload['internal_refs'], + ) + ); + } + + $payload['product_ids'] = $product_ids; + return $payload; +} diff --git a/forms-bridge/addons/odoo/jobs/skip-if-partner-exists.php b/forms-bridge/addons/odoo/jobs/skip-if-partner-exists.php new file mode 100644 index 00000000..736f7308 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/skip-if-partner-exists.php @@ -0,0 +1,90 @@ +patch( + array( + 'name' => 'odoo-search-contact-by-email', + 'method' => 'search', + 'endpoint' => 'res.partner', + ) + ) + ->submit( $query ); + + if ( ! is_wp_error( $response ) ) { + $partner_id = $response['data']['result'][0]; + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-update-contact', + 'method' => 'write', + 'endpoint' => 'res.partner', + ) + ) + ->submit( array( $partner_id ), $payload ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return; + } + + return $payload; +} + +return array( + 'title' => __( 'Skip if contact exists', 'forms-bridge' ), + 'description' => __( + 'Search contacts by name, email and vat and skip submission if it exists', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_skip_if_partner_exists', + 'input' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'name', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'name' ), + ), + array( + 'name' => 'email', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'email' ), + ), + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'vat' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/state-id.php b/forms-bridge/addons/odoo/jobs/state-id.php new file mode 100644 index 00000000..aa28638c --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/state-id.php @@ -0,0 +1,74 @@ +patch( + array( + 'endpoint' => 'res.country.state', + 'method' => 'search', + ) + ) + ->submit( array( array( 'code', '=', $payload['state_code'] ) ) ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $payload['state_id'] = $response['data']['result'][0]; + return $payload; +} + +return array( + 'title' => __( 'State ID from code', 'forms-bridge' ), + 'description' => __( + 'Given a iso2 country code code and a state odoo code gets the internal state ID', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_state_id_from_code', + 'input' => array( + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'state_code', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'country_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'state_code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'state_id', + 'schema' => array( 'type' => 'integer' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/jobs/sync-products-by-ref.php b/forms-bridge/addons/odoo/jobs/sync-products-by-ref.php new file mode 100644 index 00000000..c0339f96 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/sync-products-by-ref.php @@ -0,0 +1,149 @@ + __( 'Sync woo products', 'forms-bridge' ), + 'description' => __( + 'Search for products from the WooCommerce order by sku on Odoo and creates new ones if someone does not exists', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_sync_products_by_ref', + 'input' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + 'required' => array( 'sku', 'name', 'price' ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + 'required' => true, + ), + ), + 'output' => array( + array( + 'name' => 'line_items', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'product_id' => array( 'type' => 'integer' ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + ), + ), + 'additionalProperties' => true, + ), + 'additionalItems' => true, + ), + ), + ), +); + +function forms_bridge_odoo_sync_products_by_ref( $payload, $bridge ) { + $internal_refs = array(); + foreach ( $payload['line_items'] as $line_item ) { + if ( empty( $line_item['product']['sku'] ) ) { + return new WP_Error( + "SKU is required on product {$line_item['product']['name']}" + ); + } + + $internal_refs[] = $line_item['product']['sku']; + } + + $response = $bridge + ->patch( + array( + 'name' => 'odoo-search-products-by-ref', + 'endpoint' => 'product.product', + 'method' => 'search_read', + ) + ) + ->submit( + array( array( 'default_code', 'in', $internal_refs ) ), + array( 'id', 'default_code' ) + ); + + if ( is_wp_error( $response ) ) { + if ( $response->get_error_code() !== 'not_found' ) { + return $response; + } + + $response = $response->get_error_data()['response']; + $response['data']['result'] = array(); + } + + foreach ( $payload['line_items'] as $line_item ) { + $product = null; + foreach ( $response['data']['result'] as $candidate ) { + if ( $candidate['default_code'] === $line_item['product']['sku'] ) { + $product = $candidate; + break; + } + } + + if ( ! $product ) { + $product_response = $bridge + ->patch( + array( + 'name' => 'odoo-sync-product-by-ref', + 'endpoint' => 'product.product', + 'method' => 'create', + ) + ) + ->submit( + array( + 'name' => $line_item['product']['name'], + 'list_price' => $line_item['product']['price'], + 'default_code' => $line_item['product']['sku'], + 'sale_ok' => true, + 'purchase_ok' => false, + ) + ); + + if ( is_wp_error( $product_response ) ) { + return $product_response; + } + } + } + + return $payload; +} diff --git a/forms-bridge/addons/odoo/jobs/vat-id.php b/forms-bridge/addons/odoo/jobs/vat-id.php new file mode 100644 index 00000000..4df4ffc2 --- /dev/null +++ b/forms-bridge/addons/odoo/jobs/vat-id.php @@ -0,0 +1,83 @@ + __( 'Prefixed vat ID', 'forms-bridge' ), + 'description' => __( + 'Prefix the vat with country code, or the current locale, if it isn\'t prefixed', + 'forms-bridge' + ), + 'method' => 'forms_bridge_odoo_vat_id', + 'input' => array( + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'vat', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + 'requires' => array( 'country' ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/appointments.php b/forms-bridge/addons/odoo/templates/appointments.php new file mode 100644 index 00000000..52311060 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/appointments.php @@ -0,0 +1,313 @@ + __( 'Appointments', 'forms-bridge' ), + 'description' => __( + 'Appointments form template. The resulting bridge will convert form submissions into events on the calendar linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Appointments', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'calendar.event', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'user_id', + 'label' => __( 'Host', 'forms-bridge' ), + 'description' => __( + 'Name of the host user of the appointment', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'res.users', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'event_name', + 'label' => __( 'Appointment name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web Appointment', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'allday', + 'label' => __( 'Is all day event?', 'forms-bridge' ), + 'type' => 'boolean', + 'default' => false, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'duration', + 'label' => __( 'Duration (Hours)', 'forms-bridge' ), + 'type' => 'number', + 'default' => 1, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'categ_ids', + 'label' => __( 'Appointment tags', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'calendar.event.type', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + 'is_multi' => true, + ), + ), + 'bridge' => array( + 'endpoint' => 'calendar.event', + 'mutations' => array( + array( + array( + 'from' => '?user_id', + 'to' => 'user_id', + 'cast' => 'integer', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?allday', + 'to' => 'allday', + 'cast' => 'boolean', + ), + array( + 'from' => '?duration', + 'to' => 'duration', + 'cast' => 'number', + ), + ), + array( + array( + 'from' => 'datetime', + 'to' => 'date', + 'cast' => 'string', + ), + ), + array(), + array( + array( + 'from' => 'event_name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( + 'date-fields-to-date', + 'appointment-dates', + 'appointment-attendee', + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'your-name', + 'label' => __( 'Your name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'phone', + 'label' => __( 'Your phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'date', + 'label' => __( 'Date', 'forms-bridge' ), + 'type' => 'date', + 'required' => true, + ), + array( + 'name' => 'hour', + 'label' => __( 'Hour', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => __( '1 AM', 'forms-bridge' ), + 'value' => '01', + ), + array( + 'label' => __( '2 AM', 'forms-bridge' ), + 'value' => '02', + ), + array( + 'label' => __( '3 AM', 'forms-bridge' ), + 'value' => '03', + ), + array( + 'label' => __( '4 AM', 'forms-bridge' ), + 'value' => '04', + ), + array( + 'label' => __( '5 AM', 'forms-bridge' ), + 'value' => '05', + ), + array( + 'label' => __( '6 AM', 'forms-bridge' ), + 'value' => '06', + ), + array( + 'label' => __( '7 AM', 'forms-bridge' ), + 'value' => '07', + ), + array( + 'label' => __( '8 AM', 'forms-bridge' ), + 'value' => '08', + ), + array( + 'label' => __( '9 AM', 'forms-bridge' ), + 'value' => '09', + ), + array( + 'label' => __( '10 AM', 'forms-bridge' ), + 'value' => '10', + ), + array( + 'label' => __( '11 AM', 'forms-bridge' ), + 'value' => '11', + ), + array( + 'label' => __( '12 AM', 'forms-bridge' ), + 'value' => '12', + ), + array( + 'label' => __( '1 PM', 'forms-bridge' ), + 'value' => '13', + ), + array( + 'label' => __( '2 PM', 'forms-bridge' ), + 'value' => '14', + ), + array( + 'label' => __( '3 PM', 'forms-bridge' ), + 'value' => '15', + ), + array( + 'label' => __( '4 PM', 'forms-bridge' ), + 'value' => '16', + ), + array( + 'label' => __( '5 PM', 'forms-bridge' ), + 'value' => '17', + ), + array( + 'label' => __( '6 PM', 'forms-bridge' ), + 'value' => '18', + ), + array( + 'label' => __( '7 PM', 'forms-bridge' ), + 'value' => '19', + ), + array( + 'label' => __( '8 PM', 'forms-bridge' ), + 'value' => '20', + ), + array( + 'label' => __( '9 PM', 'forms-bridge' ), + 'value' => '21', + ), + array( + 'label' => __( '10 PM', 'forms-bridge' ), + 'value' => '22', + ), + array( + 'label' => __( '11 PM', 'forms-bridge' ), + 'value' => '23', + ), + array( + 'label' => __( '12 PM', 'forms-bridge' ), + 'value' => '24', + ), + ), + ), + array( + 'name' => 'minute', + 'label' => __( 'Minute', 'forms-bridge' ), + 'type' => 'select', + 'required' => true, + 'options' => array( + array( + 'label' => '00', + 'value' => '00.0', + ), + array( + 'label' => '05', + 'value' => '05', + ), + array( + 'label' => '10', + 'value' => '10', + ), + array( + 'label' => '15', + 'value' => '15', + ), + array( + 'label' => '20', + 'value' => '20', + ), + array( + 'label' => '25', + 'value' => '25', + ), + array( + 'label' => '30', + 'value' => '30', + ), + array( + 'label' => '35', + 'value' => '35', + ), + array( + 'label' => '40', + 'value' => '40', + ), + array( + 'label' => '45', + 'value' => '45', + ), + array( + 'label' => '50', + 'value' => '50', + ), + array( + 'label' => '55', + 'value' => '55', + ), + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/company-contacts.php b/forms-bridge/addons/odoo/templates/company-contacts.php new file mode 100644 index 00000000..269f3c69 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/company-contacts.php @@ -0,0 +1,151 @@ + __( 'Company Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form for companies template. The resulting bridge will convert form submissions into new companies linked to contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Contacts', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'res.partner', + ), + ), + 'bridge' => array( + 'endpoint' => 'res.partner', + 'workflow' => array( + 'iso2-country-code', + 'vat-id', + 'country-id', + 'contact-company', + 'skip-if-partner-exists', + ), + 'mutations' => array( + array( + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + array(), + array( + array( + 'from' => 'email', + 'to' => 'contact_email', + 'cast' => 'copy', + ), + array( + 'from' => 'phone', + 'to' => 'contact_phone', + 'cast' => 'copy', + ), + ), + array(), + array( + array( + 'from' => 'contact_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'contact_phone', + 'to' => 'phone', + 'cast' => 'string', + ), + ), + ), + array(), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Company name', 'forms-bridge' ), + 'name' => 'company_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'vat', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'street', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Job position', 'forms-bridge' ), + 'name' => 'function', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/company-quotations.php b/forms-bridge/addons/odoo/templates/company-quotations.php new file mode 100644 index 00000000..e58e9900 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/company-quotations.php @@ -0,0 +1,212 @@ + __( 'Company Quotations', 'forms-bridge' ), + 'description' => __( + 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Quotations', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'sale.order', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'product_id', + 'label' => __( 'Product', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'product.product', + 'finger' => array( + 'value' => 'result[].id', + 'label' => 'result[].name', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'endpoint' => 'sale.order', + 'custom_fields' => array( + array( + 'name' => 'state', + 'value' => 'draft', + ), + array( + 'name' => 'order_line[0][0]', + 'value' => '0', + ), + array( + 'name' => 'order_line[0][1]', + 'value' => '0', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'order_line[0][0]', + 'to' => 'order_line[0][0]', + 'cast' => 'integer', + ), + array( + 'from' => 'order_line[0][1]', + 'to' => 'order_line[0][1]', + 'cast' => 'integer', + ), + array( + 'from' => 'quantity', + 'to' => 'order_line[0][2].product_uom_qty', + 'cast' => 'integer', + ), + array( + 'from' => 'product_id', + 'to' => 'order_line[0][2].product_id', + 'cast' => 'integer', + ), + array( + 'from' => 'company-name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + array(), + array(), + array( + array( + 'from' => 'email', + 'to' => 'contact_email', + 'cast' => 'copy', + ), + array( + 'from' => 'phone', + 'to' => 'contact_phone', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'parent_id', + 'to' => 'company_partner_id', + 'cast' => 'copy', + ), + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'contact_phone', + 'to' => 'phone', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'company_partner_id', + 'to' => 'partner_id', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'iso2-country-code', + 'vat-id', + 'country-id', + 'contact-company', + 'contact', + ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Quantity', 'forms-bridge' ), + 'name' => 'quantity', + 'type' => 'number', + 'required' => true, + 'default' => 1, + 'min' => 1, + ), + array( + 'label' => __( 'Company', 'forms-bridge' ), + 'name' => 'company-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Vat ID', 'forms-bridge' ), + 'name' => 'vat', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'street', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/contacts.php b/forms-bridge/addons/odoo/templates/contacts.php new file mode 100644 index 00000000..42520017 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/contacts.php @@ -0,0 +1,108 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Contacts', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'res.partner', + ), + ), + 'bridge' => array( + 'endpoint' => 'res.partner', + 'custom_fields' => array( + array( + 'name' => 'is_company', + 'value' => '0', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'is_company', + 'to' => 'is_company', + 'cast' => 'boolean', + ), + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + array(), + ), + 'workflow' => array( + 'iso2-country-code', + 'country-id', + 'skip-if-partner-exists', + ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'street', + 'type' => 'text', + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip', + 'type' => 'text', + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/crm-company-leads.php b/forms-bridge/addons/odoo/templates/crm-company-leads.php new file mode 100644 index 00000000..c2b1b1df --- /dev/null +++ b/forms-bridge/addons/odoo/templates/crm-company-leads.php @@ -0,0 +1,234 @@ + __( 'CRM Company Leads', 'forms-bridge' ), + 'description' => __( + 'Leads form template. The resulting bridge will convert form submissions into leads linked to new companies.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'CRM Company Leads', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'crm.lead', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'user_id', + 'label' => __( 'Owner email', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the lead', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'res.users', + 'finger' => array( + 'value' => 'result[].id', + 'label' => 'result[].name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'lead_name', + 'label' => __( 'Lead name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web Lead', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'priority', + 'label' => __( 'Priority', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + 'max' => 3, + 'default' => 1, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'expected_revenue', + 'label' => __( 'Expected revenue', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + 'default' => 0, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tag_ids', + 'label' => __( 'Lead tags', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'crm.tag', + 'finger' => array( + 'value' => 'result[].id', + 'label' => 'result[].name', + ), + ), + 'is_multi' => true, + ), + ), + 'bridge' => array( + 'endpoint' => 'crm.lead', + 'mutations' => array( + array( + array( + 'from' => '?user_id', + 'to' => 'user_id', + 'cast' => 'integer', + ), + array( + 'from' => 'company_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?priority', + 'to' => 'priority', + 'cast' => 'string', + ), + array( + 'from' => '?expected_revenue', + 'to' => 'expected_revenue', + 'cast' => 'number', + ), + ), + array(), + array(), + array( + array( + 'from' => 'email', + 'to' => 'contact_email', + 'cast' => 'copy', + ), + array( + 'from' => 'phone', + 'to' => 'contact_phone', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'contact_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'contact_email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'contact_phone', + 'to' => 'phone', + 'cast' => 'string', + ), + ), + array( + array( + 'from' => 'lead_name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( + 'iso2-country-code', + 'vat-id', + 'country-id', + 'contact-company', + 'crm-contact', + ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Company name', 'forms-bridge' ), + 'name' => 'company_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Tax ID', 'forms-bridge' ), + 'name' => 'vat', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'street', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Job position', 'forms-bridge' ), + 'name' => 'function', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + ), + array( + 'label' => __( 'Comments', 'forms-bridge' ), + 'name' => 'description', + 'type' => 'textarea', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/crm-leads.php b/forms-bridge/addons/odoo/templates/crm-leads.php new file mode 100644 index 00000000..9dc50e04 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/crm-leads.php @@ -0,0 +1,143 @@ + __( 'CRM Leads', 'forms-bridge' ), + 'description' => __( + 'Lead form template. The resulting bridge will convert form submissions into leads linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'CRM Leads', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'crm.lead', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'user_id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Name of the owner user of the lead', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'res.users', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'lead_name', + 'label' => __( 'Lead name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web Lead', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'priority', + 'label' => __( 'Priority', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + 'max' => 3, + 'default' => 1, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'expected_revenue', + 'label' => __( 'Expected revenue', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + 'default' => 0, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tag_ids', + 'label' => __( 'Lead tags', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'crm.tag', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + 'is_multi' => true, + ), + ), + 'bridge' => array( + 'endpoint' => 'crm.lead', + 'mutations' => array( + array( + array( + 'from' => '?user_id', + 'to' => 'user_id', + 'cast' => 'integer', + ), + array( + 'from' => 'contact_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?priority', + 'to' => 'priority', + 'cast' => 'string', + ), + array( + 'from' => '?expected_revenue', + 'to' => 'expected_revenue', + 'cast' => 'number', + ), + ), + array( + array( + 'from' => 'lead_name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( 'crm-contact' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + ), + array( + 'label' => __( 'Comments', 'forms-bridge' ), + 'name' => 'description', + 'type' => 'textarea', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/crm-team-leads.php b/forms-bridge/addons/odoo/templates/crm-team-leads.php new file mode 100644 index 00000000..f6e06bb8 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/crm-team-leads.php @@ -0,0 +1,144 @@ + __( 'CRM Team Leads', 'forms-bridge' ), + 'description' => __( + 'Team lead form template. The resulting bridge will convert form submissions into leads linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'CRM Team Leads', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'crm.lead', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'team_id', + 'label' => __( 'Owner team', 'forms-bridge' ), + 'description' => __( + 'Name of the owner team of the lead', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'crm.team', + 'finger' => array( + 'value' => 'result[].id', + 'label' => 'result[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'lead_name', + 'label' => __( 'Lead name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web Lead', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'priority', + 'label' => __( 'Priority', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + 'max' => 3, + 'default' => 1, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'expected_revenue', + 'label' => __( 'Expected revenue', 'forms-bridge' ), + 'type' => 'number', + 'min' => 0, + 'default' => 0, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'tag_ids', + 'label' => __( 'Lead tags', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'crm.tag', + 'finger' => array( + 'value' => 'result[].id', + 'label' => 'result[].name', + ), + ), + 'is_multi' => true, + ), + ), + 'bridge' => array( + 'endpoint' => 'crm.lead', + 'mutations' => array( + array( + array( + 'from' => 'team_id', + 'to' => 'team_id', + 'cast' => 'integer', + ), + array( + 'from' => 'contact_name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => '?priority', + 'to' => 'priority', + 'cast' => 'string', + ), + array( + 'from' => '?expected_revenue', + 'to' => 'expected_revenue', + 'cast' => 'number', + ), + ), + array( + array( + 'from' => 'lead_name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( 'crm-contact' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'contact_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + ), + array( + 'label' => __( 'Comments', 'forms-bridge' ), + 'name' => 'description', + 'type' => 'textarea', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/helpdesk-tickets.php b/forms-bridge/addons/odoo/templates/helpdesk-tickets.php new file mode 100644 index 00000000..6ac2716a --- /dev/null +++ b/forms-bridge/addons/odoo/templates/helpdesk-tickets.php @@ -0,0 +1,129 @@ + __( 'Helpdesk ticket', 'forms-bridge' ), + 'description' => __( + 'Convert form submissions to helpdesk tickets', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Helpdesk', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'helpdesk.ticket', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'team_id', + 'label' => __( 'Owner team', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'helpdesk.ticket.team', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'channel_id', + 'label' => __( 'Incoming channel', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'helpdesk.ticket.channel', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'user_id', + 'label' => __( 'Owner user', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'res.user', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => 'helpdesk.ticket', + 'mutations' => array( + array( + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'partner_name', + 'cast' => 'copy', + ), + array( + 'from' => 'your-email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'partner_email', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'ticket-name', + 'to' => 'name', + 'cast' => 'string', + ), + ), + ), + 'workflow' => array( 'contact' ), + ), + 'form' => array( + 'title' => __( 'Helpdesk', 'forms-bridge' ), + 'fields' => array( + array( + 'name' => 'your-name', + 'label' => __( 'Your name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'your-email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'ticket-name', + 'label' => __( 'Subject', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'description', + 'label' => __( 'Message', 'forms-bridge' ), + 'type' => 'textarea', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/job-applicants.php b/forms-bridge/addons/odoo/templates/job-applicants.php new file mode 100644 index 00000000..1cadc7c3 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/job-applicants.php @@ -0,0 +1,148 @@ + __( 'Job position', 'forms-bridge' ), + 'description' => __( + 'Job application form. The resulting bridge will convert form submissions into applications to a job from the Human Resources module', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Job position', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'hr.applicant', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'user_id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Name of the owner user of the application', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'res.users', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'job_id', + 'label' => __( 'Job position', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'hr.job', + 'finger' => array( + 'value' => 'result.[].id', + 'label' => 'result.[].name', + ), + ), + ), + ), + 'bridge' => array( + 'endpoint' => 'hr.applicant', + 'mutations' => array( + array( + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'partner_name', + 'cast' => 'copy', + ), + array( + 'from' => 'your-email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => 'email', + 'to' => 'email_from', + 'cast' => 'copy', + ), + array( + 'from' => 'your-phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'phone', + 'to' => 'partner_phone', + 'cast' => 'copy', + ), + array( + 'from' => 'user_id', + 'to' => 'user_id', + 'cast' => 'integer', + ), + array( + 'from' => 'job_id', + 'to' => 'job_id', + 'cast' => 'integer', + ), + ), + array(), + array(), + array( + array( + 'from' => 'curriculum', + 'to' => 'curriculum', + 'cast' => 'null', + ), + array( + 'from' => 'curriculum_filename', + 'to' => 'curriculum_filename', + 'cast' => 'null', + ), + ), + ), + 'workflow' => array( 'contact', 'candidate', 'attachments' ), + ), + 'form' => array( + 'title' => 'Job position', + 'fields' => array( + array( + 'name' => 'your-name', + 'label' => __( 'Your name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'your-email', + 'label' => __( 'Your email', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'your-phone', + 'label' => __( 'Your phone', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'applicant_notes', + 'label' => __( 'Description', 'forms-bridge' ), + 'type' => 'textarea', + 'required' => true, + ), + array( + 'name' => 'curriculum', + 'label' => __( 'Curriculum', 'forms-brirdge' ), + 'type' => 'file', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/mailing-lists.php b/forms-bridge/addons/odoo/templates/mailing-lists.php new file mode 100644 index 00000000..f2250dfc --- /dev/null +++ b/forms-bridge/addons/odoo/templates/mailing-lists.php @@ -0,0 +1,85 @@ + __( 'Mailing Lists', 'forms-bridge' ), + 'description' => __( + 'Subscription form template. The resulting bridge will convert form submissions into subscriptions to mailing lists.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Mailing Lists', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'mailing.contact', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'list_ids', + 'label' => __( 'Mailing lists', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'mailing.list', + 'finger' => array( + 'value' => 'result[].id', + 'label' => 'result[].name', + ), + ), + 'is_multi' => true, + 'required' => true, + ), + ), + 'bridge' => array( + 'endpoint' => 'mailing.contact', + 'workflow' => array( 'mailing-contact' ), + 'mutations' => array( + array( + array( + 'from' => 'first_name', + 'to' => 'name[0]', + 'cast' => 'copy', + ), + array( + 'from' => 'last_name', + 'to' => 'name[1]', + 'cast' => 'copy', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'First name', 'forms-bridge' ), + 'name' => 'first_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Last name', 'forms-bridge' ), + 'name' => 'last_name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/quotations.php b/forms-bridge/addons/odoo/templates/quotations.php new file mode 100644 index 00000000..00aaa837 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/quotations.php @@ -0,0 +1,158 @@ + __( 'Quotations', 'forms-bridge' ), + 'description' => __( + 'Quotations form template. The resulting bridge will convert form submissions into quotations linked to new contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Quotations', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'sale.order', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'product_id', + 'label' => __( 'Product', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + 'endpoint' => 'product.product', + 'finger' => array( + 'value' => 'result[].id', + 'label' => 'result[].name', + ), + ), + 'required' => true, + ), + ), + 'bridge' => array( + 'endpoint' => 'sale.order', + 'custom_fields' => array( + array( + 'name' => 'state', + 'value' => 'draft', + ), + array( + 'name' => 'order_line[0][0]', + 'value' => '0', + ), + array( + 'name' => 'order_line[0][1]', + 'value' => '0', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'your-name', + 'to' => 'name', + 'cast' => 'string', + ), + array( + 'from' => 'order_line[0][0]', + 'to' => 'order_line[0][0]', + 'cast' => 'integer', + ), + array( + 'from' => 'order_line[0][1]', + 'to' => 'order_line[0][1]', + 'cast' => 'integer', + ), + array( + 'from' => 'quantity', + 'to' => 'order_line[0][2].product_uom_qty', + 'cast' => 'integer', + ), + array( + 'from' => 'product_id', + 'to' => 'order_line[0][2].product_id', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( 'iso2-country-code', 'vat-id', 'country-id', 'contact' ), + ), + 'form' => array( + 'fields' => array( + array( + 'label' => __( 'Quantity', 'forms-bridge' ), + 'name' => 'quantity', + 'type' => 'number', + 'required' => true, + 'default' => 1, + 'min' => 1, + ), + array( + 'label' => __( 'Your name', 'forms-bridge' ), + 'name' => 'your-name', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Vat ID', 'forms-bridge' ), + 'name' => 'vat', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Your email', 'forms-bridge' ), + 'name' => 'email', + 'type' => 'email', + 'required' => true, + ), + array( + 'label' => __( 'Your phone', 'forms-bridge' ), + 'name' => 'phone', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Address', 'forms-bridge' ), + 'name' => 'street', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'City', 'forms-bridge' ), + 'name' => 'city', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Zip code', 'forms-bridge' ), + 'name' => 'zip', + 'type' => 'text', + 'required' => true, + ), + array( + 'label' => __( 'Country', 'forms-bridge' ), + 'name' => 'country', + 'type' => 'select', + 'options' => array_map( + function ( $country_code ) { + global $forms_bridge_iso2_countries; + return array( + 'value' => $country_code, + 'label' => $forms_bridge_iso2_countries[ $country_code ], + ); + }, + array_keys( $forms_bridge_iso2_countries ) + ), + 'required' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/woo-delivered-orders.php b/forms-bridge/addons/odoo/templates/woo-delivered-orders.php new file mode 100644 index 00000000..e9ac2eb2 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/woo-delivered-orders.php @@ -0,0 +1,401 @@ + __( 'Delivered Orders', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product delivered sale orders linked to new contacts. To work properly, the bridge needs that you use your Odoo\'s product internal refrence as the WooCommerce product sku values.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'sale.order', + ), + ), + 'bridge' => array( + 'endpoint' => 'sale.order', + 'custom_fields' => array( + array( + 'name' => 'state', + 'value' => 'sale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'shipping.comment', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][0]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][0]', + 'to' => 'order_line[][0]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][1]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][1]', + 'to' => 'order_line[][1]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'order_line[][2].product_uom_qty', + 'cast' => 'integer', + ), + array( + 'from' => 'order_line[][2].product_uom_qty', + 'to' => 'order_line[][2].qty_delivered', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'order_line[][2].default_code', + 'cast' => 'string', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'order_line[][2].price_unit', + 'cast' => 'number', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'partner_id', + 'to' => 'order_partner_id', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.comment', + 'to' => 'comment', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'order_line[][2].default_code', + 'to' => 'internal_refs', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'order_partner_id', + 'to' => 'partner_id', + 'cast' => 'integer', + ), + array( + 'from' => 'product_ids[]', + 'to' => 'order_line[][2].product_id', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( 'contact', 'delivery-address', 'products-by-ref' ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/woo-delivered-sync-orders.php b/forms-bridge/addons/odoo/templates/woo-delivered-sync-orders.php new file mode 100644 index 00000000..95d5126b --- /dev/null +++ b/forms-bridge/addons/odoo/templates/woo-delivered-sync-orders.php @@ -0,0 +1,408 @@ + __( 'Delivered Orders + Sync', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product delivered sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Odoo by product refs.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'sale.order', + ), + ), + 'bridge' => array( + 'endpoint' => 'sale.order', + 'custom_fields' => array( + array( + 'name' => 'state', + 'value' => 'sale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'shipping.comment', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][0]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][0]', + 'to' => 'order_line[][0]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][1]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][1]', + 'to' => 'order_line[][1]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'order_line[][2].product_uom_qty', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][2].product_uom_qty', + 'to' => 'order_line[][2].qty_delivered', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'order_line[][2].default_code', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'order_line[][2].price_unit', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'partner_id', + 'to' => 'order_partner_id', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.comment', + 'to' => 'comment', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'order_line[][2].default_code', + 'to' => 'internal_refs', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'order_partner_id', + 'to' => 'partner_id', + 'cast' => 'integer', + ), + array( + 'from' => 'product_ids[]', + 'to' => 'order_line[][2].product_id', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'sync-products-by-ref', + 'contact', + 'delivery-address', + 'products-by-ref', + ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/woo-sale-orders.php b/forms-bridge/addons/odoo/templates/woo-sale-orders.php new file mode 100644 index 00000000..5aae546b --- /dev/null +++ b/forms-bridge/addons/odoo/templates/woo-sale-orders.php @@ -0,0 +1,396 @@ + __( 'Sale Orders', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. To work properly, the bridge needs that you use your Odoo\'s product internal refrence as the WooCommerce product sku values.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'sale.order', + ), + ), + 'bridge' => array( + 'endpoint' => 'sale.order', + 'custom_fields' => array( + array( + 'name' => 'state', + 'value' => 'sale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'shipping.comment', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][0]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][0]', + 'to' => 'order_line[][0]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][1]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][1]', + 'to' => 'order_line[][1]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'order_line[][2].product_uom_qty', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'order_line[][2].default_code', + 'cast' => 'string', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'order_line[][2].price_unit', + 'cast' => 'number', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'partner_id', + 'to' => 'order_partner_id', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.comment', + 'to' => 'comment', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'order_line[][2].default_code', + 'to' => 'internal_refs', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'order_partner_id', + 'to' => 'partner_id', + 'cast' => 'integer', + ), + array( + 'from' => 'product_ids[]', + 'to' => 'order_line[][2].product_id', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( 'contact', 'delivery-address', 'products-by-ref' ), + ), +); diff --git a/forms-bridge/addons/odoo/templates/woo-sync-orders.php b/forms-bridge/addons/odoo/templates/woo-sync-orders.php new file mode 100644 index 00000000..28e24b89 --- /dev/null +++ b/forms-bridge/addons/odoo/templates/woo-sync-orders.php @@ -0,0 +1,403 @@ + __( 'Sale Orders + Sync', 'forms-bridge' ), + 'description' => __( + 'Sale order bridge template. The resulting bridge will convert WooCommerce orders into product sale orders linked to new contacts. The template includes a job that synchronize products between WooCommerce and Odoo by product refs.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => 'sale.order', + ), + ), + 'bridge' => array( + 'endpoint' => 'sale.order', + 'custom_fields' => array( + array( + 'name' => 'state', + 'value' => 'sale', + ), + ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => 'billing.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?billing.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?billing.email', + 'to' => 'email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'customer_ip_address', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => '?customer_note', + 'to' => 'shipping.comment', + 'cast' => 'string', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][0]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][0]', + 'to' => 'order_line[][0]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].product.name', + 'to' => 'order_line[][1]', + 'cast' => 'copy', + ), + array( + 'from' => 'order_line[][1]', + 'to' => 'order_line[][1]', + 'cast' => 'integer', + ), + array( + 'from' => 'line_items[].quantity', + 'to' => 'order_line[][2].product_uom_qty', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.sku', + 'to' => 'order_line[][2].default_code', + 'cast' => 'copy', + ), + array( + 'from' => 'line_items[].product.price', + 'to' => 'order_line[][2].price_unit', + 'cast' => 'copy', + ), + ), + array( + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'partner_id', + 'to' => 'order_partner_id', + 'cast' => 'copy', + ), + array( + 'from' => '?shipping.first_name', + 'to' => 'name[0]', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.last_name', + 'to' => 'name[1]', + 'cast' => 'string', + ), + array( + 'from' => 'name', + 'to' => 'name', + 'cast' => 'concat', + ), + array( + 'from' => '?shipping.phone', + 'to' => 'phone', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_1', + 'to' => 'street', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.address_2', + 'to' => 'street2', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.city', + 'to' => 'city', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.postcode', + 'to' => 'zip', + 'cast' => 'string', + ), + array( + 'from' => '?shipping.comment', + 'to' => 'comment', + 'cast' => 'string', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'order_line[][2].default_code', + 'to' => 'internal_refs', + 'cast' => 'inherit', + ), + ), + array( + array( + 'from' => 'order_partner_id', + 'to' => 'partner_id', + 'cast' => 'integer', + ), + array( + 'from' => 'product_ids[]', + 'to' => 'order_line[][2].product_id', + 'cast' => 'integer', + ), + ), + ), + 'workflow' => array( + 'sync-products-by-ref', + 'contact', + 'delivery-address', + 'products-by-ref', + ), + ), +); diff --git a/addons/rest/assets/logo.png b/forms-bridge/addons/rest/assets/logo.png similarity index 100% rename from addons/rest/assets/logo.png rename to forms-bridge/addons/rest/assets/logo.png diff --git a/forms-bridge/addons/rest/class-rest-addon.php b/forms-bridge/addons/rest/class-rest-addon.php new file mode 100644 index 00000000..f74d360f --- /dev/null +++ b/forms-bridge/addons/rest/class-rest-addon.php @@ -0,0 +1,29 @@ + $payload['Last_Name'], + ); + + $lead_fields = array( + 'Owner', + 'Company', + 'First_Name', + 'Full_Name', + 'Email', + 'Phone', + 'Fax', + 'Mobile', + 'Website', + 'Lead_Source', + 'Lead_Status', + 'Industry', + 'No_of_Employees', + 'Annual_Revenue', + 'Street', + 'City', + 'Zip_Code', + 'State', + 'Country', + 'Description', + 'Tag', + ); + + foreach ( $lead_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $lead[ $field ] = $payload[ $field ]; + } + } + + $response = $bridge + ->patch( + array( + 'name' => 'zoho-crm-create-lead', + 'scope' => 'ZohoCRM.modules.leads.CREATE', + 'endpoint' => '/crm/v7/Leads/upsert', + 'template' => null, + ) + ) + ->submit( $lead ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $code = $response['data']['data'][0]['code'] ?? null; + if ( $code === 'DUPLICATE_DATA' ) { + return $response['data']['data'][0]['details']['duplicate_record']; + } else { + return $response['data']['data'][0]['details']; + } +} + +function forms_bridge_zoho_crm_create_contact( $payload, $bridge ) { + $contact = array( + 'Last_Name' => $payload['Last_Name'], + ); + + $contact_fields = array( + 'Owner', + 'Lead_Source', + 'First_Name', + 'Full_Name', + 'Email', + 'Fax', + 'Mobile', + 'Phone', + 'Title', + 'Department', + 'Account_Name', + 'Mailing_Street', + 'Mailing_City', + 'Mailing_Zip', + 'Mailing_State', + 'Mailing_Country', + 'Description', + 'Tag', + ); + + foreach ( $contact_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $contact[ $field ] = $payload[ $field ]; + } + } + + if ( + isset( $payload['Account_Name'] ) && + is_string( $payload['Account_Name'] ) + ) { + $account = forms_bridge_zoho_crm_create_account( $payload, $bridge ); + + if ( is_wp_error( $account ) ) { + return $account; + } + + $payload['Account_Name'] = array( 'id' => $account['id'] ); + } + + $response = $bridge + ->patch( + array( + 'name' => 'zoho-crm-create-contact', + 'scope' => 'ZohoCRM.modules.contacts.CREATE', + 'endpoint' => '/crm/v7/Contacts/upsert', + 'template' => null, + ) + ) + ->submit( $contact ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $code = $response['data']['data'][0]['code']; + if ( $code === 'DUPLICATE_DATA' ) { + return $response['data']['data'][0]['details']['duplicate_record']; + } else { + return $response['data']['data'][0]['details']; + } +} + +function forms_bridge_zoho_crm_create_account( $payload, $bridge ) { + $company = array( + 'Account_Name' => $payload['Account_Name'], + ); + + $company_fields = array( + 'Billing_Street', + 'Billing_Code', + 'Billing_City', + 'Billing_State', + 'Billing_Country', + 'Phone', + 'Fax', + 'Mobile', + 'Website', + 'Owner', + 'Industry', + 'Ownership', + 'Employees', + 'Description', + 'Tag', + ); + + foreach ( $company_fields as $field ) { + if ( isset( $payload[ $field ] ) ) { + $company[ $field ] = $payload[ $field ]; + } + } + + $response = $bridge + ->patch( + array( + 'name' => 'zoho-crm-create-account', + 'scope' => 'ZohoCRM.modules.accounts.CREATE', + 'endpoint' => '/crm/v7/Accounts/upsert', + 'template' => null, + ) + ) + ->submit( $company ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $code = $response['data']['data'][0]['code'] ?? null; + if ( $code === 'DUPLICATE_DATA' ) { + return $response['data']['data'][0]['details']['duplicate_record']; + } else { + return $response['data']['data'][0]['details']; + } +} diff --git a/addons/zoho/assets/logo.png b/forms-bridge/addons/zoho/assets/logo.png similarity index 100% rename from addons/zoho/assets/logo.png rename to forms-bridge/addons/zoho/assets/logo.png diff --git a/forms-bridge/addons/zoho/class-zoho-addon.php b/forms-bridge/addons/zoho/class-zoho-addon.php new file mode 100644 index 00000000..4dec4fbc --- /dev/null +++ b/forms-bridge/addons/zoho/class-zoho-addon.php @@ -0,0 +1,173 @@ + '__zoho-' . time(), + 'backend' => $backend, + 'endpoint' => '/crm/v7/users', + 'method' => 'GET', + ) + ); + + $backend = $bridge->backend; + if ( ! $backend ) { + return false; + } + + $credential = $backend->credential; + if ( ! $credential ) { + return false; + } + + $parsed = wp_parse_url( $backend->base_url ); + $host = $parsed['host'] ?? ''; + + if ( + ! preg_match( + '/www\.zohoapis\.(\w{2,3}(\.\w{2})?)$/', + $host, + $matches + ) + ) { + return false; + } + + // $region = $matches[1]; + // if (!preg_match('/' . $region . '$/', $credential->region)) { + // return false; + // } + + $response = $bridge->submit( array( 'type' => 'CurrentUser' ) ); + return ! is_wp_error( $response ); + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + $bridge_class = static::BRIDGE; + $bridge = new $bridge_class( + array( + 'name' => '__zoho-' . time(), + 'backend' => $backend, + 'endpoint' => $endpoint, + 'method' => 'GET', + ) + ); + + return $bridge->submit(); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields. + * + * @param string $endpoint API endpoint. + * @param string $backend Backend name. + * + * @return array List of fields and content type of the endpoint. + */ + public function get_endpoint_schema( $endpoint, $backend ) { + if ( + ! preg_match( + '/\/(([A-Z][a-z]+(_[A-Z][a-z])?)(?:\/upsert)?$)/', + $endpoint, + $matches + ) + ) { + return array(); + } + + $module = $matches[2]; + + $bridge_class = static::BRIDGE; + $bridge = new $bridge_class( + array( + 'name' => '__zoho-' . time(), + 'backend' => $backend, + 'endpoint' => '/crm/v7/settings/layouts', + 'method' => 'GET', + ) + ); + + $response = $bridge->submit( array( 'module' => $module ) ); + + if ( is_wp_error( $response ) ) { + return array(); + } + + $fields = array(); + foreach ( $response['data']['layouts'] as $layout ) { + foreach ( $layout['sections'] as $section ) { + foreach ( $section['fields'] as $field ) { + $type = $field['json_type']; + if ( $type === 'jsonobject' ) { + $type = 'object'; + } elseif ( $type === 'jsonarray' ) { + $type = 'array'; + } elseif ( $type === 'double' ) { + $type = 'number'; + } + + $fields[] = array( + 'name' => $field['api_name'], + 'schema' => array( 'type' => $type ), + ); + } + } + } + + return $fields; + } +} + +Zoho_Addon::setup(); diff --git a/forms-bridge/addons/zoho/class-zoho-form-bridge.php b/forms-bridge/addons/zoho/class-zoho-form-bridge.php new file mode 100644 index 00000000..b24da5c5 --- /dev/null +++ b/forms-bridge/addons/zoho/class-zoho-form-bridge.php @@ -0,0 +1,82 @@ +is_valid ) { + return new WP_Error( 'invalid_bridge' ); + } + + $method = $this->method; + if ( 'POST' === $method || 'PUT' === $method ) { + $payload = wp_is_numeric_array( $payload ) ? $payload : array( $payload ); + $payload = array( 'data' => $payload ); + } + + add_filter( + 'http_bridge_backend_headers', + function ( $headers, $backend ) { + if ( $backend->name === $this->data['backend'] ) { + if ( isset( $headers['Authorization'] ) ) { + $headers['Authorization'] = str_replace( + 'Bearer', + 'Zoho-oauthtoken', + $headers['Authorization'] + ); + } + } + + return $headers; + }, + 9, + 2 + ); + + $response = $this->backend()->$method( + $this->endpoint, + $payload, + array(), + $attachments + ); + + if ( is_wp_error( $response ) ) { + $data = json_decode( + $response->get_error_data()['response']['body'], + true + ); + + $code = $data['data'][0]['code'] ?? null; + if ( 'DUPLICATE_DATA' !== $code ) { + return $response; + } + + $response = $response->get_error_data()['response']; + $response['data'] = json_decode( $response['body'], true ); + } + + return $response; + } +} diff --git a/forms-bridge/addons/zoho/hooks.php b/forms-bridge/addons/zoho/hooks.php new file mode 100644 index 00000000..e2cf9ab2 --- /dev/null +++ b/forms-bridge/addons/zoho/hooks.php @@ -0,0 +1,266 @@ + array( + array( + 'ref' => '#credential', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'schema', + 'type' => 'text', + 'value' => 'Bearer', + ), + array( + 'ref' => '#credential', + 'name' => 'oauth_url', + 'label' => __( 'Authorization URL', 'forms-bridge' ), + 'type' => 'text', + 'value' => 'https://accounts.{region}/oauth/v2', + ), + array( + 'ref' => '#credential', + 'name' => 'region', + 'label' => __( 'Datacenter', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'value' => 'zoho.com', + 'label' => 'zoho.com', + ), + array( + 'value' => 'zoho.eu', + 'label' => 'zoho.eu', + ), + array( + 'value' => 'zoho.in', + 'label' => 'zoho.in', + ), + array( + 'value' => 'zoho.com.cn', + 'label' => 'zoho.com.cn', + ), + array( + 'value' => 'zoho.com.au', + 'label' => 'zoho.com.au', + ), + array( + 'value' => 'zoho.jp', + 'label' => 'zoho.jp', + ), + array( + 'label' => 'zoho.sa', + 'value' => 'zoho.sa', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_id', + 'label' => __( 'Client ID', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'client_secret', + 'label' => __( 'Client secret', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#credential', + 'name' => 'scope', + 'label' => __( 'Scope', 'forms-bridge' ), + // 'description' => __( + // 'See the documentation for more information', + // 'forms-bridge' + // ), + 'type' => 'text', + 'value' => + 'ZohoCRM.modules.ALL,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ', + 'required' => true, + ), + array( + 'ref' => '#backend', + 'name' => 'name', + 'default' => 'Zoho API', + ), + array( + 'ref' => '#backend', + 'name' => 'base_url', + 'type' => 'select', + 'options' => array( + array( + 'label' => 'www.zohoapis.com', + 'value' => 'https://www.zohoapis.com', + ), + array( + 'label' => 'www.zohoapis.eu', + 'value' => 'https://www.zohoapis.eu', + ), + array( + 'label' => 'www.zohoapis.com.au', + 'value' => 'https://www.zohoapis.com.au', + ), + array( + 'label' => 'www.zohoapis.in', + 'value' => 'https://www.zohoapis.in', + ), + array( + 'label' => 'www.zohoapis.cn', + 'value' => 'https://www.zohoapis.cn', + ), + array( + 'label' => 'www.zohoapis.jp', + 'value' => 'https://www.zohoapis.jp', + ), + array( + 'label' => 'www.zohoapis.sa', + 'value' => 'https://www.zohoapis.sa', + ), + array( + 'label' => 'www.zohoapis.ca', + 'value' => 'https://www.zohoapis.ca', + ), + ), + 'default' => 'https://www.zohoapis.com', + 'required' => true, + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'value' => 'POST', + ), + ), + 'bridge' => array( + 'backend' => 'Zoho API', + 'endpoint' => '', + ), + 'credential' => array( + 'name' => '', + 'schema' => 'Bearer', + 'oauth_url' => 'https://accounts.{region}/oauth/v2', + 'scope' => + 'ZohoCRM.modules.ALL,ZohoCRM.settings.layouts.READ,ZohoCRM.users.READ', + 'client_id' => '', + 'client_secret' => '', + 'access_token' => '', + 'expires_at' => 0, + 'refresh_token' => '', + ), + 'backend' => array( + 'base_url' => 'https://www.zohoapis.{region}', + 'headers' => array( + array( + 'name' => 'Accept', + 'value' => 'application/json', + ), + ), + ), + ), + $defaults, + $schema + ); + }, + 10, + 3 +); + +add_filter( + 'forms_bridge_template_data', + function ( $data, $template_id ) { + if ( strpos( $template_id, 'zoho-' ) !== 0 ) { + return $data; + } + + $region = $data['credential']['region']; + $data['credential']['oauth_url'] = preg_replace( + '/{region}/', + $region, + $data['credential']['oauth_url'] + ); + unset( $data['credential']['region'] ); + + $index = array_search( + 'Tag', + array_column( $data['bridge']['custom_fields'], 'name' ), + true + ); + + if ( false !== $index ) { + $field = &$data['bridge']['custom_fields'][ $index ]; + + if ( ! empty( $field['value'] ) ) { + $tags = array_filter( + array_map( 'trim', explode( ',', strval( $field['value'] ) ) ) + ); + + $l = count( $tags ); + for ( $i = 0; $i < $l; $i++ ) { + $data['bridge']['custom_fields'][] = array( + 'name' => "Tag[{$i}].name", + 'value' => $tags[ $i ], + ); + } + } + + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + + $index = array_search( + 'All_day', + array_column( $data['bridge']['custom_fields'], 'name' ), + true + ); + + if ( false !== $index ) { + $data['form']['fields'] = array_filter( + $data['form']['fields'], + function ( $field ) { + return ! in_array( + $field['name'], + array( + 'hour', + 'minute', + __( 'Hour', 'forms-bridge' ), + __( 'Minute', 'forms-bridge' ), + ), + true + ); + } + ); + + $index = array_search( + 'duration', + array_column( $data['bridge']['custom_fields'], 'name' ), + true + ); + + if ( false !== $index ) { + array_splice( $data['bridge']['custom_fields'], $index, 1 ); + } + } + + return $data; + }, + 10, + 2 +); diff --git a/forms-bridge/addons/zoho/jobs/appointment-participant.php b/forms-bridge/addons/zoho/jobs/appointment-participant.php new file mode 100644 index 00000000..2f4d24d9 --- /dev/null +++ b/forms-bridge/addons/zoho/jobs/appointment-participant.php @@ -0,0 +1,104 @@ + 'contact', + 'participant' => $contact['id'], + ); + + return $payload; +} + +return array( + 'title' => __( 'Appointment participant', 'forms-bridge' ), + 'description' => __( + 'Search for a contact or creates a new one and sets its ID as appointment participant', + 'forms-bridge' + ), + 'method' => 'forms_bridge_zoho_appointment_participant', + 'input' => array( + array( + 'name' => 'Last_Name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'First_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Full_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_Street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_City', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_Zip', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_State', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mailing_Country', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Description', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Account_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Title', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'Participants', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'type' => array( 'type' => 'string' ), + 'participant' => array( 'type' => 'string' ), + ), + ), + 'additionalItems' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/zoho/jobs/contact-account.php b/forms-bridge/addons/zoho/jobs/contact-account.php new file mode 100644 index 00000000..7b813c2b --- /dev/null +++ b/forms-bridge/addons/zoho/jobs/contact-account.php @@ -0,0 +1,125 @@ + __( 'Contact account', 'forms-bridge' ), + 'description' => __( + 'Create an account and sets its id as the Account_Name field on the payload', + 'forms-bridge' + ), + 'method' => 'forms_bridge_zoho_crm_contact_account', + 'input' => array( + array( + 'name' => 'Account_Name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'Rating', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'Billing_Street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_City', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_Code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_State', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Billing_Country', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Fax', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Website', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Owner', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + ), + 'required' => array( 'id' ), + 'additionalProperties' => false, + ), + ), + array( + 'name' => 'Industry', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Ownership', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Employees', + 'schema' => array( 'type' => 'integer' ), + ), + array( + 'name' => 'Description', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Tag', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + 'required' => array( 'name' ), + ), + ), + ), + ), + 'output' => array( + array( + 'name' => 'Account_Name', + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + ), + ), + ), +); + +function forms_bridge_zoho_crm_contact_account( $payload, $bridge ) { + $account = forms_bridge_zoho_crm_create_account( $payload, $bridge ); + + if ( is_wp_error( $account ) ) { + return $account; + } + + $payload['Account_Name'] = array( 'id' => $account['id'] ); + return $payload; +} diff --git a/forms-bridge/addons/zoho/jobs/crm-meeting-participant.php b/forms-bridge/addons/zoho/jobs/crm-meeting-participant.php new file mode 100644 index 00000000..c12a91ac --- /dev/null +++ b/forms-bridge/addons/zoho/jobs/crm-meeting-participant.php @@ -0,0 +1,146 @@ + 'lead', + 'participant' => $lead['id'], + ); + + return $payload; +} + +return array( + 'title' => __( 'CRM meeting participant', 'forms-bridge' ), + 'description' => __( + 'Search for a lead or creates a new one and sets its ID as meeting participant', + 'forms-bridge' + ), + 'method' => 'forms_bridge_crm_meeting_participant', + 'input' => array( + array( + 'name' => 'Last_Name', + 'schema' => array( 'type' => 'string' ), + 'required' => true, + ), + array( + 'name' => 'First_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Full_Name', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Designation', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Secondary_Email', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Phone', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Mobile', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Fax', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Website', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Lead_Source', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Lead_Status', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Description', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Company', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'No_of_Employees', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Industry', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Annual_Revenue', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Street', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'City', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'State', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Zip_Code', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'Tag', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( 'type' => 'string' ), + ), + 'additionalProperties' => false, + 'required' => array( 'name' ), + ), + ), + ), + ), + 'output' => array( + array( + 'name' => 'Participants', + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'type' => array( 'type' => 'string' ), + 'participant' => array( 'type' => 'string' ), + ), + ), + 'additionalItems' => true, + ), + ), + ), +); diff --git a/forms-bridge/addons/zoho/jobs/event-dates.php b/forms-bridge/addons/zoho/jobs/event-dates.php new file mode 100644 index 00000000..b323cba0 --- /dev/null +++ b/forms-bridge/addons/zoho/jobs/event-dates.php @@ -0,0 +1,54 @@ +getTimestamp(); + + $payload['Start_DateTime'] = date( 'c', $timestamp ); + $payload['End_DateTime'] = date( 'c', $timestamp + 3600 * $duration ); + + return $payload; +} + +return array( + 'title' => __( 'Meeting dates', 'forms-bridge' ), + 'description' => __( + 'Sets meeting start and end time from "date" and "duration" fields', + 'forms-bridge' + ), + 'method' => 'forms_bridge_zoho_meeting_dates', + 'input' => array( + array( + 'name' => 'date', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'duration', + 'schema' => array( 'type' => 'number' ), + ), + ), + 'output' => array( + array( + 'name' => 'Start_DateTime', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'End_DateTime', + 'schema' => array( 'type' => 'string' ), + ), + ), +); diff --git a/forms-bridge/addons/zoho/templates/accounts.php b/forms-bridge/addons/zoho/templates/accounts.php new file mode 100644 index 00000000..16cd8adc --- /dev/null +++ b/forms-bridge/addons/zoho/templates/accounts.php @@ -0,0 +1,119 @@ + __( 'Company Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form for companies template. The resulting bridge will convert form submissions into new contacts linked to company accounts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/crm/v7/Contacts/upsert', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the account', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/crm/v7/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Contacts', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'Account_Name', + 'label' => __( 'Company name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Billing_Street', + 'label' => __( 'Address', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Billing_City', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Billing_Code', + 'label' => __( 'Zip code', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Billing_State', + 'label' => __( 'State', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Billing_Country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Title', + 'label' => __( 'Job position', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/crm/v7/Contacts/upsert', + 'workflow' => array( 'contact-account' ), + ), +); diff --git a/forms-bridge/addons/zoho/templates/company-leads.php b/forms-bridge/addons/zoho/templates/company-leads.php new file mode 100644 index 00000000..f6806ad9 --- /dev/null +++ b/forms-bridge/addons/zoho/templates/company-leads.php @@ -0,0 +1,176 @@ + __( 'Company Leads', 'forms-bridge' ), + 'description' => __( + 'Lead form template. The resulting bridge will convert form submissions into company leads.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/crm/v7/Leads/upsert', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the deal', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/crm/v7/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Lead_Source', + 'label' => __( 'Lead source', 'forms-bridge' ), + 'description' => __( + 'Label to identify your website sourced leads', + 'forms-bridge' + ), + 'type' => 'text', + 'default' => 'WordPress', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Lead_Status', + 'label' => __( 'Lead status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Not Contacted', 'forms-bridge' ), + 'value' => 'Not Connected', + ), + array( + 'label' => __( 'Qualified', 'forms-bridge' ), + 'value' => 'Qualified', + ), + array( + 'label' => __( 'Not qualified', 'forms-bridge' ), + 'value' => 'Not Qualified', + ), + array( + 'label' => __( 'Pre-qualified', 'forms-bridge' ), + 'value' => 'Pre-Qualified', + ), + array( + 'label' => __( 'Attempted to Contact', 'forms-bridge' ), + 'value' => 'New Lead', + ), + array( + 'label' => __( 'Contact in Future', 'forms-bridge' ), + 'value' => 'Connected', + ), + array( + 'label' => __( 'Junk Lead', 'forms-bridge' ), + 'value' => 'Junk Lead', + ), + array( + 'label' => __( 'Lost Lead', 'forms-bridge' ), + 'value' => 'Lost Lead', + ), + ), + 'default' => 'Not Contacted', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Tag', + 'label' => __( 'Lead tags', 'forms-bridge' ), + 'description' => __( + 'Tag names separated by commas', + 'forms-bridge' + ), + 'type' => 'text', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Company Leads', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'Company', + 'label' => __( 'Company', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Street', + 'label' => __( 'Street', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Zip_Code', + 'label' => __( 'Postal code', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'City', + 'label' => __( 'City', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'State', + 'label' => __( 'State', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Country', + 'label' => __( 'Country', 'forms-bridge' ), + 'type' => 'text', + ), + + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Title', + 'label' => __( 'Job position', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/crm/v7/Leads/upsert', + ), +); diff --git a/forms-bridge/addons/zoho/templates/contacts.php b/forms-bridge/addons/zoho/templates/contacts.php new file mode 100644 index 00000000..314aa3ff --- /dev/null +++ b/forms-bridge/addons/zoho/templates/contacts.php @@ -0,0 +1,78 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert form submissions into contacts.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/crm/v7/Contacts/upsert', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the account', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/crm/v7/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Contacts', 'forms-bridge' ), + ), + ), + 'form' => array( + 'title' => 'Contacts', + 'fields' => array( + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/crm/v7/Contacts/upsert', + ), +); diff --git a/forms-bridge/addons/zoho/templates/leads.php b/forms-bridge/addons/zoho/templates/leads.php new file mode 100644 index 00000000..a3e184f1 --- /dev/null +++ b/forms-bridge/addons/zoho/templates/leads.php @@ -0,0 +1,139 @@ + __( 'Leads', 'forms-bridge' ), + 'description' => __( + 'Lead form template. The resulting bridge will convert form submissions into leads.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/crm/v7/Leads/upsert', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the deal', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/crm/v7/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Lead_Source', + 'label' => __( 'Lead source', 'forms-bridge' ), + 'description' => __( + 'Label to identify your website sourced leads', + 'forms-bridge' + ), + 'type' => 'text', + 'default' => 'WordPress', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Lead_Status', + 'label' => __( 'Lead status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Not Contacted', 'forms-bridge' ), + 'value' => 'Not Connected', + ), + array( + 'label' => __( 'Qualified', 'forms-bridge' ), + 'value' => 'Qualified', + ), + array( + 'label' => __( 'Not qualified', 'forms-bridge' ), + 'value' => 'Not Qualified', + ), + array( + 'label' => __( 'Pre-qualified', 'forms-bridge' ), + 'value' => 'Pre-Qualified', + ), + array( + 'label' => __( 'Attempted to Contact', 'forms-bridge' ), + 'value' => 'New Lead', + ), + array( + 'label' => __( 'Contact in Future', 'forms-bridge' ), + 'value' => 'Connected', + ), + array( + 'label' => __( 'Junk Lead', 'forms-bridge' ), + 'value' => 'Junk Lead', + ), + array( + 'label' => __( 'Lost Lead', 'forms-bridge' ), + 'value' => 'Lost Lead', + ), + ), + 'default' => 'Not Contacted', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Tag', + 'label' => __( 'Lead tags', 'forms-bridge' ), + 'description' => __( + 'Tag names separated by commas', + 'forms-bridge' + ), + 'type' => 'text', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Leads', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/crm/v7/Leads/upsert', + ), +); diff --git a/forms-bridge/addons/zoho/templates/meetings.php b/forms-bridge/addons/zoho/templates/meetings.php new file mode 100644 index 00000000..867ae28c --- /dev/null +++ b/forms-bridge/addons/zoho/templates/meetings.php @@ -0,0 +1,343 @@ + __( 'Meetings', 'forms-bridge' ), + 'description' => __( + 'Meetings form template. The resulting bridge will convert form submissions into events on the calendar linked to new leads.', + 'forms-bridge' + ), + 'fields' => array( + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/crm/v7/Events', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the deal', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/crm/v7/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + 'required' => true, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Event_Title', + 'label' => __( 'Event title', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => __( 'Web Meetting', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Lead_Source', + 'label' => __( 'Lead source', 'forms-bridge' ), + 'description' => __( + 'Label to identify your website sourced leads', + 'forms-bridge' + ), + 'type' => 'text', + 'required' => true, + 'default' => 'WordPress', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Lead_Status', + 'label' => __( 'Lead status', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( 'Not Contacted', 'forms-bridge' ), + 'value' => 'Not Connected', + ), + array( + 'label' => __( 'Qualified', 'forms-bridge' ), + 'value' => 'Qualified', + ), + array( + 'label' => __( 'Not qualified', 'forms-bridge' ), + 'value' => 'Not Qualified', + ), + array( + 'label' => __( 'Pre-qualified', 'forms-bridge' ), + 'value' => 'Pre-Qualified', + ), + array( + 'label' => __( 'Attempted to Contact', 'forms-bridge' ), + 'value' => 'New Lead', + ), + array( + 'label' => __( 'Contact in Future', 'forms-bridge' ), + 'value' => 'Connected', + ), + array( + 'label' => __( 'Junk Lead', 'forms-bridge' ), + 'value' => 'Junk Lead', + ), + array( + 'label' => __( 'Lost Lead', 'forms-bridge' ), + 'value' => 'Lost Lead', + ), + ), + 'required' => true, + 'default' => 'Not Contacted', + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'All_day', + 'label' => __( 'Is all day event?', 'forms-bridge' ), + 'type' => 'boolean', + 'default' => false, + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'duration', + 'label' => __( 'Meeting duration', 'forms-bridge' ), + 'type' => 'number', + 'default' => 1, + 'min' => 0, + 'max' => 24, + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'default' => __( 'Meetings', 'forms-bridge' ), + ), + ), + 'form' => array( + 'fields' => array( + array( + 'name' => 'First_Name', + 'label' => __( 'First name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Last_Name', + 'label' => __( 'Last name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'name' => 'Email', + 'label' => __( 'Email', 'forms-bridge' ), + 'type' => 'email', + 'required' => true, + ), + array( + 'name' => 'Phone', + 'label' => __( 'Phone', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'name' => 'date', + 'label' => __( 'Date', 'forms-bridge' ), + 'type' => 'date', + 'required' => true, + ), + array( + 'name' => 'hour', + 'label' => __( 'Hour', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => __( '1 AM', 'forms-bridge' ), + 'value' => '01', + ), + array( + 'label' => __( '2 AM', 'forms-bridge' ), + 'value' => '02', + ), + array( + 'label' => __( '3 AM', 'forms-bridge' ), + 'value' => '03', + ), + array( + 'label' => __( '4 AM', 'forms-bridge' ), + 'value' => '04', + ), + array( + 'label' => __( '5 AM', 'forms-bridge' ), + 'value' => '05', + ), + array( + 'label' => __( '6 AM', 'forms-bridge' ), + 'value' => '06', + ), + array( + 'label' => __( '7 AM', 'forms-bridge' ), + 'value' => '07', + ), + array( + 'label' => __( '8 AM', 'forms-bridge' ), + 'value' => '08', + ), + array( + 'label' => __( '9 AM', 'forms-bridge' ), + 'value' => '09', + ), + array( + 'label' => __( '10 AM', 'forms-bridge' ), + 'value' => '10', + ), + array( + 'label' => __( '11 AM', 'forms-bridge' ), + 'value' => '11', + ), + array( + 'label' => __( '12 AM', 'forms-bridge' ), + 'value' => '12', + ), + array( + 'label' => __( '1 PM', 'forms-bridge' ), + 'value' => '13', + ), + array( + 'label' => __( '2 PM', 'forms-bridge' ), + 'value' => '14', + ), + array( + 'label' => __( '3 PM', 'forms-bridge' ), + 'value' => '15', + ), + array( + 'label' => __( '4 PM', 'forms-bridge' ), + 'value' => '16', + ), + array( + 'label' => __( '5 PM', 'forms-bridge' ), + 'value' => '17', + ), + array( + 'label' => __( '6 PM', 'forms-bridge' ), + 'value' => '18', + ), + array( + 'label' => __( '7 PM', 'forms-bridge' ), + 'value' => '19', + ), + array( + 'label' => __( '8 PM', 'forms-bridge' ), + 'value' => '20', + ), + array( + 'label' => __( '9 PM', 'forms-bridge' ), + 'value' => '21', + ), + array( + 'label' => __( '10 PM', 'forms-bridge' ), + 'value' => '22', + ), + array( + 'label' => __( '11 PM', 'forms-bridge' ), + 'value' => '23', + ), + array( + 'label' => __( '12 PM', 'forms-bridge' ), + 'value' => '24', + ), + ), + 'required' => true, + ), + array( + 'name' => 'minute', + 'label' => __( 'Minute', 'forms-bridge' ), + 'type' => 'select', + 'options' => array( + array( + 'label' => '00', + 'value' => '00.0', + ), + array( + 'label' => '05', + 'value' => '05', + ), + array( + 'label' => '10', + 'value' => '10', + ), + array( + 'label' => '15', + 'value' => '15', + ), + array( + 'label' => '20', + 'value' => '20', + ), + array( + 'label' => '25', + 'value' => '25', + ), + array( + 'label' => '30', + 'value' => '30', + ), + array( + 'label' => '35', + 'value' => '35', + ), + array( + 'label' => '40', + 'value' => '40', + ), + array( + 'label' => '45', + 'value' => '45', + ), + array( + 'label' => '50', + 'value' => '50', + ), + array( + 'label' => '55', + 'value' => '55', + ), + ), + 'required' => true, + ), + array( + 'name' => 'Description', + 'label' => __( 'Comments', 'forms-bridge' ), + 'type' => 'textarea', + ), + ), + ), + 'bridge' => array( + 'endpoint' => '/crm/v7/Events', + 'workflow' => array( + 'date-fields-to-date', + 'event-dates', + 'crm-meeting-participant', + ), + 'mutations' => array( + array( + array( + 'from' => 'All_day', + 'to' => 'All_day', + 'cast' => 'boolean', + ), + ), + array( + array( + 'from' => 'datetime', + 'to' => 'date', + 'cast' => 'string', + ), + ), + ), + ), +); diff --git a/forms-bridge/addons/zoho/templates/woo-contacts.php b/forms-bridge/addons/zoho/templates/woo-contacts.php new file mode 100644 index 00000000..7811bebd --- /dev/null +++ b/forms-bridge/addons/zoho/templates/woo-contacts.php @@ -0,0 +1,348 @@ + __( 'Contacts', 'forms-bridge' ), + 'description' => __( + 'Contact form template. The resulting bridge will convert woocommerce customers into contacts.', + 'forms-bridge' + ), + 'integrations' => array( 'woo' ), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'title', + 'value' => __( 'Woo Checkout', 'forms-bridge' ), + ), + array( + 'ref' => '#bridge/custom_fields[]', + 'name' => 'Owner.id', + 'label' => __( 'Owner', 'forms-bridge' ), + 'description' => __( + 'Email of the owner user of the account', + 'forms-bridge' + ), + 'type' => 'select', + 'options' => array( + 'endpoint' => '/crm/v7/users', + 'finger' => array( + 'value' => 'users[].id', + 'label' => 'users[].full_name', + ), + ), + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'value' => '/crm/v7/Contacts/upsert', + ), + ), + 'bridge' => array( + 'endpoint' => '/crm/v7/Contacts/upsert', + 'workflow' => array( 'contact-account' ), + 'mutations' => array( + array( + array( + 'from' => 'id', + 'to' => 'id', + 'cast' => 'null', + ), + array( + 'from' => 'parent_id', + 'to' => 'parent_id', + 'cast' => 'null', + ), + array( + 'from' => 'status', + 'to' => 'status', + 'cast' => 'null', + ), + array( + 'from' => 'version', + 'to' => 'version', + 'cast' => 'null', + ), + array( + 'from' => 'prices_include_tax', + 'to' => 'prices_include_tax', + 'cast' => 'null', + ), + array( + 'from' => 'date_created', + 'to' => 'date_created', + 'cast' => 'null', + ), + array( + 'from' => 'date_modified', + 'to' => 'date_modified', + 'cast' => 'null', + ), + array( + 'from' => 'discount_total', + 'to' => 'discount_total', + 'cast' => 'null', + ), + array( + 'from' => 'discount_tax', + 'to' => 'discount_tax', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_total', + 'to' => 'shipping_total', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_tax', + 'to' => 'shipping_tax', + 'cast' => 'null', + ), + array( + 'from' => 'cart_total', + 'to' => 'cart_total', + 'cast' => 'null', + ), + array( + 'from' => 'cart_tax', + 'to' => 'cart_tax', + 'cast' => 'null', + ), + array( + 'from' => 'total', + 'to' => 'total', + 'cast' => 'null', + ), + array( + 'from' => 'total_tax', + 'to' => 'total_tax', + 'cast' => 'null', + ), + array( + 'from' => 'customer_id', + 'to' => 'customer_id', + 'cast' => 'null', + ), + array( + 'from' => 'order_key', + 'to' => 'order_key', + 'cast' => 'null', + ), + array( + 'from' => '?Owner', + 'to' => 'Contact_Owner', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.company', + 'to' => 'Account_Name', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'Billing_Street', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.city', + 'to' => 'Billing_City', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.state', + 'to' => 'Billing_State', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.country', + 'to' => 'Billing_Country', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'Billing_Code', + 'cast' => 'copy', + ), + array( + 'from' => '?billing.first_name', + 'to' => 'First_Name', + 'cast' => 'string', + ), + array( + 'from' => 'billing.last_name', + 'to' => 'Last_Name', + 'cast' => 'string', + ), + array( + 'from' => 'billing.email', + 'to' => 'Email', + 'cast' => 'string', + ), + array( + 'from' => '?billing.phone', + 'to' => 'Phone', + 'cast' => 'string', + ), + array( + 'from' => '?billing.address_1', + 'to' => 'Mailing_Street', + 'cast' => 'string', + ), + array( + 'from' => '?billing.city', + 'to' => 'Mailing_City', + 'cast' => 'string', + ), + array( + 'from' => '?billing.state', + 'to' => 'Mailing_State', + 'cast' => 'string', + ), + array( + 'from' => '?billing.country', + 'to' => 'Mailing_Country', + 'cast' => 'string', + ), + array( + 'from' => '?billing.postcode', + 'to' => 'Mailing_Zip', + 'cast' => 'string', + ), + array( + 'from' => 'billing', + 'to' => 'billing', + 'cast' => 'null', + ), + array( + 'from' => 'shipping', + 'to' => 'shipping', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method', + 'to' => 'payment_method', + 'cast' => 'null', + ), + array( + 'from' => 'payment_method_title', + 'to' => 'payment_method_title', + 'cast' => 'null', + ), + array( + 'from' => 'transaction_id', + 'to' => 'transaction_id', + 'cast' => 'null', + ), + array( + 'from' => 'customer_ip_address', + 'to' => 'ip_signup', + 'cast' => 'null', + ), + array( + 'from' => 'customer_user_agent', + 'to' => 'customer_user_agent', + 'cast' => 'null', + ), + array( + 'from' => 'created_via', + 'to' => 'created_via', + 'cast' => 'null', + ), + array( + 'from' => 'customer_note', + 'to' => 'notes', + 'cast' => 'null', + ), + array( + 'from' => 'date_completed', + 'to' => 'date_completed', + 'cast' => 'null', + ), + array( + 'from' => 'date_paid', + 'to' => 'date_paid', + 'cast' => 'null', + ), + array( + 'from' => 'cart_hash', + 'to' => 'cart_hash', + 'cast' => 'null', + ), + array( + 'from' => 'order_stock_reduced', + 'to' => 'order_stock_reduced', + 'cast' => 'null', + ), + array( + 'from' => 'download_permissions_granted', + 'to' => 'download_permissions_granted', + 'cast' => 'null', + ), + array( + 'from' => 'new_order_email_sent', + 'to' => 'new_order_email_sent', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_sales', + 'to' => 'recorded_sales', + 'cast' => 'null', + ), + array( + 'from' => 'recorded_coupon_usage_counts', + 'to' => 'recorded_coupon_usage_counts', + 'cast' => 'null', + ), + array( + 'from' => 'number', + 'to' => 'number', + 'cast' => 'null', + ), + array( + 'from' => 'tax_lines', + 'to' => 'tax_lines', + 'cast' => 'null', + ), + array( + 'from' => 'shipping_lines', + 'to' => 'shipping_lines', + 'cast' => 'null', + ), + array( + 'from' => 'fee_lines', + 'to' => 'fee_lines', + 'cast' => 'null', + ), + array( + 'from' => 'coupon_lines', + 'to' => 'coupon_lines', + 'cast' => 'null', + ), + array( + 'from' => 'line_items', + 'to' => 'line_items', + 'cast' => 'null', + ), + array( + 'from' => 'currency', + 'to' => 'currency', + 'cast' => 'null', + ), + ), + array( + array( + 'from' => 'Contact_Owner', + 'to' => 'Owner', + 'cast' => 'inherit', + ), + ), + ), + ), +); diff --git a/assets/spinner.gif b/forms-bridge/assets/spinner.gif similarity index 100% rename from assets/spinner.gif rename to forms-bridge/assets/spinner.gif diff --git a/forms-bridge/data/country-phone-codes.php b/forms-bridge/data/country-phone-codes.php new file mode 100644 index 00000000..d997980e --- /dev/null +++ b/forms-bridge/data/country-phone-codes.php @@ -0,0 +1,240 @@ + __( 'United States', 'forms-bridge' ), + '7' => __( 'Russia', 'forms-bridge' ), + '20' => __( 'Egypt', 'forms-bridge' ), + '27' => __( 'South Africa', 'forms-bridge' ), + '30' => __( 'Greece', 'forms-bridge' ), + '31' => __( 'Netherlands', 'forms-bridge' ), + '32' => __( 'Belgium', 'forms-bridge' ), + '33' => __( 'France', 'forms-bridge' ), + '34' => __( 'Spain', 'forms-bridge' ), + '36' => __( 'Hungary', 'forms-bridge' ), + '39' => __( 'Italy', 'forms-bridge' ), + '40' => __( 'Romania', 'forms-bridge' ), + '41' => __( 'Switzerland', 'forms-bridge' ), + '43' => __( 'Austria', 'forms-bridge' ), + '44' => __( 'United Kingdom', 'forms-bridge' ), + '45' => __( 'Denmark', 'forms-bridge' ), + '46' => __( 'Sweden', 'forms-bridge' ), + '47' => __( 'Svalbard and Jan Mayen', 'forms-bridge' ), + '48' => __( 'Poland', 'forms-bridge' ), + '49' => __( 'Germany', 'forms-bridge' ), + '51' => __( 'Peru', 'forms-bridge' ), + '52' => __( 'Mexico', 'forms-bridge' ), + '53' => __( 'Cuba', 'forms-bridge' ), + '54' => __( 'Argentina', 'forms-bridge' ), + '55' => __( 'Brazil', 'forms-bridge' ), + '56' => __( 'Chile', 'forms-bridge' ), + '57' => __( 'Colombia', 'forms-bridge' ), + '58' => __( 'Venezuela', 'forms-bridge' ), + '60' => __( 'Malaysia', 'forms-bridge' ), + '61' => __( 'Cocos Islands', 'forms-bridge' ), + '62' => __( 'Indonesia', 'forms-bridge' ), + '63' => __( 'Philippines', 'forms-bridge' ), + '64' => __( 'Pitcairn', 'forms-bridge' ), + '65' => __( 'Singapore', 'forms-bridge' ), + '66' => __( 'Thailand', 'forms-bridge' ), + '81' => __( 'Japan', 'forms-bridge' ), + '82' => __( 'South Korea', 'forms-bridge' ), + '84' => __( 'Vietnam', 'forms-bridge' ), + '86' => __( 'China', 'forms-bridge' ), + '90' => __( 'Turkey', 'forms-bridge' ), + '91' => __( 'India', 'forms-bridge' ), + '92' => __( 'Pakistan', 'forms-bridge' ), + '93' => __( 'Afghanistan', 'forms-bridge' ), + '94' => __( 'Sri Lanka', 'forms-bridge' ), + '95' => __( 'Myanmar', 'forms-bridge' ), + '98' => __( 'Iran', 'forms-bridge' ), + '211' => __( 'South Sudan', 'forms-bridge' ), + '212' => __( 'Western Sahara', 'forms-bridge' ), + '213' => __( 'Algeria', 'forms-bridge' ), + '216' => __( 'Tunisia', 'forms-bridge' ), + '218' => __( 'Libya', 'forms-bridge' ), + '220' => __( 'Gambia', 'forms-bridge' ), + '221' => __( 'Senegal', 'forms-bridge' ), + '222' => __( 'Mauritania', 'forms-bridge' ), + '223' => __( 'Mali', 'forms-bridge' ), + '224' => __( 'Guinea', 'forms-bridge' ), + '225' => __( 'Ivory Coast', 'forms-bridge' ), + '226' => __( 'Burkina Faso', 'forms-bridge' ), + '227' => __( 'Niger', 'forms-bridge' ), + '228' => __( 'Togo', 'forms-bridge' ), + '229' => __( 'Benin', 'forms-bridge' ), + '230' => __( 'Mauritius', 'forms-bridge' ), + '231' => __( 'Liberia', 'forms-bridge' ), + '232' => __( 'Sierra Leone', 'forms-bridge' ), + '233' => __( 'Ghana', 'forms-bridge' ), + '234' => __( 'Nigeria', 'forms-bridge' ), + '235' => __( 'Chad', 'forms-bridge' ), + '236' => __( 'Central African Republic', 'forms-bridge' ), + '237' => __( 'Cameroon', 'forms-bridge' ), + '238' => __( 'Cape Verde', 'forms-bridge' ), + '239' => __( 'Sao Tome and Principe', 'forms-bridge' ), + '240' => __( 'Equatorial Guinea', 'forms-bridge' ), + '241' => __( 'Gabon', 'forms-bridge' ), + '242' => __( 'Republic of the Congo', 'forms-bridge' ), + '243' => __( 'Democratic Republic of the Congo', 'forms-bridge' ), + '244' => __( 'Angola', 'forms-bridge' ), + '245' => __( 'Guinea-Bissau', 'forms-bridge' ), + '246' => __( 'British Indian Ocean Territory', 'forms-bridge' ), + '248' => __( 'Seychelles', 'forms-bridge' ), + '249' => __( 'Sudan', 'forms-bridge' ), + '250' => __( 'Rwanda', 'forms-bridge' ), + '251' => __( 'Ethiopia', 'forms-bridge' ), + '252' => __( 'Somalia', 'forms-bridge' ), + '253' => __( 'Djibouti', 'forms-bridge' ), + '254' => __( 'Kenya', 'forms-bridge' ), + '255' => __( 'Tanzania', 'forms-bridge' ), + '256' => __( 'Uganda', 'forms-bridge' ), + '257' => __( 'Burundi', 'forms-bridge' ), + '258' => __( 'Mozambique', 'forms-bridge' ), + '260' => __( 'Zambia', 'forms-bridge' ), + '261' => __( 'Madagascar', 'forms-bridge' ), + '262' => __( 'Reunion', 'forms-bridge' ), + '263' => __( 'Zimbabwe', 'forms-bridge' ), + '264' => __( 'Namibia', 'forms-bridge' ), + '265' => __( 'Malawi', 'forms-bridge' ), + '266' => __( 'Lesotho', 'forms-bridge' ), + '267' => __( 'Botswana', 'forms-bridge' ), + '268' => __( 'Swaziland', 'forms-bridge' ), + '269' => __( 'Comoros', 'forms-bridge' ), + '290' => __( 'Saint Helena', 'forms-bridge' ), + '291' => __( 'Eritrea', 'forms-bridge' ), + '297' => __( 'Aruba', 'forms-bridge' ), + '298' => __( 'Faroe Islands', 'forms-bridge' ), + '299' => __( 'Greenland', 'forms-bridge' ), + '350' => __( 'Gibraltar', 'forms-bridge' ), + '351' => __( 'Portugal', 'forms-bridge' ), + '352' => __( 'Luxembourg', 'forms-bridge' ), + '353' => __( 'Ireland', 'forms-bridge' ), + '354' => __( 'Iceland', 'forms-bridge' ), + '355' => __( 'Albania', 'forms-bridge' ), + '356' => __( 'Malta', 'forms-bridge' ), + '357' => __( 'Cyprus', 'forms-bridge' ), + '358' => __( 'Finland', 'forms-bridge' ), + '359' => __( 'Bulgaria', 'forms-bridge' ), + '370' => __( 'Lithuania', 'forms-bridge' ), + '371' => __( 'Latvia', 'forms-bridge' ), + '372' => __( 'Estonia', 'forms-bridge' ), + '373' => __( 'Moldova', 'forms-bridge' ), + '374' => __( 'Armenia', 'forms-bridge' ), + '375' => __( 'Belarus', 'forms-bridge' ), + '376' => __( 'Andorra', 'forms-bridge' ), + '377' => __( 'Monaco', 'forms-bridge' ), + '378' => __( 'San Marino', 'forms-bridge' ), + '379' => __( 'Vatican', 'forms-bridge' ), + '380' => __( 'Ukraine', 'forms-bridge' ), + '381' => __( 'Serbia', 'forms-bridge' ), + '382' => __( 'Montenegro', 'forms-bridge' ), + '383' => __( 'Kosovo', 'forms-bridge' ), + '385' => __( 'Croatia', 'forms-bridge' ), + '386' => __( 'Slovenia', 'forms-bridge' ), + '387' => __( 'Bosnia and Herzegovina', 'forms-bridge' ), + '389' => __( 'Macedonia', 'forms-bridge' ), + '420' => __( 'Czech Republic', 'forms-bridge' ), + '421' => __( 'Slovakia', 'forms-bridge' ), + '423' => __( 'Liechtenstein', 'forms-bridge' ), + '500' => __( 'Falkland Islands', 'forms-bridge' ), + '501' => __( 'Belize', 'forms-bridge' ), + '502' => __( 'Guatemala', 'forms-bridge' ), + '503' => __( 'El Salvador', 'forms-bridge' ), + '504' => __( 'Honduras', 'forms-bridge' ), + '505' => __( 'Nicaragua', 'forms-bridge' ), + '506' => __( 'Costa Rica', 'forms-bridge' ), + '507' => __( 'Panama', 'forms-bridge' ), + '508' => __( 'Saint Pierre and Miquelon', 'forms-bridge' ), + '509' => __( 'Haiti', 'forms-bridge' ), + '590' => __( 'Saint Martin', 'forms-bridge' ), + '591' => __( 'Bolivia', 'forms-bridge' ), + '592' => __( 'Guyana', 'forms-bridge' ), + '593' => __( 'Ecuador', 'forms-bridge' ), + '595' => __( 'Paraguay', 'forms-bridge' ), + '597' => __( 'Suriname', 'forms-bridge' ), + '598' => __( 'Uruguay', 'forms-bridge' ), + '599' => __( 'Netherlands Antilles', 'forms-bridge' ), + '670' => __( 'East Timor', 'forms-bridge' ), + '672' => __( 'Antarctica', 'forms-bridge' ), + '673' => __( 'Brunei', 'forms-bridge' ), + '674' => __( 'Nauru', 'forms-bridge' ), + '675' => __( 'Papua New Guinea', 'forms-bridge' ), + '676' => __( 'Tonga', 'forms-bridge' ), + '677' => __( 'Solomon Islands', 'forms-bridge' ), + '678' => __( 'Vanuatu', 'forms-bridge' ), + '679' => __( 'Fiji', 'forms-bridge' ), + '680' => __( 'Palau', 'forms-bridge' ), + '681' => __( 'Wallis and Futuna', 'forms-bridge' ), + '682' => __( 'Cook Islands', 'forms-bridge' ), + '683' => __( 'Niue', 'forms-bridge' ), + '685' => __( 'Samoa', 'forms-bridge' ), + '686' => __( 'Kiribati', 'forms-bridge' ), + '687' => __( 'New Caledonia', 'forms-bridge' ), + '688' => __( 'Tuvalu', 'forms-bridge' ), + '689' => __( 'French Polynesia', 'forms-bridge' ), + '690' => __( 'Tokelau', 'forms-bridge' ), + '691' => __( 'Micronesia', 'forms-bridge' ), + '692' => __( 'Marshall Islands', 'forms-bridge' ), + '850' => __( 'North Korea', 'forms-bridge' ), + '852' => __( 'Hong Kong', 'forms-bridge' ), + '853' => __( 'Macau', 'forms-bridge' ), + '855' => __( 'Cambodia', 'forms-bridge' ), + '856' => __( 'Laos', 'forms-bridge' ), + '880' => __( 'Bangladesh', 'forms-bridge' ), + '886' => __( 'Taiwan', 'forms-bridge' ), + '960' => __( 'Maldives', 'forms-bridge' ), + '961' => __( 'Lebanon', 'forms-bridge' ), + '962' => __( 'Jordan', 'forms-bridge' ), + '963' => __( 'Syria', 'forms-bridge' ), + '964' => __( 'Iraq', 'forms-bridge' ), + '965' => __( 'Kuwait', 'forms-bridge' ), + '966' => __( 'Saudi Arabia', 'forms-bridge' ), + '967' => __( 'Yemen', 'forms-bridge' ), + '968' => __( 'Oman', 'forms-bridge' ), + '970' => __( 'Palestine', 'forms-bridge' ), + '971' => __( 'United Arab Emirates', 'forms-bridge' ), + '972' => __( 'Israel', 'forms-bridge' ), + '973' => __( 'Bahrain', 'forms-bridge' ), + '974' => __( 'Qatar', 'forms-bridge' ), + '975' => __( 'Bhutan', 'forms-bridge' ), + '976' => __( 'Mongolia', 'forms-bridge' ), + '977' => __( 'Nepal', 'forms-bridge' ), + '992' => __( 'Tajikistan', 'forms-bridge' ), + '993' => __( 'Turkmenistan', 'forms-bridge' ), + '994' => __( 'Azerbaijan', 'forms-bridge' ), + '995' => __( 'Georgia', 'forms-bridge' ), + '996' => __( 'Kyrgyzstan', 'forms-bridge' ), + '998' => __( 'Uzbekistan', 'forms-bridge' ), + '1-684' => __( 'American Samoa', 'forms-bridge' ), + '1-264' => __( 'Anguilla', 'forms-bridge' ), + '1-268' => __( 'Antigua and Barbuda', 'forms-bridge' ), + '1-242' => __( 'Bahamas', 'forms-bridge' ), + '1-246' => __( 'Barbados', 'forms-bridge' ), + '1-441' => __( 'Bermuda', 'forms-bridge' ), + '1-284' => __( 'British Virgin Islands', 'forms-bridge' ), + '1-345' => __( 'Cayman Islands', 'forms-bridge' ), + '1-767' => __( 'Dominica', 'forms-bridge' ), + '1-809, 1-829, 1-849' => __( 'Dominican Republic', 'forms-bridge' ), + '1-473' => __( 'Grenada', 'forms-bridge' ), + '1-671' => __( 'Guam', 'forms-bridge' ), + '44-1481' => __( 'Guernsey', 'forms-bridge' ), + '44-1624' => __( 'Isle of Man', 'forms-bridge' ), + '1-876' => __( 'Jamaica', 'forms-bridge' ), + '44-1534' => __( 'Jersey', 'forms-bridge' ), + '1-664' => __( 'Montserrat', 'forms-bridge' ), + '1-670' => __( 'Northern Mariana Islands', 'forms-bridge' ), + '1-787, 1-939' => __( 'Puerto Rico', 'forms-bridge' ), + '1-869' => __( 'Saint Kitts and Nevis', 'forms-bridge' ), + '1-758' => __( 'Saint Lucia', 'forms-bridge' ), + '1-784' => __( 'Saint Vincent and the Grenadines', 'forms-bridge' ), + '1-721' => __( 'Sint Maarten', 'forms-bridge' ), + '1-868' => __( 'Trinidad and Tobago', 'forms-bridge' ), + '1-649' => __( 'Turks and Caicos Islands', 'forms-bridge' ), + '1-340' => __( 'U.S. Virgin Islands', 'forms-bridge' ), +); diff --git a/forms-bridge/data/iso2-countries.php b/forms-bridge/data/iso2-countries.php new file mode 100644 index 00000000..923e7425 --- /dev/null +++ b/forms-bridge/data/iso2-countries.php @@ -0,0 +1,259 @@ + __( 'Andorra', 'forms-bridge' ), + 'AE' => __( 'United Arab Emirates', 'forms-bridge' ), + 'AF' => __( 'Afghanistan', 'forms-bridge' ), + 'AG' => __( 'Antigua and Barbuda', 'forms-bridge' ), + 'AI' => __( 'Anguilla', 'forms-bridge' ), + 'AL' => __( 'Albania', 'forms-bridge' ), + 'AM' => __( 'Armenia', 'forms-bridge' ), + 'AO' => __( 'Angola', 'forms-bridge' ), + 'AQ' => __( 'Antarctica', 'forms-bridge' ), + 'AR' => __( 'Argentina', 'forms-bridge' ), + 'AS' => __( 'American Samoa', 'forms-bridge' ), + 'AT' => __( 'Austria', 'forms-bridge' ), + 'AU' => __( 'Australia', 'forms-bridge' ), + 'AW' => __( 'Aruba', 'forms-bridge' ), + 'AX' => __( 'Åland Islands', 'forms-bridge' ), + 'AZ' => __( 'Azerbaijan', 'forms-bridge' ), + 'BA' => __( 'Bosnia and Herzegovina', 'forms-bridge' ), + 'BB' => __( 'Barbados', 'forms-bridge' ), + 'BD' => __( 'Bangladesh', 'forms-bridge' ), + 'BE' => __( 'Belgium', 'forms-bridge' ), + 'BF' => __( 'Burkina Faso', 'forms-bridge' ), + 'BG' => __( 'Bulgaria', 'forms-bridge' ), + 'BH' => __( 'Bahrain', 'forms-bridge' ), + 'BI' => __( 'Burundi', 'forms-bridge' ), + 'BJ' => __( 'Benin', 'forms-bridge' ), + 'BL' => __( 'Saint Barthélémy', 'forms-bridge' ), + 'BM' => __( 'Bermuda', 'forms-bridge' ), + 'BN' => __( 'Brunei Darussalam', 'forms-bridge' ), + 'BO' => __( 'Bolivia', 'forms-bridge' ), + 'BQ' => __( 'Bonaire, Sint Eustatius and Saba', 'forms-bridge' ), + 'BR' => __( 'Brazil', 'forms-bridge' ), + 'BS' => __( 'Bahamas', 'forms-bridge' ), + 'BT' => __( 'Bhutan', 'forms-bridge' ), + 'BV' => __( 'Bouvet Island', 'forms-bridge' ), + 'BW' => __( 'Botswana', 'forms-bridge' ), + 'BY' => __( 'Belarus', 'forms-bridge' ), + 'BZ' => __( 'Belize', 'forms-bridge' ), + 'CA' => __( 'Canada', 'forms-bridge' ), + 'CC' => __( 'Cocos (Keeling) Islands', 'forms-bridge' ), + 'CF' => __( 'Central African Republic', 'forms-bridge' ), + 'CD' => __( 'Democratic Republic of the Congo', 'forms-bridge' ), + 'CG' => __( 'Congo', 'forms-bridge' ), + 'CH' => __( 'Switzerland', 'forms-bridge' ), + 'CI' => __( 'Côte d\'Ivoire', 'forms-bridge' ), + 'CK' => __( 'Cook Islands', 'forms-bridge' ), + 'CL' => __( 'Chile', 'forms-bridge' ), + 'CM' => __( 'Cameroon', 'forms-bridge' ), + 'CN' => __( 'China', 'forms-bridge' ), + 'CO' => __( 'Colombia', 'forms-bridge' ), + 'CR' => __( 'Costa Rica', 'forms-bridge' ), + 'CU' => __( 'Cuba', 'forms-bridge' ), + 'CV' => __( 'Cape Verde', 'forms-bridge' ), + 'CW' => __( 'Curaçao', 'forms-bridge' ), + 'CX' => __( 'Christmas Island', 'forms-bridge' ), + 'CY' => __( 'Cyprus', 'forms-bridge' ), + 'CZ' => __( 'Czech Republic', 'forms-bridge' ), + 'DE' => __( 'Germany', 'forms-bridge' ), + 'DJ' => __( 'Djibouti', 'forms-bridge' ), + 'DK' => __( 'Denmark', 'forms-bridge' ), + 'DM' => __( 'Dominica', 'forms-bridge' ), + 'DO' => __( 'Dominican Republic', 'forms-bridge' ), + 'DZ' => __( 'Algeria', 'forms-bridge' ), + 'EC' => __( 'Ecuador', 'forms-bridge' ), + 'EE' => __( 'Estonia', 'forms-bridge' ), + 'EG' => __( 'Egypt', 'forms-bridge' ), + 'EH' => __( 'Western Sahara', 'forms-bridge' ), + 'ER' => __( 'Eritrea', 'forms-bridge' ), + 'ES' => __( 'Spain', 'forms-bridge' ), + 'ET' => __( 'Ethiopia', 'forms-bridge' ), + 'FI' => __( 'Finland', 'forms-bridge' ), + 'FJ' => __( 'Fiji', 'forms-bridge' ), + 'FK' => __( 'Falkland Islands', 'forms-bridge' ), + 'FM' => __( 'Micronesia', 'forms-bridge' ), + 'FO' => __( 'Faroe Islands', 'forms-bridge' ), + 'FR' => __( 'France', 'forms-bridge' ), + 'GA' => __( 'Gabon', 'forms-bridge' ), + 'GD' => __( 'Grenada', 'forms-bridge' ), + 'GE' => __( 'Georgia', 'forms-bridge' ), + 'GF' => __( 'French Guiana', 'forms-bridge' ), + 'GH' => __( 'Ghana', 'forms-bridge' ), + 'GI' => __( 'Gibraltar', 'forms-bridge' ), + 'GG' => __( 'Guernsey', 'forms-bridge' ), + 'GL' => __( 'Greenland', 'forms-bridge' ), + 'GM' => __( 'Gambia', 'forms-bridge' ), + 'GN' => __( 'Guinea', 'forms-bridge' ), + 'GP' => __( 'Guadeloupe', 'forms-bridge' ), + 'GQ' => __( 'Equatorial Guinea', 'forms-bridge' ), + 'GR' => __( 'Greece', 'forms-bridge' ), + 'GS' => __( 'South Georgia and the South Sandwich Islands', 'forms-bridge' ), + 'GT' => __( 'Guatemala', 'forms-bridge' ), + 'GU' => __( 'Guam', 'forms-bridge' ), + 'GW' => __( 'Guinea-Bissau', 'forms-bridge' ), + 'GY' => __( 'Guyana', 'forms-bridge' ), + 'HK' => __( 'Hong Kong', 'forms-bridge' ), + 'HM' => __( 'Heard Island and McDonald Islands', 'forms-bridge' ), + 'HN' => __( 'Honduras', 'forms-bridge' ), + 'HR' => __( 'Croatia', 'forms-bridge' ), + 'HT' => __( 'Haiti', 'forms-bridge' ), + 'HU' => __( 'Hungary', 'forms-bridge' ), + 'ID' => __( 'Indonesia', 'forms-bridge' ), + 'IE' => __( 'Ireland', 'forms-bridge' ), + 'IL' => __( 'Israel', 'forms-bridge' ), + 'IM' => __( 'Isle of Man', 'forms-bridge' ), + 'IN' => __( 'India', 'forms-bridge' ), + 'IO' => __( 'British Indian Ocean Territory', 'forms-bridge' ), + 'IQ' => __( 'Iraq', 'forms-bridge' ), + 'IR' => __( 'Iran', 'forms-bridge' ), + 'IS' => __( 'Iceland', 'forms-bridge' ), + 'IT' => __( 'Italy', 'forms-bridge' ), + 'JE' => __( 'Jersey', 'forms-bridge' ), + 'JM' => __( 'Jamaica', 'forms-bridge' ), + 'JO' => __( 'Jordan', 'forms-bridge' ), + 'JP' => __( 'Japan', 'forms-bridge' ), + 'KE' => __( 'Kenya', 'forms-bridge' ), + 'KG' => __( 'Kyrgyzstan', 'forms-bridge' ), + 'KH' => __( 'Cambodia', 'forms-bridge' ), + 'KI' => __( 'Kiribati', 'forms-bridge' ), + 'KM' => __( 'Comoros', 'forms-bridge' ), + 'KN' => __( 'Saint Kitts and Nevis', 'forms-bridge' ), + 'KP' => __( 'North Korea', 'forms-bridge' ), + 'KR' => __( 'South Korea', 'forms-bridge' ), + 'KW' => __( 'Kuwait', 'forms-bridge' ), + 'KY' => __( 'Cayman Islands', 'forms-bridge' ), + 'KZ' => __( 'Kazakhstan', 'forms-bridge' ), + 'LA' => __( 'Laos', 'forms-bridge' ), + 'LB' => __( 'Lebanon', 'forms-bridge' ), + 'LC' => __( 'Saint Lucia', 'forms-bridge' ), + 'LI' => __( 'Liechtenstein', 'forms-bridge' ), + 'LK' => __( 'Sri Lanka', 'forms-bridge' ), + 'LR' => __( 'Liberia', 'forms-bridge' ), + 'LS' => __( 'Lesotho', 'forms-bridge' ), + 'LT' => __( 'Lithuania', 'forms-bridge' ), + 'LU' => __( 'Luxembourg', 'forms-bridge' ), + 'LV' => __( 'Latvia', 'forms-bridge' ), + 'LY' => __( 'Libya', 'forms-bridge' ), + 'MA' => __( 'Morocco', 'forms-bridge' ), + 'MC' => __( 'Monaco', 'forms-bridge' ), + 'MD' => __( 'Moldova', 'forms-bridge' ), + 'ME' => __( 'Montenegro', 'forms-bridge' ), + 'MF' => __( 'Saint Martin (French part)', 'forms-bridge' ), + 'MG' => __( 'Madagascar', 'forms-bridge' ), + 'MH' => __( 'Marshall Islands', 'forms-bridge' ), + 'MK' => __( 'Macedonia, the former Yugoslav Republic of', 'forms-bridge' ), + 'ML' => __( 'Mali', 'forms-bridge' ), + 'MM' => __( 'Myanmar', 'forms-bridge' ), + 'MN' => __( 'Mongolia', 'forms-bridge' ), + 'MO' => __( 'Macau', 'forms-bridge' ), + 'MP' => __( 'Northern Mariana Islands', 'forms-bridge' ), + 'MQ' => __( 'Martinique', 'forms-bridge' ), + 'MR' => __( 'Mauritania', 'forms-bridge' ), + 'MS' => __( 'Montserrat', 'forms-bridge' ), + 'MT' => __( 'Malta', 'forms-bridge' ), + 'MU' => __( 'Mauritius', 'forms-bridge' ), + 'MV' => __( 'Maldives', 'forms-bridge' ), + 'MW' => __( 'Malawi', 'forms-bridge' ), + 'MX' => __( 'Mexico', 'forms-bridge' ), + 'MY' => __( 'Malaysia', 'forms-bridge' ), + 'MZ' => __( 'Mozambique', 'forms-bridge' ), + 'NA' => __( 'Namibia', 'forms-bridge' ), + 'NC' => __( 'New Caledonia', 'forms-bridge' ), + 'NE' => __( 'Niger', 'forms-bridge' ), + 'NF' => __( 'Norfolk Island', 'forms-bridge' ), + 'NG' => __( 'Nigeria', 'forms-bridge' ), + 'NI' => __( 'Nicaragua', 'forms-bridge' ), + 'NL' => __( 'Netherlands', 'forms-bridge' ), + 'NO' => __( 'Norway', 'forms-bridge' ), + 'NP' => __( 'Nepal', 'forms-bridge' ), + 'NR' => __( 'Nauru', 'forms-bridge' ), + 'NU' => __( 'Niue', 'forms-bridge' ), + 'NZ' => __( 'New Zealand', 'forms-bridge' ), + 'OM' => __( 'Oman', 'forms-bridge' ), + 'PA' => __( 'Panama', 'forms-bridge' ), + 'PE' => __( 'Peru', 'forms-bridge' ), + 'PF' => __( 'French Polynesia', 'forms-bridge' ), + 'PG' => __( 'Papua New Guinea', 'forms-bridge' ), + 'PH' => __( 'Philippines', 'forms-bridge' ), + 'PK' => __( 'Pakistan', 'forms-bridge' ), + 'PL' => __( 'Poland', 'forms-bridge' ), + 'PM' => __( 'Saint Pierre and Miquelon', 'forms-bridge' ), + 'PN' => __( 'Pitcairn Islands', 'forms-bridge' ), + 'PR' => __( 'Puerto Rico', 'forms-bridge' ), + 'PS' => __( 'State of Palestine', 'forms-bridge' ), + 'PT' => __( 'Portugal', 'forms-bridge' ), + 'PW' => __( 'Palau', 'forms-bridge' ), + 'PY' => __( 'Paraguay', 'forms-bridge' ), + 'QA' => __( 'Qatar', 'forms-bridge' ), + 'RE' => __( 'Réunion', 'forms-bridge' ), + 'RO' => __( 'Romania', 'forms-bridge' ), + 'RS' => __( 'Serbia', 'forms-bridge' ), + 'RU' => __( 'Russian Federation', 'forms-bridge' ), + 'RW' => __( 'Rwanda', 'forms-bridge' ), + 'SA' => __( 'Saudi Arabia', 'forms-bridge' ), + 'SB' => __( 'Solomon Islands', 'forms-bridge' ), + 'SC' => __( 'Seychelles', 'forms-bridge' ), + 'SD' => __( 'Sudan', 'forms-bridge' ), + 'SE' => __( 'Sweden', 'forms-bridge' ), + 'SG' => __( 'Singapore', 'forms-bridge' ), + 'SH' => __( 'Saint Helena, Ascension and Tristan da Cunha', 'forms-bridge' ), + 'SI' => __( 'Slovenia', 'forms-bridge' ), + 'SJ' => __( 'Svalbard and Jan Mayen', 'forms-bridge' ), + 'SK' => __( 'Slovakia', 'forms-bridge' ), + 'SL' => __( 'Sierra Leone', 'forms-bridge' ), + 'SM' => __( 'San Marino', 'forms-bridge' ), + 'SN' => __( 'Senegal', 'forms-bridge' ), + 'SO' => __( 'Somalia', 'forms-bridge' ), + 'SR' => __( 'Suriname', 'forms-bridge' ), + 'SS' => __( 'South Sudan', 'forms-bridge' ), + 'ST' => __( 'São Tomé and Príncipe', 'forms-bridge' ), + 'SV' => __( 'El Salvador', 'forms-bridge' ), + 'SX' => __( 'Sint Maarten (Dutch part)', 'forms-bridge' ), + 'SY' => __( 'Syria', 'forms-bridge' ), + 'SZ' => __( 'Swaziland', 'forms-bridge' ), + 'TC' => __( 'Turks and Caicos Islands', 'forms-bridge' ), + 'TD' => __( 'Chad', 'forms-bridge' ), + 'TF' => __( 'French Southern Territories', 'forms-bridge' ), + 'TG' => __( 'Togo', 'forms-bridge' ), + 'TH' => __( 'Thailand', 'forms-bridge' ), + 'TJ' => __( 'Tajikistan', 'forms-bridge' ), + 'TK' => __( 'Tokelau', 'forms-bridge' ), + 'TM' => __( 'Turkmenistan', 'forms-bridge' ), + 'TN' => __( 'Tunisia', 'forms-bridge' ), + 'TO' => __( 'Tonga', 'forms-bridge' ), + 'TL' => __( 'Timor-Leste', 'forms-bridge' ), + 'TR' => __( 'Turkey', 'forms-bridge' ), + 'TT' => __( 'Trinidad and Tobago', 'forms-bridge' ), + 'TV' => __( 'Tuvalu', 'forms-bridge' ), + 'TW' => __( 'Taiwan', 'forms-bridge' ), + 'TZ' => __( 'Tanzania', 'forms-bridge' ), + 'UA' => __( 'Ukraine', 'forms-bridge' ), + 'UG' => __( 'Uganda', 'forms-bridge' ), + 'GB' => __( 'United Kingdom', 'forms-bridge' ), + 'UM' => __( 'USA Minor Outlying Islands', 'forms-bridge' ), + 'US' => __( 'United States', 'forms-bridge' ), + 'UY' => __( 'Uruguay', 'forms-bridge' ), + 'UZ' => __( 'Uzbekistan', 'forms-bridge' ), + 'VA' => __( 'Holy See (Vatican City State)', 'forms-bridge' ), + 'VC' => __( 'Saint Vincent and the Grenadines', 'forms-bridge' ), + 'VE' => __( 'Venezuela', 'forms-bridge' ), + 'VG' => __( 'Virgin Islands (British)', 'forms-bridge' ), + 'VI' => __( 'Virgin Islands (USA)', 'forms-bridge' ), + 'VN' => __( 'Vietnam', 'forms-bridge' ), + 'VU' => __( 'Vanuatu', 'forms-bridge' ), + 'WF' => __( 'Wallis and Futuna', 'forms-bridge' ), + 'WS' => __( 'Samoa', 'forms-bridge' ), + 'YE' => __( 'Yemen', 'forms-bridge' ), + 'YT' => __( 'Mayotte', 'forms-bridge' ), + 'ZA' => __( 'South Africa', 'forms-bridge' ), + 'ZM' => __( 'Zambia', 'forms-bridge' ), + 'ZW' => __( 'Zimbabwe', 'forms-bridge' ), + 'XK' => __( 'Kosovo', 'forms-bridge' ), +); diff --git a/forms-bridge/data/iso3-countries.php b/forms-bridge/data/iso3-countries.php new file mode 100644 index 00000000..620c5a43 --- /dev/null +++ b/forms-bridge/data/iso3-countries.php @@ -0,0 +1,249 @@ + __( 'Afghanistan', 'forms-bridge' ), + 'ALB' => __( 'Albania', 'forms-bridge' ), + 'DZA' => __( 'Algeria', 'forms-bridge' ), + 'ASM' => __( 'American Samoa', 'forms-bridge' ), + 'AND' => __( 'Andorra', 'forms-bridge' ), + 'AGO' => __( 'Angola', 'forms-bridge' ), + 'AIA' => __( 'Anguilla', 'forms-bridge' ), + 'ATA' => __( 'Antarctica', 'forms-bridge' ), + 'ATG' => __( 'Antigua and Barbuda', 'forms-bridge' ), + 'ARG' => __( 'Argentina', 'forms-bridge' ), + 'ARM' => __( 'Armenia', 'forms-bridge' ), + 'ABW' => __( 'Aruba', 'forms-bridge' ), + 'AUS' => __( 'Australia', 'forms-bridge' ), + 'AUT' => __( 'Austria', 'forms-bridge' ), + 'AZE' => __( 'Azerbaijan', 'forms-bridge' ), + 'BHS' => __( 'Bahamas', 'forms-bridge' ), + 'BHR' => __( 'Bahrain', 'forms-bridge' ), + 'BGD' => __( 'Bangladesh', 'forms-bridge' ), + 'BRB' => __( 'Barbados', 'forms-bridge' ), + 'BLR' => __( 'Belarus', 'forms-bridge' ), + 'BEL' => __( 'Belgium', 'forms-bridge' ), + 'BLZ' => __( 'Belize', 'forms-bridge' ), + 'BEN' => __( 'Benin', 'forms-bridge' ), + 'BMU' => __( 'Bermuda', 'forms-bridge' ), + 'BTN' => __( 'Bhutan', 'forms-bridge' ), + 'BOL' => __( 'Bolivia', 'forms-bridge' ), + 'BIH' => __( 'Bosnia and Herzegovina', 'forms-bridge' ), + 'BWA' => __( 'Botswana', 'forms-bridge' ), + 'BRA' => __( 'Brazil', 'forms-bridge' ), + 'IOT' => __( 'British Indian Ocean Territory', 'forms-bridge' ), + 'VGB' => __( 'British Virgin Islands', 'forms-bridge' ), + 'BRN' => __( 'Brunei', 'forms-bridge' ), + 'BGR' => __( 'Bulgaria', 'forms-bridge' ), + 'BFA' => __( 'Burkina Faso', 'forms-bridge' ), + 'BDI' => __( 'Burundi', 'forms-bridge' ), + 'KHM' => __( 'Cambodia', 'forms-bridge' ), + 'CMR' => __( 'Cameroon', 'forms-bridge' ), + 'CAN' => __( 'Canada', 'forms-bridge' ), + 'CPV' => __( 'Cape Verde', 'forms-bridge' ), + 'CYM' => __( 'Cayman Islands', 'forms-bridge' ), + 'CAF' => __( 'Central African Republic', 'forms-bridge' ), + 'TCD' => __( 'Chad', 'forms-bridge' ), + 'CHL' => __( 'Chile', 'forms-bridge' ), + 'CHN' => __( 'China', 'forms-bridge' ), + 'CXR' => __( 'Christmas Island', 'forms-bridge' ), + 'CCK' => __( 'Cocos Islands', 'forms-bridge' ), + 'COL' => __( 'Colombia', 'forms-bridge' ), + 'COM' => __( 'Comoros', 'forms-bridge' ), + 'COK' => __( 'Cook Islands', 'forms-bridge' ), + 'CRI' => __( 'Costa Rica', 'forms-bridge' ), + 'HRV' => __( 'Croatia', 'forms-bridge' ), + 'CUB' => __( 'Cuba', 'forms-bridge' ), + 'CUW' => __( 'Curacao', 'forms-bridge' ), + 'CYP' => __( 'Cyprus', 'forms-bridge' ), + 'CZE' => __( 'Czech Republic', 'forms-bridge' ), + 'COD' => __( 'Democratic Republic of the Congo', 'forms-bridge' ), + 'DNK' => __( 'Denmark', 'forms-bridge' ), + 'DJI' => __( 'Djibouti', 'forms-bridge' ), + 'DMA' => __( 'Dominica', 'forms-bridge' ), + 'DOM' => __( 'Dominican Republic', 'forms-bridge' ), + 'TLS' => __( 'East Timor', 'forms-bridge' ), + 'ECU' => __( 'Ecuador', 'forms-bridge' ), + 'EGY' => __( 'Egypt', 'forms-bridge' ), + 'SLV' => __( 'El Salvador', 'forms-bridge' ), + 'GNQ' => __( 'Equatorial Guinea', 'forms-bridge' ), + 'ERI' => __( 'Eritrea', 'forms-bridge' ), + 'EST' => __( 'Estonia', 'forms-bridge' ), + 'ETH' => __( 'Ethiopia', 'forms-bridge' ), + 'FLK' => __( 'Falkland Islands', 'forms-bridge' ), + 'FRO' => __( 'Faroe Islands', 'forms-bridge' ), + 'FJI' => __( 'Fiji', 'forms-bridge' ), + 'FIN' => __( 'Finland', 'forms-bridge' ), + 'FRA' => __( 'France', 'forms-bridge' ), + 'PYF' => __( 'French Polynesia', 'forms-bridge' ), + 'GAB' => __( 'Gabon', 'forms-bridge' ), + 'GMB' => __( 'Gambia', 'forms-bridge' ), + 'GEO' => __( 'Georgia', 'forms-bridge' ), + 'DEU' => __( 'Germany', 'forms-bridge' ), + 'GHA' => __( 'Ghana', 'forms-bridge' ), + 'GIB' => __( 'Gibraltar', 'forms-bridge' ), + 'GRC' => __( 'Greece', 'forms-bridge' ), + 'GRL' => __( 'Greenland', 'forms-bridge' ), + 'GRD' => __( 'Grenada', 'forms-bridge' ), + 'GUM' => __( 'Guam', 'forms-bridge' ), + 'GTM' => __( 'Guatemala', 'forms-bridge' ), + 'GGY' => __( 'Guernsey', 'forms-bridge' ), + 'GIN' => __( 'Guinea', 'forms-bridge' ), + 'GNB' => __( 'Guinea-Bissau', 'forms-bridge' ), + 'GUY' => __( 'Guyana', 'forms-bridge' ), + 'HTI' => __( 'Haiti', 'forms-bridge' ), + 'HND' => __( 'Honduras', 'forms-bridge' ), + 'HKG' => __( 'Hong Kong', 'forms-bridge' ), + 'HUN' => __( 'Hungary', 'forms-bridge' ), + 'ISL' => __( 'Iceland', 'forms-bridge' ), + 'IND' => __( 'India', 'forms-bridge' ), + 'IDN' => __( 'Indonesia', 'forms-bridge' ), + 'IRN' => __( 'Iran', 'forms-bridge' ), + 'IRQ' => __( 'Iraq', 'forms-bridge' ), + 'IRL' => __( 'Ireland', 'forms-bridge' ), + 'IMN' => __( 'Isle of Man', 'forms-bridge' ), + 'ISR' => __( 'Israel', 'forms-bridge' ), + 'ITA' => __( 'Italy', 'forms-bridge' ), + 'CIV' => __( 'Ivory Coast', 'forms-bridge' ), + 'JAM' => __( 'Jamaica', 'forms-bridge' ), + 'JPN' => __( 'Japan', 'forms-bridge' ), + 'JEY' => __( 'Jersey', 'forms-bridge' ), + 'JOR' => __( 'Jordan', 'forms-bridge' ), + 'KAZ' => __( 'Kazakhstan', 'forms-bridge' ), + 'KEN' => __( 'Kenya', 'forms-bridge' ), + 'KIR' => __( 'Kiribati', 'forms-bridge' ), + 'XKX' => __( 'Kosovo', 'forms-bridge' ), + 'KWT' => __( 'Kuwait', 'forms-bridge' ), + 'KGZ' => __( 'Kyrgyzstan', 'forms-bridge' ), + 'LAO' => __( 'Laos', 'forms-bridge' ), + 'LVA' => __( 'Latvia', 'forms-bridge' ), + 'LBN' => __( 'Lebanon', 'forms-bridge' ), + 'LSO' => __( 'Lesotho', 'forms-bridge' ), + 'LBR' => __( 'Liberia', 'forms-bridge' ), + 'LBY' => __( 'Libya', 'forms-bridge' ), + 'LIE' => __( 'Liechtenstein', 'forms-bridge' ), + 'LTU' => __( 'Lithuania', 'forms-bridge' ), + 'LUX' => __( 'Luxembourg', 'forms-bridge' ), + 'MAC' => __( 'Macau', 'forms-bridge' ), + 'MKD' => __( 'Macedonia', 'forms-bridge' ), + 'MDG' => __( 'Madagascar', 'forms-bridge' ), + 'MWI' => __( 'Malawi', 'forms-bridge' ), + 'MYS' => __( 'Malaysia', 'forms-bridge' ), + 'MDV' => __( 'Maldives', 'forms-bridge' ), + 'MLI' => __( 'Mali', 'forms-bridge' ), + 'MLT' => __( 'Malta', 'forms-bridge' ), + 'MHL' => __( 'Marshall Islands', 'forms-bridge' ), + 'MRT' => __( 'Mauritania', 'forms-bridge' ), + 'MUS' => __( 'Mauritius', 'forms-bridge' ), + 'MYT' => __( 'Mayotte', 'forms-bridge' ), + 'MEX' => __( 'Mexico', 'forms-bridge' ), + 'FSM' => __( 'Micronesia', 'forms-bridge' ), + 'MDA' => __( 'Moldova', 'forms-bridge' ), + 'MCO' => __( 'Monaco', 'forms-bridge' ), + 'MNG' => __( 'Mongolia', 'forms-bridge' ), + 'MNE' => __( 'Montenegro', 'forms-bridge' ), + 'MSR' => __( 'Montserrat', 'forms-bridge' ), + 'MAR' => __( 'Morocco', 'forms-bridge' ), + 'MOZ' => __( 'Mozambique', 'forms-bridge' ), + 'MMR' => __( 'Myanmar', 'forms-bridge' ), + 'NAM' => __( 'Namibia', 'forms-bridge' ), + 'NRU' => __( 'Nauru', 'forms-bridge' ), + 'NPL' => __( 'Nepal', 'forms-bridge' ), + 'NLD' => __( 'Netherlands', 'forms-bridge' ), + 'ANT' => __( 'Netherlands Antilles', 'forms-bridge' ), + 'NCL' => __( 'New Caledonia', 'forms-bridge' ), + 'NZL' => __( 'New Zealand', 'forms-bridge' ), + 'NIC' => __( 'Nicaragua', 'forms-bridge' ), + 'NER' => __( 'Niger', 'forms-bridge' ), + 'NGA' => __( 'Nigeria', 'forms-bridge' ), + 'NIU' => __( 'Niue', 'forms-bridge' ), + 'PRK' => __( 'North Korea', 'forms-bridge' ), + 'MNP' => __( 'Northern Mariana Islands', 'forms-bridge' ), + 'NOR' => __( 'Norway', 'forms-bridge' ), + 'OMN' => __( 'Oman', 'forms-bridge' ), + 'PAK' => __( 'Pakistan', 'forms-bridge' ), + 'PLW' => __( 'Palau', 'forms-bridge' ), + 'PSE' => __( 'Palestine', 'forms-bridge' ), + 'PAN' => __( 'Panama', 'forms-bridge' ), + 'PNG' => __( 'Papua New Guinea', 'forms-bridge' ), + 'PRY' => __( 'Paraguay', 'forms-bridge' ), + 'PER' => __( 'Peru', 'forms-bridge' ), + 'PHL' => __( 'Philippines', 'forms-bridge' ), + 'PCN' => __( 'Pitcairn', 'forms-bridge' ), + 'POL' => __( 'Poland', 'forms-bridge' ), + 'PRT' => __( 'Portugal', 'forms-bridge' ), + 'PRI' => __( 'Puerto Rico', 'forms-bridge' ), + 'QAT' => __( 'Qatar', 'forms-bridge' ), + 'COG' => __( 'Republic of the Congo', 'forms-bridge' ), + 'REU' => __( 'Reunion', 'forms-bridge' ), + 'ROU' => __( 'Romania', 'forms-bridge' ), + 'RUS' => __( 'Russia', 'forms-bridge' ), + 'RWA' => __( 'Rwanda', 'forms-bridge' ), + 'BLM' => __( 'Saint Barthelemy', 'forms-bridge' ), + 'SHN' => __( 'Saint Helena', 'forms-bridge' ), + 'KNA' => __( 'Saint Kitts and Nevis', 'forms-bridge' ), + 'LCA' => __( 'Saint Lucia', 'forms-bridge' ), + 'MAF' => __( 'Saint Martin', 'forms-bridge' ), + 'SPM' => __( 'Saint Pierre and Miquelon', 'forms-bridge' ), + 'VCT' => __( 'Saint Vincent and the Grenadines', 'forms-bridge' ), + 'WSM' => __( 'Samoa', 'forms-bridge' ), + 'SMR' => __( 'San Marino', 'forms-bridge' ), + 'STP' => __( 'Sao Tome and Principe', 'forms-bridge' ), + 'SAU' => __( 'Saudi Arabia', 'forms-bridge' ), + 'SEN' => __( 'Senegal', 'forms-bridge' ), + 'SRB' => __( 'Serbia', 'forms-bridge' ), + 'SYC' => __( 'Seychelles', 'forms-bridge' ), + 'SLE' => __( 'Sierra Leone', 'forms-bridge' ), + 'SGP' => __( 'Singapore', 'forms-bridge' ), + 'SXM' => __( 'Sint Maarten', 'forms-bridge' ), + 'SVK' => __( 'Slovakia', 'forms-bridge' ), + 'SVN' => __( 'Slovenia', 'forms-bridge' ), + 'SLB' => __( 'Solomon Islands', 'forms-bridge' ), + 'SOM' => __( 'Somalia', 'forms-bridge' ), + 'ZAF' => __( 'South Africa', 'forms-bridge' ), + 'KOR' => __( 'South Korea', 'forms-bridge' ), + 'SSD' => __( 'South Sudan', 'forms-bridge' ), + 'ESP' => __( 'Spain', 'forms-bridge' ), + 'LKA' => __( 'Sri Lanka', 'forms-bridge' ), + 'SDN' => __( 'Sudan', 'forms-bridge' ), + 'SUR' => __( 'Suriname', 'forms-bridge' ), + 'SJM' => __( 'Svalbard and Jan Mayen', 'forms-bridge' ), + 'SWZ' => __( 'Swaziland', 'forms-bridge' ), + 'SWE' => __( 'Sweden', 'forms-bridge' ), + 'CHE' => __( 'Switzerland', 'forms-bridge' ), + 'SYR' => __( 'Syria', 'forms-bridge' ), + 'TWN' => __( 'Taiwan', 'forms-bridge' ), + 'TJK' => __( 'Tajikistan', 'forms-bridge' ), + 'TZA' => __( 'Tanzania', 'forms-bridge' ), + 'THA' => __( 'Thailand', 'forms-bridge' ), + 'TGO' => __( 'Togo', 'forms-bridge' ), + 'TKL' => __( 'Tokelau', 'forms-bridge' ), + 'TON' => __( 'Tonga', 'forms-bridge' ), + 'TTO' => __( 'Trinidad and Tobago', 'forms-bridge' ), + 'TUN' => __( 'Tunisia', 'forms-bridge' ), + 'TUR' => __( 'Turkey', 'forms-bridge' ), + 'TKM' => __( 'Turkmenistan', 'forms-bridge' ), + 'TCA' => __( 'Turks and Caicos Islands', 'forms-bridge' ), + 'TUV' => __( 'Tuvalu', 'forms-bridge' ), + 'VIR' => __( 'U.S. Virgin Islands', 'forms-bridge' ), + 'UGA' => __( 'Uganda', 'forms-bridge' ), + 'UKR' => __( 'Ukraine', 'forms-bridge' ), + 'ARE' => __( 'United Arab Emirates', 'forms-bridge' ), + 'GBR' => __( 'United Kingdom', 'forms-bridge' ), + 'USA' => __( 'United States', 'forms-bridge' ), + 'URY' => __( 'Uruguay', 'forms-bridge' ), + 'UZB' => __( 'Uzbekistan', 'forms-bridge' ), + 'VUT' => __( 'Vanuatu', 'forms-bridge' ), + 'VAT' => __( 'Vatican', 'forms-bridge' ), + 'VEN' => __( 'Venezuela', 'forms-bridge' ), + 'VNM' => __( 'Vietnam', 'forms-bridge' ), + 'WLF' => __( 'Wallis and Futuna', 'forms-bridge' ), + 'ESH' => __( 'Western Sahara', 'forms-bridge' ), + 'YEM' => __( 'Yemen', 'forms-bridge' ), + 'ZMB' => __( 'Zambia', 'forms-bridge' ), + 'ZWE' => __( 'Zimbabwe', 'forms-bridge' ), +); diff --git a/forms-bridge/deps/http b/forms-bridge/deps/http new file mode 160000 index 00000000..bdd6e145 --- /dev/null +++ b/forms-bridge/deps/http @@ -0,0 +1 @@ +Subproject commit bdd6e145e266e12e6f423a764fc2720a43faa436 diff --git a/forms-bridge/deps/plugin b/forms-bridge/deps/plugin new file mode 160000 index 00000000..cfbbf34c --- /dev/null +++ b/forms-bridge/deps/plugin @@ -0,0 +1 @@ +Subproject commit cfbbf34c2b5490f6e21e99d814bb89e87ddd7d26 diff --git a/forms-bridge/forms-bridge.php b/forms-bridge/forms-bridge.php new file mode 100644 index 00000000..f8c1156f --- /dev/null +++ b/forms-bridge/forms-bridge.php @@ -0,0 +1,56 @@ + + */ + private static $addons = array(); + + /** + * Handles addon's registry option name. + * + * @var string + */ + private const REGISTRY = 'forms_bridge_addons'; + + /** + * Handles addon public name. + * + * @var string + */ + const TITLE = ''; + + /** + * Handles addon's API name. + * + * @var string + */ + const NAME = ''; + + /** + * Handles addon's custom bridge class name. + * + * @var string + */ + const BRIDGE = '\FORMS_BRIDGE\Form_Bridge'; + + /** + * Addon's default config getter. + * + * @return array + */ + public static function schema() { + $bridge_schema = FBAPI::get_bridge_schema( static::NAME ); + + return array( + 'type' => 'object', + 'properties' => array( + 'title' => array( 'type' => 'string' ), + 'description' => array( + 'type' => 'string', + 'default' => '', + ), + 'bridges' => array( + 'type' => 'array', + 'items' => $bridge_schema, + 'default' => array(), + ), + ), + 'required' => array( 'title', 'bridges' ), + ); + } + + /** + * Addon's default data getter. + * + * @return array + */ + protected static function defaults() { + return array( + 'title' => static::TITLE, + 'bridges' => array(), + ); + } + + /** + * Public singleton initializer. + * + * @param mixed[] ...$args Array of class constructor arguments. + */ + final public static function setup( ...$args ) { + return static::get_instance( ...$args ); + } + + /** + * Public addons registry getter. + * + * @return array Addons registry state. + */ + private static function registry() { + $state = get_option( self::REGISTRY, array( 'rest' => true ) ) ?: array(); + $addons_dir = FORMS_BRIDGE_ADDONS_DIR; + $addons = array_diff( scandir( $addons_dir ), array( '.', '..' ) ); + + $registry = array(); + foreach ( $addons as $addon ) { + $addon_dir = "{$addons_dir}/{$addon}"; + if ( ! is_dir( $addon_dir ) ) { + continue; + } + + $index = "{$addon_dir}/class-{$addon}-addon.php"; + if ( is_file( $index ) && is_readable( $index ) ) { + $registry[ $addon ] = boolval( $state[ $addon ] ?? false ); + } + } + + return $registry; + } + + /** + * Updates the addons' registry state. + * + * @param array $addons Addons registry state. + */ + private static function update_registry( $addons = array() ) { + $registry = self::registry(); + foreach ( $addons as $addon => $enabled ) { + if ( ! isset( $registry[ $addon ] ) ) { + continue; + } + + $registry[ $addon ] = (bool) $enabled; + } + + update_option( self::REGISTRY, $registry ); + } + + /** + * Public addons list getter. + * + * @return Addon[] List of enabled addon instances. + */ + final public static function addons() { + $addons = array(); + foreach ( self::$addons as $addon ) { + if ( $addon->enabled ) { + $addons[] = $addon; + } + } + + return $addons; + } + + /** + * Addon instances getter. + * + * @param string $name Addon name. + * + * @return Addon|null + */ + final public static function addon( $name ) { + return self::$addons[ $name ] ?? null; + } + + /** + * Public addons loader. + */ + final public static function load_addons() { + $addons_dir = FORMS_BRIDGE_ADDONS_DIR; + $registry = self::registry(); + foreach ( $registry as $addon => $enabled ) { + require_once "{$addons_dir}/{$addon}/class-{$addon}-addon.php"; + + if ( $enabled ) { + self::$addons[ $addon ]->load(); + } + } + + Settings_Store::ready( + function ( $store ) { + $store::use_getter( + 'general', + function ( $data ) { + $registry = self::registry(); + $addons = array(); + foreach ( self::$addons as $name => $addon ) { + $logo_path = + FORMS_BRIDGE_ADDONS_DIR . + '/' . + $addon::NAME . + '/assets/logo.png'; + + if ( is_file( $logo_path ) && is_readable( $logo_path ) ) { + $logo = plugin_dir_url( $logo_path ) . 'logo.png'; + } else { + $logo = ''; + } + + $addons[ $name ] = array( + 'name' => $name, + 'title' => $addon::TITLE, + 'enabled' => $registry[ $name ] ?? false, + 'logo' => $logo, + ); + } + + ksort( $addons ); + $addons = array_values( $addons ); + + $addons = apply_filters( 'forms_bridge_addons', $addons ); + return array_merge( $data, array( 'addons' => $addons ) ); + } + ); + + $store::use_setter( + 'general', + function ( $data ) { + if ( ! isset( $data['addons'] ) || ! is_array( $data['addons'] ) ) { + return $data; + } + + $registry = array(); + foreach ( $data['addons'] as $addon ) { + $registry[ $addon['name'] ] = (bool) $addon['enabled']; + } + + self::update_registry( $registry ); + + unset( $data['addons'] ); + return $data; + }, + 9 + ); + } + ); + } + + /** + * Middelware to the addon settings validation method to filter out of domain + * setting updates. + * + * @param array $data Setting data. + * + * @return array Validated setting data. + */ + private static function sanitize_setting( $data ) { + if ( ! isset( $data['bridges'] ) ) { + return $data; + } + + $data['bridges'] = static::sanitize_bridges( $data['bridges'] ); + + return $data; + } + + /** + * Apply bridges setting data sanitization and validation. + * + * @param array $bridges Collection of bridges data. + * + * @return array + */ + private static function sanitize_bridges( $bridges ) { + $uniques = array(); + $sanitized = array(); + + $schema = FBAPI::get_bridge_schema( static::NAME ); + foreach ( $bridges as $bridge ) { + $bridge['name'] = trim( $bridge['name'] ); + if ( in_array( $bridge['name'], $uniques, true ) ) { + continue; + } + + $bridge = static::sanitize_bridge( $bridge, $schema ); + if ( $bridge ) { + $sanitized[] = $bridge; + $uniques[] = $bridge['name']; + } + } + + return $sanitized; + } + + /** + * Common bridge sanitization method. + * + * @param array $bridge Bridge data. + * @param array $schema Bridge schema. + * + * @return array + */ + protected static function sanitize_bridge( $bridge, $schema ) { + $backends = Settings_Store::setting( 'http' )->backends ?: array(); + + foreach ( $backends as $candidate ) { + if ( $candidate['name'] === $bridge['backend'] ) { + $backend = $candidate; + break; + } + } + + if ( ! isset( $backend ) ) { + $bridge['backend'] = ''; + } + + static $forms; + if ( null === $forms ) { + $forms = FBAPI::get_forms(); + } + + foreach ( $forms as $candidate ) { + if ( $candidate['_id'] === $bridge['form_id'] ) { + $form = $candidate; + break; + } + } + + if ( ! isset( $form ) ) { + $bridge['form_id'] = ''; + } + + $bridge['mutations'] = array_slice( + $bridge['mutations'], + 0, + count( $bridge['workflow'] ) + 1 + ); + + $l = count( $bridge['workflow'] ); + for ( $i = 0; $i <= $l; $i++ ) { + $bridge['mutations'][ $i ] = $bridge['mutations'][ $i ] ?? array(); + } + + $bridge['is_valid'] = + $bridge['form_id'] && + $bridge['backend'] && + $bridge['method'] && + $bridge['endpoint']; + + $bridge['enabled'] = boolval( $bridge['enabled'] ?? true ); + + return $bridge; + } + + /** + * Handles the enabled state of the addon instance. + * + * @var bool + */ + public $enabled = false; + + /** + * Private class constructor. Add addons scripts as dependency to the + * plugin's scripts and setup settings hooks. + * + * @param mixed[] ...$args Array of class constructor arguments. + */ + protected function construct( ...$args ) { + if ( empty( static::NAME ) || empty( static::TITLE ) ) { + Logger::log( 'Skip invalid addon registration', Logger::DEBUG ); + Logger::log( + 'Addon name and title const are required', + Logger::ERROR + ); + return; + } + + self::$addons[ static::NAME ] = $this; + } + + /** + * Loads the addon. + */ + public function load() { + add_action( + 'init', + static function () { + self::load_data(); + }, + 5, + 0 + ); + + add_filter( + 'forms_bridge_templates', + static function ( $templates, $addon = null ) { + if ( ! wp_is_numeric_array( $templates ) ) { + $templates = array(); + } + + if ( $addon && static::NAME !== $addon ) { + return $templates; + } + + foreach ( static::load_templates() as $template ) { + $templates[] = $template; + } + + return $templates; + }, + 10, + 2 + ); + + add_filter( + 'forms_bridge_jobs', + static function ( $jobs, $addon = null ) { + if ( ! wp_is_numeric_array( $jobs ) ) { + $jobs = array(); + } + + if ( $addon && static::NAME !== $addon ) { + return $jobs; + } + + foreach ( static::load_jobs() as $job ) { + $jobs[] = $job; + } + + return $jobs; + }, + 10, + 2 + ); + + add_filter( + 'forms_bridge_bridges', + static function ( $bridges, $addon = null ) { + if ( ! wp_is_numeric_array( $bridges ) ) { + $bridges = array(); + } + + if ( $addon && static::NAME !== $addon ) { + return $bridges; + } + + $setting = static::setting(); + if ( ! $setting ) { + return $bridges; + } + + foreach ( $setting->bridges ?: array() as $bridge_data ) { + $bridge_class = static::BRIDGE; + $bridges[] = new $bridge_class( $bridge_data, static::NAME ); + } + + return $bridges; + }, + 10, + 2 + ); + + Settings_Store::register_setting( + static function ( $settings ) { + $schema = static::schema(); + $schema['name'] = static::NAME; + $schema['default'] = static::defaults(); + + $settings[] = $schema; + return $settings; + } + ); + + Settings_Store::ready( + static function ( $store ) { + $store::use_getter( + static::NAME, + static function ( $data ) { + $templates = FBAPI::get_addon_templates( static::NAME ); + $jobs = FBAPI::get_addon_jobs( static::NAME ); + + return array_merge( + $data, + array( + 'templates' => array_map( + static function ( $template ) { + return array( + 'title' => $template->title, + 'name' => $template->name, + 'integrations' => $template->integrations, + ); + }, + $templates + ), + 'jobs' => array_map( + static function ( $job ) { + return array( + 'title' => $job->title, + 'name' => $job->name, + ); + }, + $jobs + ), + ) + ); + } + ); + + $store::use_setter( + static::NAME, + static function ( $data ) { + if ( ! is_array( $data ) ) { + return $data; + } + + unset( $data['templates'] ); + unset( $data['jobs'] ); + + return static::sanitize_setting( $data ); + }, + 9 + ); + } + ); + + $this->enabled = true; + } + + /** + * Addon's setting name getter. + * + * @return string + */ + final protected static function setting_name() { + return 'forms-bridge_' . static::NAME; + } + + /** + * Addon's setting instance getter. + * + * @return Setting|null Setting instance. + */ + final protected static function setting() { + return Forms_Bridge::setting( static::NAME ); + } + + /** + * Performs a request against the backend to check the connexion status. + * + * @param string $backend Target backend name. + * + * @return boolean|WP_Error + */ + public function ping( $backend ) { + return false; + } + + /** + * Performs a GET request against the backend endpoint and retrive the response data. + * + * @param string $endpoint Target endpoint name. + * @param string $backend Target backend name. + * + * @return array|WP_Error + */ + public function fetch( $endpoint, $backend ) { + return array( + 'headers' => array(), + 'cookies' => array(), + 'filename' => null, + 'body' => '', + 'response' => array( + 'status' => 202, + 'message' => 'Accepted', + ), + 'http_response' => array( + 'data' => null, + 'headers' => null, + 'status' => null, + ), + 'data' => array(), + ); + } + + /** + * Performs an introspection of the backend endpoint and returns API fields + * and accepted content type. + * + * @param string $endpoint Target endpoint name. + * @param string $backend Target backend name. + * + * @return array|WP_Error + */ + public function get_endpoint_schema( $endpoint, $backend ) { + return array(); + } + + /** + * Get posts from the database based on a post type and an addon name. + * + * @param string $post_type Post type slug. + * @param string $addon Addon name. + * + * @return WP_Post[] + */ + private static function autoload_posts( $post_type, $addon ) { + if ( ! in_array( $post_type, array( Form_Bridge_Template::TYPE, Job::TYPE ), true ) ) { + return array(); + } + + // phpcs:disable WordPress.DB.SlowDBQuery + return get_posts( + array( + 'post_type' => $post_type, + 'posts_per_page' => -1, + 'meta_key' => '_fb-addon', + 'meta_value' => $addon, + ) + ); + // phpcs:enable + } + + /** + * Autoload config files from a given addon's directory. Used to load + * template and job config files. + * + * @param string $dir Path of the target directory. + * @param string[] $extensions Allowed file extensions. + * + * @return array Array with data from files. + */ + private static function autoload_dir( $dir, $extensions = array( 'php', 'json' ) ) { + if ( ! is_readable( $dir ) || ! is_dir( $dir ) ) { + return array(); + } + + static $load_cache; + + $files = array(); + foreach ( array_diff( scandir( $dir ), array( '.', '..' ) ) as $file ) { + $file_path = $dir . '/' . $file; + + if ( is_file( $file_path ) && is_readable( $file_path ) ) { + $files[] = $file_path; + } + } + + $loaded = array(); + foreach ( $files as $file_path ) { + $file = basename( $file_path ); + $name = pathinfo( $file )['filename']; + $ext = pathinfo( $file )['extension'] ?? null; + + if ( ! in_array( $ext, $extensions ) ) { + continue; + } + + if ( isset( $load_cache[ $file_path ] ) ) { + $loaded[] = $load_cache[ $file_path ]; + continue; + } + + $data = null; + if ( 'php' === $ext ) { + $data = include_once $file_path; + } elseif ( 'json' === $ext ) { + // phpcs:disable Generic.CodeAnalysis.EmptyStatement + try { + $content = file_get_contents( $file_path ); + $data = json_decode( $content, true, JSON_THROW_ON_ERROR ); + } catch ( TypeError ) { + // pass. + } catch ( Error ) { + // pass. + } + // phpcs:enable + } + + if ( is_array( $data ) ) { + $data['name'] = $name; + $loaded[] = $data; + $load_cache[ $file_path ] = $data; + } + } + + return $loaded; + } + + /** + * Loads addon's bridge data. + */ + private static function load_data() { + $dir = FORMS_BRIDGE_ADDONS_DIR . '/' . static::NAME . '/data'; + self::autoload_dir( $dir ); + } + + /** + * Loads addon's bridge templates. + * + * @return Form_Bridge_Template[]. + */ + private static function load_templates() { + $dir = FORMS_BRIDGE_ADDONS_DIR . '/' . static::NAME . '/templates'; + + $directories = apply_filters( + 'forms_bridge_template_directories', + array( + $dir, + Forms_Bridge::path() . 'includes/templates', + get_stylesheet_directory() . + '/forms-bridge/templates/' . + static::NAME, + ), + static::NAME + ); + + $templates = array(); + foreach ( $directories as $dir ) { + if ( ! is_dir( $dir ) ) { + continue; + } + + foreach ( self::autoload_dir( $dir ) as $template ) { + $template['name'] = sanitize_title( $template['name'] ); + $templates[ $template['name'] ] = $template; + } + } + + foreach ( + self::autoload_posts( 'fb-bridge-template', static::NAME ) + as $template_post + ) { + $template[ $template->post_name ] = $template_post; + } + + $templates = array_values( $templates ); + + $templates = apply_filters( + 'forms_bridge_load_templates', + $templates, + static::NAME + ); + + $loaded = array(); + foreach ( $templates as $template ) { + if ( + is_array( $template ) && + isset( $template['data'], $template['name'] ) + ) { + $template = array_merge( + $template['data'], + array( + 'name' => $template['name'], + ) + ); + } + + $template = new Form_Bridge_Template( $template, static::NAME ); + + if ( $template->is_valid ) { + $loaded[] = $template; + } + } + + return $loaded; + } + + /** + * Addon's jobs loader. + * + * @return Job[] + */ + private static function load_jobs() { + $dir = FORMS_BRIDGE_ADDONS_DIR . '/' . static::NAME . '/jobs'; + + $directories = apply_filters( + 'forms_bridge_job_directories', + array( + $dir, + Forms_Bridge::path() . '/includes/jobs', + get_stylesheet_directory() . '/jobs/' . static::NAME, + ), + static::NAME + ); + + $jobs = array(); + foreach ( $directories as $dir ) { + if ( ! is_dir( $dir ) ) { + continue; + } + + foreach ( self::autoload_dir( $dir ) as $job ) { + $job['name'] = sanitize_title( $job['name'] ); + $jobs[ $job['name'] ] = $job; + } + } + + foreach ( self::autoload_posts( 'fb-job', static::NAME ) as $job_post ) { + $jobs[ $job_post->post_name ] = $job_post; + } + + $jobs = array_values( $jobs ); + + $jobs = apply_filters( 'forms_bridge_load_jobs', $jobs, static::NAME ); + + $loaded = array(); + foreach ( $jobs as $job ) { + if ( is_array( $job ) && isset( $job['data'], $job['name'] ) ) { + $job = array_merge( $job['data'], array( 'name' => $job['name'] ) ); + } + + $job = new Job( $job, static::NAME ); + + if ( $job->is_valid ) { + $loaded[] = $job; + } + } + + return $loaded; + } + + // phpcs:disable + // public static function get_api() + // { + // $__FILE__ = (new ReflectionClass(static::class))->getFileName(); + // $file = dirname($__FILE__) . '/api.php'; + + // if (!is_file($file) || !is_readable($file)) { + // return []; + // } + + // $source = file_get_contents($file); + // $tokens = token_get_all($source); + + // $functions = []; + // $nextStringIsFunc = false; + // $inClass = false; + // $bracesCount = 0; + + // foreach ($tokens as $token) { + // switch ($token[0]) { + // case T_CLASS: + // $inClass = true; + // break; + // case T_FUNCTION: + // if (!$inClass) { + // $nextStringIsFunc = true; + // } + // break; + + // case T_STRING: + // if ($nextStringIsFunc) { + // $nextStringIsFunc = false; + // $functions[] = $token[1]; + // } + // break; + // case '(': + // case ';': + // $nextStringIsFunc = false; + // break; + // case '{': + // if ($inClass) { + // $bracesCount++; + // } + // break; + + // case '}': + // if ($inClass) { + // $bracesCount--; + // if ($bracesCount === 0) { + // $inClass = false; + // } + // } + // break; + // } + // } + + // return $functions; + // } +} diff --git a/forms-bridge/includes/class-api.php b/forms-bridge/includes/class-api.php new file mode 100644 index 00000000..76f1bcca --- /dev/null +++ b/forms-bridge/includes/class-api.php @@ -0,0 +1,630 @@ +name === $name ) { + return $bridge; + } + } + } + + /** + * Gets the current bridge from the bridges loop. + * + * @return Form_Bridge|null + */ + public static function get_current_bridge() { + return Forms_Bridge::current_bridge(); + } + + /** + * Gets the collection of available bridges. + * + * @return Form_Bridge[] + */ + public static function get_bridges() { + return apply_filters( 'forms_bridge_bridges', array() ); + } + + /** + * Gets the collection of available bridges filtered by form ID. + * + * @param string $form_id Form ID. + * @param string|null $integration Integration slug, required if form_id is not prefixed. + * + * @return Form_Bridge[] + */ + public static function get_form_bridges( $form_id, $integration = null ) { + $bridges = apply_filters( 'forms_bridge_bridges', array() ); + + if ( preg_match( '/^(\w+):(\d+)$/', $form_id, $matches ) ) { + [, $integration, $form_id] = $matches; + $form_id = (int) $form_id; + } elseif ( empty( $integration ) ) { + return array(); + } + + $form_id = "{$integration}:{$form_id}"; + + $form_bridges = array(); + foreach ( $bridges as $bridge ) { + if ( $bridge->form_id === $form_id ) { + $form_bridges[] = $bridge; + } + } + + return $form_bridges; + } + + /** + * Gets the collection of available bridges filtered by addon name. + * + * @param string $addon Addon name. + * + * @return Form_Bridge[] + */ + public static function get_addon_bridges( $addon ) { + return apply_filters( 'forms_bridge_bridges', array(), $addon ); + } + + /** + * Inserts or updates the bridge data to the database. + * + * @param array $data Bridge data. + * @param string $addon Addon name. + * + * @return boolean + */ + public static function save_bridge( $data, $addon ) { + $addon = self::get_addon( $addon ); + if ( ! $addon ) { + return false; + } + + $bridge_class = $addon::BRIDGE; + $bridge = new $bridge_class( $data ); + + if ( ! $bridge->is_valid ) { + return false; + } + + return $bridge->save(); + } + + /** + * Deletes the bridge data from the database. + * + * @param string $name Bridge name. + * @param string $addon Addon name. + * + * @return boolean + */ + public static function delete_bridge( $name, $addon ) { + $bridge = self::get_bridge( $name, $addon ); + + if ( ! $bridge ) { + return false; + } + + return $bridge->delete(); + } + + /** + * Gets the bridge schema for a given addon. + * + * @param string $addon Addon name. + * + * @return array|null + */ + public static function get_bridge_schema( $addon ) { + $addon = Addon::addon( $addon ); + + if ( ! $addon ) { + return; + } + + return Form_Bridge::schema( $addon::NAME ); + } + + /** + * Gets the collection of available templates. + * + * @return Form_Bridge_Template[] + */ + public static function get_templates() { + return apply_filters( 'forms_bridge_templates', array() ); + } + + /** + * Gets the collection of available templates filtered by addon name. + * + * @param string $addon Addon name. + * + * @return Form_Bridge_Template[] + */ + public static function get_addon_templates( $addon ) { + return apply_filters( 'forms_bridge_templates', array(), $addon ); + } + + /** + * Gets a template instance by name and addon. + * + * @param string $name Template name. + * @param string $addon Addon name. + * + * @return Form_Bridge_Template|null + */ + public static function get_template( $name, $addon ) { + $templates = self::get_addon_templates( $addon ); + + foreach ( $templates as $template ) { + if ( $template->name === $name ) { + return $template; + } + } + } + + /** + * Inserts or updates the template data on the database. + * + * @param array $data Template data. + * @param string $addon Addon name. + * + * @return integer|boolean Post ID or false. + */ + public static function save_template( $data, $addon ) { + $template = new Form_Bridge_Template( $data, $addon ); + + if ( ! $template->is_valid ) { + return false; + } + + return $template->save(); + } + + /** + * Delete the template data from the database. + * + * @param string $name Template name. + * @param string $addon Addon name. + * + * @return boolean + */ + public static function reset_template( $name, $addon ) { + $template = self::get_template( $name, $addon ); + + if ( ! $template ) { + return false; + } + + return $template->reset(); + } + + /** + * Gets the template schema for a given addon. + * + * @param string $addon Addon name. + * + * @return array|null + */ + public static function get_template_schema( $addon ) { + return Form_Bridge_Template::schema( $addon ); + } + + /** + * Gets the collection of available jobs. + * + * @return Job[] + */ + public static function get_jobs() { + return apply_filters( 'forms_bridge_jobs', array() ); + } + + /** + * Gets the collection of available jobs filtered by addon name. + * + * @param string $addon Addon name. + * + * @return Job[] + */ + public static function get_addon_jobs( $addon ) { + return apply_filters( 'forms_bridge_jobs', array(), $addon ); + } + + /** + * Gets a job instance by name and addon. + * + * @param string $name Job name. + * @param string $addon Addon name. + * + * @return Job|null + */ + public static function get_job( $name, $addon ) { + $jobs = self::get_addon_jobs( $addon ); + + foreach ( $jobs as $job ) { + if ( $job->name === $name ) { + return $job; + } + } + } + + /** + * Inserts or updates the job data on the database. + * + * @param array $data Job data. + * @param string $addon Addon name. + * + * @return integer|boolean Post ID or false. + */ + public static function save_job( $data, $addon ) { + $job = new Job( $data, $addon ); + + if ( ! $job->is_valid ) { + return false; + } + + return $job->save(); + } + + /** + * Deletes the job data from the database. + * + * @param string $name Job name. + * @param string $addon Addon name. + * + * @return boolean + */ + public static function reset_job( $name, $addon ) { + $job = self::get_job( $name, $addon ); + + if ( ! $job ) { + return false; + } + + return $job->reset(); + } + + /** + * Gets the job schema. + * + * @return array + */ + public static function get_job_schema() { + return Job::schema(); + } + + /** + * Gets the collection of available backends. + * + * @return Backend[] + */ + public static function get_backends() { + return apply_filters( 'http_bridge_backends', array() ); + } + + /** + * Gets a backend instance by name. + * + * @param string $name Backend name. + * + * @return Backend|null + */ + public static function get_backend( $name ) { + $backends = self::get_backends(); + + foreach ( $backends as $backend ) { + if ( $backend->name === $name ) { + return $backend; + } + } + } + + /** + * Inserts or updates the backend data on the database. + * + * @param array $data Backend data. + * + * @return boolean + */ + public static function save_backend( $data ) { + $backend = new Backend( $data ); + + if ( ! $backend->is_valid ) { + return false; + } + + $setting = Settings_Store::setting( 'http' ); + if ( ! $setting ) { + return false; + } + + $backends = $setting->backends ?: array(); + + $index = array_search( $backend->name, array_column( $backends, 'name' ), true ); + + if ( false === $index ) { + $backends[] = $data; + } else { + $backends[ $index ] = $data; + } + + $setting->backends = $backends; + return true; + } + + /** + * Delete the backend data from the database. + * + * @param string $name Backend name. + * + * @return boolean + */ + public static function delete_backend( $name ) { + $backend = self::get_backend( $name ); + + if ( ! $backend ) { + return false; + } + + if ( ! $backend->is_valid ) { + return false; + } + + $setting = Settings_Store::setting( 'http' ); + if ( ! $setting ) { + return false; + } + + $backends = $setting->backends ?: array(); + + $index = array_search( $backend->name, array_column( $backends, 'name' ), true ); + + if ( false === $index ) { + return false; + } + + array_splice( $backends, $index ); + $setting->backends = $backends; + + return true; + } + + /** + * Gets the backend schema. + * + * @return array + */ + public static function get_backend_schema() { + return Backend::schema(); + } + + /** + * Gets the collection of available credentials. + * + * @return Credential[] + */ + public static function get_credentials() { + return apply_filters( 'http_bridge_credentials', array() ); + } + + /** + * Gets a credential instance by name and addon. + * + * @param string $name Credential name. + * + * @return Credential|null + */ + public static function get_credential( $name ) { + $credentials = self::get_credentials(); + + foreach ( $credentials as $credential ) { + if ( $credential->name === $name ) { + return $credential; + } + } + } + + /** + * Inserts or updates the credential data to the database. + * + * @param array $data Credential data. + * + * @return boolean + */ + public static function save_credential( $data ) { + $credential = new Credential( $data ); + + if ( ! $credential->is_valid ) { + return false; + } + + $setting = Settings_Store::setting( 'http' ); + if ( ! $setting ) { + return false; + } + + $credentials = $setting->credentials ?: array(); + + $index = array_search( $credential->name, array_column( $credentials, 'name' ), true ); + + if ( false === $index ) { + $credentials[] = $data; + } else { + $credentials[ $index ] = $data; + } + + $setting->credentials = $credentials; + return true; + } + + /** + * Deletes the credential data from the database. + * + * @param string $name Credential name. + * + * @return boolean + */ + public static function delete_credential( $name ) { + $credential = self::get_credential( $name ); + + if ( ! $credential ) { + return false; + } + + if ( ! $credential->is_valid ) { + return false; + } + + $setting = Settings_Store::setting( 'http' ); + if ( ! $setting ) { + return false; + } + + $credentials = $setting->credentials ?: array(); + + $index = array_search( $credential->name, array_column( $credentials, 'name' ), true ); + + if ( false === $index ) { + return false; + } + + array_splice( $credentials, $index ); + $setting->credentials = $credentials; + + return true; + } + + /** + * Gets the credential schema for a given addon. + * + * @return array + */ + public static function get_credential_schema() { + return Credential::schema(); + } +} diff --git a/forms-bridge/includes/class-form-bridge-template.php b/forms-bridge/includes/class-form-bridge-template.php new file mode 100644 index 00000000..07cedf23 --- /dev/null +++ b/forms-bridge/includes/class-form-bridge-template.php @@ -0,0 +1,1327 @@ + 'http://json-schema.org/draft-04/schema#', + 'title' => 'form-bridge-template', + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'title' => _x( + 'Name', + 'Bridge template schema', + 'forms-bridge' + ), + 'description' => __( + 'Internal and unique name of the template', + 'forms-bridge' + ), + 'type' => 'string', + 'minLength' => 1, + ), + 'title' => array( + 'title' => _x( + 'Title', + 'Bridge template schema', + 'forms-bridge' + ), + 'description' => __( + 'Public title of the template', + 'forms-bridge' + ), + 'type' => 'string', + 'minLength' => 1, + ), + 'description' => array( + 'title' => _x( + 'Description', + 'Bridge template schema', + 'forms-bridge' + ), + 'description' => __( + 'Short description of the template purpose', + 'forms-bridge' + ), + 'type' => 'string', + 'default' => '', + ), + 'integrations' => array( + 'title' => _x( + 'Integrations', + 'Bridge template schema', + 'forms-bridge' + ), + 'description' => __( + 'Template\'s supported integrations', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'uniqueItems' => true, + 'minItems' => 1, + ), + 'fields' => array( + 'title' => _x( + 'Fields', + 'Bridge template schema', + 'forms-bridge' + ), + 'description' => __( + 'Template fields to be filled by the user', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'ref' => array( + 'type' => 'string', + 'pattern' => '^#.+', + ), + 'name' => array( + 'type' => 'string', + 'minLength' => 1, + ), + 'label' => array( + 'type' => 'string', + 'minLength' => 1, + ), + 'description' => array( 'type' => 'string' ), + 'type' => array( + 'type' => 'string', + 'enum' => array( + 'text', + 'number', + 'select', + 'boolean', + 'email', + 'url', + ), + ), + 'required' => array( 'type' => 'boolean' ), + 'value' => array( + 'type' => array( + 'integer', + 'number', + 'string', + 'array', + 'boolean', + ), + ), + 'default' => array( + 'type' => array( + 'integer', + 'number', + 'string', + 'array', + 'boolean', + ), + ), + 'options' => array( + 'anyOf' => array( + array( + 'description' => __( + 'List of field options', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'label' => array( + 'type' => 'string', + ), + 'value' => array( + 'type' => 'string', + ), + ), + 'required' => array( 'value', 'label' ), + ), + 'uniqueItems' => true, + ), + array( + 'description' => __( + 'How to get options from the addon API', + 'forms-bridge' + ), + 'type' => 'object', + 'properties' => array( + 'endpoint' => array( + 'description' => __( + 'Endpoint to get values from', + 'forms-bridge' + ), + 'type' => 'string', + ), + 'finger' => array( + 'description' => __( + 'Fingers to get values from the endpoint response', + 'forms-bridge' + ), + 'oneOf' => array( + array( + 'type' => 'object', + 'properties' => array( + 'value' => array( + 'type' => + 'string', + ), + 'label' => array( + 'type' => + 'string', + ), + ), + 'required' => array( 'value' ), + ), + array( + 'type' => 'string', + ), + ), + ), + ), + 'required' => array( 'endpoint', 'finger' ), + ), + ), + ), + 'enum' => array( + 'type' => 'array', + 'items' => array( + 'type' => array( 'integer', 'number', 'string' ), + ), + 'uniqueItems' => true, + ), + 'min' => array( 'type' => 'integer' ), + 'max' => array( 'type' => 'integer' ), + 'multiple' => array( 'type' => 'boolean' ), + ), + 'required' => array( 'ref', 'name', 'type' ), + 'additionalProperties' => true, + ), + ), + 'form' => array( + 'title' => _x( + 'Form', + 'Bridge template schema', + 'forms-bridge' + ), + 'description' => __( + 'Form title and fields settings', + 'forms-bridge' + ), + 'type' => 'object', + 'properties' => array( + 'title' => array( + 'type' => 'string', + 'default' => '', + ), + 'fields' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'type' => 'string', + 'minLength' => 1, + ), + 'label' => array( 'type' => 'string' ), + 'type' => array( + 'type' => 'string', + 'enum' => array( + 'text', + 'textarea', + 'number', + 'url', + 'email', + 'tel', + 'select', + 'date', + 'hidden', + 'file', + 'checkbox', + ), + ), + 'required' => array( 'type' => 'boolean' ), + 'options' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'value' => array( + 'type' => array( + 'integer', + 'number', + 'string', + 'boolean', + ), + ), + 'label' => array( + 'type' => 'string', + ), + ), + 'required' => array( 'value', 'label' ), + ), + ), + 'value' => array( + 'type' => array( + 'integer', + 'number', + 'string', + // 'boolean', + // 'object', + 'array', + // 'null', + ), + ), + 'conditional' => array( 'type' => 'boolean' ), + 'is_file' => array( 'type' => 'boolean' ), + 'is_multi' => array( 'type' => 'boolean' ), + 'filetypes' => array( 'type' => 'string' ), + 'min' => array( 'type' => 'number' ), + 'max' => array( 'type' => 'number' ), + 'step' => array( 'type' => 'number' ), + 'format' => array( 'type' => 'string' ), + ), + 'required' => array( 'name', 'type' ), + ), + ), + ), + 'required' => array( 'title', 'fields' ), + 'additionalProperties' => false, + ), + 'bridge' => self::child_schema_to_template( + $bridge_schema, + _x( 'Bridge', 'Bridge template schema', 'forms-bridge' ) + ), + 'backend' => self::child_schema_to_template( + $backend_schema, + _x( 'Backend', 'Bridge template schema', 'forms-bridge' ) + ), + 'credential' => self::child_schema_to_template( + $credential_schema, + _x( 'Credential', 'Bridge template schema', 'forms-bridge' ) + ), + ), + 'additionalProperties' => false, + 'required' => array( + 'name', + 'title', + 'integrations', + 'fields', + 'form', + 'backend', + 'bridge', + ), + ); + + if ( ! $addon ) { + return $schema; + } + + return apply_filters( 'forms_bridge_template_schema', $schema, $addon ); + } + + + /** + * Decorates schemas of the template child objects (form, backend, credential) to scale down + * schema requirements and to fit the templates schema. + * + * @param array $schema Object json schema. + * @param string $title Schema title. + * + * @return array Decorated schema. + */ + private static function child_schema_to_template( $schema, $title ) { + if ( isset( $schema['oneOf'] ) ) { + $schema['oneOf'] = array_map( + static function ( $schema ) use ( + $title + ) { + $title = $schema['title'] ?? $title; + return self::child_schema_to_template( $schema, $title ); + }, + $schema['oneOf'] + ); + return $schema; + } elseif ( isset( $schema['anyOf'] ) ) { + $schema['anyOf'] = array_map( + static function ( $schema ) use ( + $title + ) { + $title = $schema['title'] ?? $title; + return self::child_schema_to_template( $schema, $title ); + }, + $schema['anyOf'] + ); + return $schema; + } + + foreach ( $schema['properties'] as &$prop_schema ) { + if ( 'string' === $prop_schema['type'] ) { + $prop_schema['default'] = ''; + unset( $prop_schema['minLength'] ); + unset( $prop_schema['pattern'] ); + unset( $prop_schema['format'] ); + } elseif ( 'array' === $prop_schema['type'] ) { + $prop_schema['default'] = array(); + unset( $prop_schema['minItems'] ); + } + } + + if ( ! isset( $schema['default'] ) ) { + $schema['default'] = array(); + } + + $schema['title'] = $title; + return $schema; + } + + /** + * Template default data getter. + * + * @param string $addon Template addon namespace. + * @param array $schema Template schema. + * + * @return array + */ + protected static function defaults( $addon = null, $schema = null ) { + if ( ! is_array( $schema ) ) { + $schema = static::schema( $addon ); + } + + return apply_filters( + 'forms_bridge_template_defaults', + array( + 'integrations' => array(), + 'fields' => array( + array( + 'ref' => '#form', + 'name' => 'id', + 'label' => __( 'Form ID', 'forms-bridge' ), + 'type' => 'text', + ), + array( + 'ref' => '#form', + 'name' => 'title', + 'label' => __( 'Form title', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#backend', + 'name' => 'name', + 'label' => __( 'Name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + ), + array( + 'ref' => '#backend', + 'name' => 'base_url', + 'label' => __( 'Base URL', 'forms-bridge' ), + 'type' => 'url', + 'required' => true, + 'default' => 'https://', + 'format' => 'uri', + ), + array( + 'ref' => '#bridge', + 'name' => 'name', + 'label' => __( 'Bridge name', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'minLength' => 1, + ), + array( + 'ref' => '#bridge', + 'name' => 'endpoint', + 'label' => __( 'Endpoint', 'forms-bridge' ), + 'type' => 'text', + 'required' => true, + 'default' => '', + ), + array( + 'ref' => '#bridge', + 'name' => 'method', + 'label' => __( 'Method', 'forms-bridge' ), + 'type' => 'options', + 'options' => array( + array( + 'label' => 'GET', + 'value' => 'GET', + ), + array( + 'label' => 'POST', + 'value' => 'POST', + ), + array( + 'label' => 'PUT', + 'value' => 'PUT', + ), + array( + 'label' => 'PATCH', + 'value' => 'PATCH', + ), + array( + 'label' => 'DELETE', + 'value' => 'DELETE', + ), + ), + 'required' => true, + 'default' => 'POST', + ), + ), + 'bridge' => array( + 'endpoint' => '', + 'method' => 'POST', + ), + 'backend' => array( + 'headers' => array( + array( + 'name' => 'Content-Type', + 'value' => 'application/json', + ), + ), + ), + 'form' => array( + 'title' => '', + 'fields' => array(), + ), + ), + $addon, + $schema + ); + } + + /** + * Gets the template config data from a post. + * + * @param WP_Post $post Post instance. + * + * @return array Template config data. + */ + private static function data_from_post( $post ) { + return array( + 'name' => $post->post_name, + 'title' => $post->post_title, + 'description' => $post->post_excerpt, + 'fields' => + (array) ( get_post_meta( $post->ID, '_template-fields', true ) ?: + array() ), + 'form' => + (array) ( get_post_meta( $post->ID, '_template-form', true ) ?: + array() ), + 'bridge' => + (array) ( get_post_meta( $post->ID, '_template-bridge', true ) ?: + array() ), + 'backend' => + (array) ( get_post_meta( $post->ID, '_template-backend', true ) ?: + array() ), + 'credential' => + (array) ( get_post_meta( + $post->ID, + '_template-credential', + true + ) ?: + array() ), + ); + } + + /** + * Store template attribute values, validates data and binds the + * instance to custom forms bridge template hooks. + * + * @param array $data Template data. + * @param string $addon Addon name. + */ + public function __construct( $data, $addon ) { + if ( $data instanceof WP_Post ) { + $data = self::data_from_post( $data ); + } + + $this->addon = $addon; + $this->data = $this->validate( $data ); + + if ( $this->is_valid ) { + $this->id = $this->addon . '-' . $data['name']; + } + } + + /** + * Magic method to proxy private template attributes and data. + * + * @param string $name Attribute name. + * + * @return mixed Attribute value or null. + */ + public function __get( $name ) { + switch ( $name ) { + case 'id': + return $this->id; + case 'addon': + return $this->addon; + case 'data': + return $this->data; + case 'is_valid': + return ! is_wp_error( $this->data ) && + Addon::addon( $this->addon ) !== null; + default: + if ( ! $this->is_valid ) { + return; + } + + return $this->data[ $name ] ?? null; + } + } + + /** + * Validates input data against the template schema. + * + * @param array $data Input data. + * + * @return array|WP_Error Validated data. + */ + private function validate( $data ) { + $schema = static::schema( $this->addon ); + $defaults = static::defaults( $this->addon, $schema ); + + if ( empty( $data['integrations'] ) ) { + foreach ( Integration::integrations() as $integration ) { + if ( 'woo' !== $integration::NAME ) { + $data['integrations'][] = $integration::NAME; + } + } + } + + $data = wpct_plugin_merge_object( $data, $defaults, $schema ); + return wpct_plugin_sanitize_with_schema( $data, $schema ); + } + + /** + * Decorates the template data for REST responses. + * + * @return array REST data. + */ + public function data() { + if ( ! $this->is_valid ) { + return; + } + + return array_merge( + array( + 'id' => $this->id, + 'addon' => $this->addon, + ), + $this->data + ); + } + + /** + * Gets the templates's post ID from database. + * + * @return int|null Int if it's stored on the database, null otherwise. + */ + private function get_post_id() { + // phpcs:disable WordPress.DB.SlowDBQuery + $ids = get_posts( + array( + 'post_type' => self::TYPE, + 'name' => $this->name, + 'meta_key' => '_fb-addon', + 'meta_value' => $this->addon, + 'fields' => 'ids', + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'update_menu_item_cache' => false, + ) + ); + // phpcs:enable + + if ( count( $ids ) ) { + return $ids[0]; + } + } + + /** + * Save the template config to the database as a custom post entry. + * + * @return int|WP_Error Post ID or WP_Error in case of insert errors. + */ + public function save() { + if ( ! $this->is_valid ) { + return $this->data; + } + + $post_arr = array( + 'post_type' => self::TYPE, + 'post_name' => $this->name, + 'post_title' => $this->title, + 'post_excerpt' => $this->description, + ); + + $post_id = $this->get_post_id(); + if ( $post_id ) { + $post_arr['ID'] = $post_id; + $post_id = wp_update_post( $post_arr, true ); + } else { + $post_id = wp_insert_post( $post_arr, true ); + } + + if ( ! is_wp_error( $post_id ) ) { + update_post_meta( $post_id, '_template-fields', $this->fields ); + update_post_meta( $post_id, '_template-form', $this->form ); + update_post_meta( $post_id, '_template-bridge', $this->bridge ); + update_post_meta( $post_id, '_template-backend', $this->backend ); + update_post_meta( + $post_id, + '_template-credential', + $this->credential + ); + } + + return $post_id; + } + + /** + * Delete the template config from the database and restore its default configuration. + * If the template does not exists as file-based configuration, the template will be deleted. + * + * @return bool + */ + public function reset() { + $post_id = $this->get_post_id(); + + if ( ! $post_id ) { + return false; + } + + return wp_delete_post( $post_id, true ) instanceof WP_Post; + } + + /** + * Applies the input fields with the template's data to + * create a form and bind it with a bridge. + * + * @param array $fields User input fields data. + * @param string $integration Target integration. + */ + public function use( $fields, $integration ) { + if ( ! $this->is_valid ) { + return new WP_Error( + 'invalid_template', + __( 'The target template is invalid', 'forms-bridge' ) + ); + } + + $template = $this->data; + $schema = static::schema( $this->addon ); + + // Add constants to the user fields. + foreach ( $template['fields'] as $field ) { + if ( ! empty( $field['value'] ) ) { + $fields[] = $field; + } + } + + $all_fields = wpct_plugin_merge_collection( + $fields, + $template['fields'], + $schema['properties']['fields']['items'] + ); + + $requireds = array_filter( + $all_fields, + static function ( $field ) { + return ( $field['required'] ?? false ) && ! isset( $field['value'] ); + } + ); + + if ( count( $requireds ) || count( $fields ) > count( $all_fields ) ) { + return new WP_Error( + 'invalid_fields', + __( 'Invalid template fields', 'forms-bridge' ) + ); + } + + $data = $template; + foreach ( $fields as $field ) { + $is_required = $field['required'] ?? false; + + $field_schema = $schema['properties']['fields']['items']; + $field_schema['required'] = array( 'ref', 'name' ); + + if ( $is_required ) { + $field_schema['required'][] = 'value'; + } + + $field = wpct_plugin_sanitize_with_schema( $field, $field_schema ); + + if ( is_wp_error( $field ) ) { + return new WP_Error( + 'invalid_field', + sprintf( + /* translators: %s: Field name */ + __( + 'Field `%s` does not match the schema', + 'forms-bridge' + ), + $field['name'] + ) + ); + } + + if ( ! isset( $field['value'] ) ) { + continue; + } + + if ( is_array( $field['value'] ) && empty( $field['type'] ) ) { + continue; + } + + if ( '' === $field['value'] ) { + continue; + } + + if ( 'boolean' === $field['type'] ) { + if ( ! isset( $field['value'][0] ) ) { + continue; + } else { + $field['value'] = '1'; + } + } + + // Inherit form field structure if field ref points to form fields. + if ( '#form/fields[]' === $field['ref'] ) { + $index = array_search( + $field['name'], + array_column( $template['form']['fields'], 'name' ), + true + ); + + $form_field = $template['form']['fields'][ $index ]; + $field['index'] = $index; + $field['value'] = array_merge( + $form_field, + array( + 'value' => $field['value'], + ) + ); + } elseif ( + '#backend/headers[]' === $field['ref'] || + '#bridge/custom_fields[]' === $field['ref'] + ) { + $field['value'] = array( + 'name' => $field['name'], + 'value' => $field['value'] ?? null, + ); + } + + $keys = explode( '/', substr( $field['ref'], 1 ) ); + $leaf = &$data; + foreach ( $keys as $key ) { + $clean_key = str_replace( '[]', '', $key ); + if ( ! isset( $leaf[ $clean_key ] ) ) { + return new WP_Error( + 'invalid_ref', + sprintf( + /* translators: %s: ref value */ + __( + 'Invalid template field ref `%s`', + 'forms-bridge' + ), + $field['ref'] + ) + ); + } + + $leaf = &$leaf[ $clean_key ]; + } + + if ( '[]' === substr( $key, -2 ) ) { + if ( isset( $field['index'] ) ) { + $leaf[ $field['index'] ] = $field['value']; + } else { + $leaf[] = $field['value']; + } + } elseif ( isset( $field['value'] ) ) { + $leaf[ $field['name'] ] = $field['value']; + } + } + + $data = apply_filters( + 'forms_bridge_template_data', + $data, + $this->id, + $this + ); + + if ( is_wp_error( $data ) ) { + return $data; + } elseif ( empty( $data ) ) { + return new WP_Error( + 'template_creation_error', + __( 'There is a problem with the template data', 'forms-bridge' ) + ); + } + + if ( 'woo' === $integration ) { + $data['form']['id'] = 1; + } elseif ( 'wpforms' === $integration ) { + $mappers = array(); + foreach ( $data['form']['fields'] as &$field ) { + if ( 'file' !== $field['type'] ) { + $mappers[] = array( + 'from' => JSON_Finger::sanitize_key( $field['label'] ), + 'to' => $field['name'], + 'cast' => 'inherit', + ); + } + + $field['name'] = $field['label']; + } + + $data['bridge']['mutations'][0] = array_merge( + $mappers, + $data['bridge']['mutations'][0] ?? array() + ); + } + + $integration_instance = Integration::integration( $integration ); + + try { + $create_form = ! $this->form_exists( + $data['form']['id'] ?? null, + $integration + ); + + if ( $create_form ) { + do_action_ref_array( + 'forms_bridge_before_template_form', + array( + $data['form'], + $this->name, + $this, + ) + ); + + $form_id = $integration_instance->create_form( $data['form'] ); + + if ( ! $form_id ) { + return new WP_Error( + 'form_creation_error', + __( + 'Forms bridge can\'t create the form', + 'forms-bridge' + ), + array( + 'status' => 400, + 'data' => $data['form'], + ) + ); + } + + $data['form']['id'] = $form_id; + + do_action( + 'forms_bridge_template_form', + $data['form'], + $this->id, + $this + ); + } + + $data['bridge']['form_id'] = + $integration . ':' . $data['form']['id']; + + $create_credential = false; + if ( ! empty( $data['credential']['name'] ) ) { + $create_credential = ! $this->credential_exists( + $data['credential']['name'] + ); + + if ( $create_credential ) { + $result = $this->create_credential( $data['credential'] ); + + if ( ! $result ) { + if ( $create_form ) { + $integration_instance->remove_form( + $data['form']['id'] + ); + } + + return new WP_Error( + 'credential_creation_error', + __( + 'Forms bridge can\'t create the credential', + 'forms-bridge', + ), + array( + 'status' => 400, + 'data' => $data['credential'], + ) + ); + } + } + + $data['backend']['credential'] = $data['credential']['name']; + } + + $create_backend = ! $this->backend_exists( $data['backend']['name'] ); + if ( $create_backend ) { + $result = $this->create_backend( $data['backend'] ); + + if ( ! $result ) { + if ( $create_form ) { + $integration_instance->remove_form( $data['form']['id'] ); + } + + if ( $create_credential ) { + $this->remove_credential( $data['credential']['name'] ); + } + + return new WP_Error( + 'backend_creation_error', + __( + 'Forms bridge can\'t create the backend', + 'forms-bridge', + ), + array( + 'status' => 400, + 'data' => $data['backend'], + ) + ); + } + } + + $data['bridge']['backend'] = $data['backend']['name']; + + $bridge_created = $this->create_bridge( $data['bridge'] ); + + if ( ! $bridge_created ) { + if ( $create_form ) { + $integration_instance->remove_form( + $data['bridge']['form_id'] + ); + } + + if ( $create_credential ) { + $this->remove_credential( $data['credential']['name'] ); + } + + if ( $create_backend ) { + $this->remove_backend( $data['backend']['name'] ); + } + + return new WP_Error( + 'bridge_creation_error', + __( + 'Forms bridge can\'t create the form bridge', + 'forms-bridge', + ), + array( + 'status' => 400, + 'data' => $data['bridge'], + ) + ); + } + } catch ( Error | Exception $e ) { + if ( isset( $create_form ) && $create_form ) { + $integration_instance->remove_form( $data['form']['id'] ); + } + + if ( isset( $create_credential ) && $create_credential ) { + $this->remove_credential( $data['credential']['name'] ); + } + + if ( isset( $create_backend ) && $create_backend ) { + $this->remove_backend( $data['backend']['name'] ); + } + + if ( isset( $bridge_created ) && $bridge_created ) { + $this->remove_bridge( $data['bridge']['name'] ); + } + + return new WP_Error( + 'internal_server_error', + $e->getMessage(), + array( + 'status' => 500, + ) + ); + } + + return true; + } + + /** + * Checks if a form with the given id exists on the settings store. + * + * @param string $form_id Internal ID of the form. + * @param string $integration Slug of the target integration. + * + * @return boolean + */ + private function form_exists( $form_id, $integration ) { + $form = FBAPI::get_form_by_id( $form_id, $integration ); + return ! empty( $form['id'] ); + } + + /** + * Checks if a backend with the given name exists on the settings store. + * + * @param string $name Backend name. + * + * @return boolean + */ + final protected function backend_exists( $name ) { + $backends = Settings_Store::setting( 'http' )->backends ?: array(); + return array_search( $name, array_column( $backends, 'name' ), true ) !== false; + } + + /** + * Stores the backend data on the settings store. + * + * @param array $data Backend data. + * + * @return boolean Creation result. + */ + private function create_backend( $data ) { + $setting = Settings_Store::setting( 'http' ); + $backends = $setting->backends ?: array(); + + do_action_ref_array( + 'forms_bridge_before_template_backend', + array( + $data, + $this->name, + $this, + ) + ); + + $setting->backends = array_merge( $backends, array( $data ) ); + $setting->flush(); + + $is_valid = $this->backend_exists( $data['name'] ); + if ( ! $is_valid ) { + return; + } + + do_action( 'forms_bridge_template_backend', $data, $this->id, $this ); + return true; + } + + /** + * Removes backend from the settings store by name. + * + * @param string $name Backend name. + */ + private function remove_backend( $name ) { + $setting = Settings_Store::setting( 'http' ); + $backends = $setting->backends ?: array(); + + $setting->backends = array_filter( + $backends, + static function ( + $backend + ) use ( $name ) { + return $backend['name'] !== $name; + } + ); + } + + /** + * Checks if a bridge with the given name exists on the settings store. + * + * @param string $name Bridge name. + * + * @return boolean + */ + private function bridge_exists( $name ) { + $bridges = Settings_Store::setting( $this->addon )->bridges ?: array(); + return false !== array_search( $name, array_column( $bridges, 'name' ), true ); + } + + /** + * Stores the form bridge data on the settings store. + * + * @param array $data Form bridge data. + * + * @return boolean Creation result. + */ + private function create_bridge( $data ) { + $name_conflict = $this->bridge_exists( $data['name'] ); + if ( $name_conflict ) { + return; + } + + $setting = Settings_Store::setting( $this->addon ); + $bridges = $setting->bridges ?: array(); + + do_action_ref_array( + 'forms_bridge_before_template_bridge', + array( + $data, + $this->name, + $this, + ) + ); + + $setting->bridges = array_merge( $bridges, array( $data ) ); + $setting->flush(); + + $is_valid = $this->bridge_exists( $data['name'] ); + if ( ! $is_valid ) { + return; + } + + do_action( 'forms_bridge_template_bridge', $data, $this->id, $this ); + return true; + } + + /** + * Removes a bridge from the settings store by name. + * + * @param string $name Bridge name. + */ + private function remove_bridge( $name ) { + $setting = Settings_Store::setting( $this->addon ); + $bridges = $setting->bridges ?: array(); + + $setting->bridges = array_filter( + $bridges, + static function ( + $bridge + ) use ( $name ) { + return $bridge['name'] !== $name; + } + ); + } + + /** + * Checks if a credential with the given name exists on the settings store. + * + * @param string $name Credential name. + * + * @return boolean + */ + private function credential_exists( $name ) { + $credentials = Settings_Store::setting( 'http' )->credentials ?: array(); + return false !== array_search( $name, array_column( $credentials, 'name' ), true ); + } + + /** + * Stores the bridge credential data on the settings store. + * + * @param array $data Credential data. + * + * @return boolean Creation result. + */ + private function create_credential( $data ) { + $setting = Settings_Store::setting( 'http' ); + $credentials = $setting->credentials ?: array(); + + if ( ! is_array( $credentials ) ) { + return; + } + + do_action_ref_array( + 'forms_bridge_before_template_credential', + array( + $data, + $this->name, + $this, + ) + ); + + $setting->credentials = array_merge( $credentials, array( $data ) ); + $setting->flush(); + + $is_valid = $this->credential_exists( $data['name'] ); + if ( ! $is_valid ) { + return; + } + + do_action( 'forms_bridge_template_credential', $data, $this->id, $this ); + return true; + } + + /** + * Removes a credential from the settings store by name. + * + * @param string $name Credential name. + */ + private function remove_credential( $name ) { + $setting = Settings_Store::setting( 'http' ); + $credentials = $setting->credentials ?: array(); + + $setting->credentials = array_filter( + $credentials, + static function ( + $credential + ) use ( $name ) { + return $credential['name'] !== $name; + } + ); + } +} diff --git a/forms-bridge/includes/class-form-bridge.php b/forms-bridge/includes/class-form-bridge.php new file mode 100644 index 00000000..a94dfbcc --- /dev/null +++ b/forms-bridge/includes/class-form-bridge.php @@ -0,0 +1,927 @@ + 'http://json-schema.org/draft-04/schema#', + 'title' => 'form-bridge', + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'title' => _x( 'Name', 'Bridge schema', 'forms-bridge' ), + 'description' => __( + 'Unique name of the bridge', + 'forms-bridge' + ), + 'type' => 'string', + 'minLength' => 1, + ), + 'form_id' => array( + 'title' => _x( 'Form', 'Bridge schema', 'forms-bridge' ), + 'description' => __( + 'Internal form id with integration prefix', + 'forms-bridge' + ), + 'type' => 'string', + 'pattern' => '^\w+:\d+$', + 'default' => '', + ), + 'backend' => array( + 'title' => _x( 'Backend', 'Bridge schema', 'forms-bridge' ), + 'description' => __( 'Backend name', 'forms-bridge' ), + 'type' => 'string', + // 'default' => '', + ), + 'endpoint' => array( + 'title' => _x( 'Endpoint', 'Bridge schema', 'forms-bridge' ), + 'description' => __( 'HTTP API endpoint', 'forms-bridge' ), + 'type' => 'string', + 'default' => '/', + ), + 'method' => array( + 'title' => _x( 'Method', 'Bridge schema', 'forms-bridge' ), + 'description' => __( 'HTTP method', 'forms-bridge' ), + 'type' => 'string', + 'enum' => array( 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' ), + 'default' => 'POST', + ), + 'custom_fields' => array( + 'description' => __( + 'Array of bridge\'s custom fields', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'type' => 'string', + 'minLength' => 1, + 'validate_callback' => + '\FORMS_BRIDGE\JSON_Finger::validate', + ), + 'value' => array( + 'type' => array( 'string', 'integer', 'number' ), + 'minLength' => 1, + ), + ), + 'additionalProperties' => false, + 'required' => array( 'name', 'value' ), + ), + 'default' => array(), + ), + 'mutations' => array( + 'description' => __( + 'Stack of bridge mutations', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'from' => array( + 'type' => 'string', + 'minLength' => 1, + 'validate_callback' => + '\FORMS_BRIDGE\JSON_Finger::validate', + ), + 'to' => array( + 'type' => 'string', + 'minLength' => 1, + 'validate_callback' => + '\FORMS_BRIDGE\JSON_Finger::validate', + ), + 'cast' => array( + 'type' => 'string', + 'enum' => array( + 'boolean', + 'string', + 'integer', + 'number', + 'not', + 'and', + 'or', + 'xor', + 'json', + 'csv', + 'concat', + 'join', + 'sum', + 'count', + 'inherit', + 'copy', + 'null', + ), + ), + ), + 'additionalProperties' => false, + 'required' => array( 'from', 'to', 'cast' ), + ), + ), + 'default' => array(), + ), + 'workflow' => array( + 'description' => __( + 'Chain of workflow job names', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( + 'type' => 'string', + 'minLength' => 1, + ), + 'default' => array(), + ), + 'is_valid' => array( + 'description' => __( + 'Validation result of the bridge setting', + 'forms-bridge' + ), + 'type' => 'boolean', + 'default' => true, + ), + 'enabled' => array( + 'description' => __( + 'Boolean flag to enable/disable a bridge', + 'forms-bridge' + ), + 'type' => 'boolean', + 'default' => true, + ), + ), + 'required' => array( + 'name', + 'form_id', + 'backend', + 'method', + 'endpoint', + 'custom_fields', + 'mutations', + 'workflow', + 'is_valid', + 'enabled', + ), + 'additionalProperties' => false, + ); + + if ( ! $addon ) { + return $schema; + } + + return apply_filters( 'forms_bridge_bridge_schema', $schema, $addon ); + } + + /** + * Handles the form bridge setting data. + * + * @var array + */ + protected $data; + + /** + * Handles the form bridge identifier string. + * + * @var string + */ + protected $id; + + /** + * Handles form bridge addon slug. + * + * @var string + */ + protected $addon; + + /** + * Stores the form bridge's data as a private attribute. + * + * @param array $data Bridge data. + * @param string $addon Bridge addon. + */ + public function __construct( $data, $addon = 'rest' ) { + $this->data = wpct_plugin_sanitize_with_schema( + $data, + static::schema( $addon ) + ); + $this->addon = $addon; + + if ( $this->is_valid ) { + $this->id = $addon . '-' . $data['name']; + } + } + + public function data() { + if ( ! $this->is_valid ) { + return; + } + + return array_merge( + $this->data, + array( + 'id' => $this->id, + 'name' => $this->name, + 'addon' => $this->addon, + ) + ); + } + + /** + * Magic method to proxy public attributes to method getters. + * + * @param string $name Attribute name. + * + * @return mixed Attribute value or null. + */ + public function __get( $name ) { + switch ( $name ) { + case 'id': + return $this->id; + case 'addon': + return $this->addon; + case 'form': + return $this->form(); + case 'integration': + return $this->integration(); + case 'backend': + return $this->backend(); + case 'content_type': + return $this->content_type(); + case 'workflow': + return $this->workflow(); + case 'is_valid': + return ! is_wp_error( $this->data ) && + $this->data['is_valid'] && + Addon::addon( $this->addon ) !== null; + default: + if ( ! $this->is_valid ) { + return; + } + + return $this->data[ $name ] ?? null; + } + } + + /** + * Retrives the bridge's backend instance. + * + * @return Backend|null + */ + protected function backend() { + if ( ! $this->is_valid ) { + return; + } + + return FBAPI::get_backend( $this->data['backend'] ); + } + + /** + * Retrives the bridge's form data. + * + * @return array|null + */ + protected function form() { + $form_id = $this->form_id; + if ( ! $form_id ) { + return; + } + + if ( ! preg_match( '/^\w+:\d+$/', $form_id ) ) { + return; + } + + [$integration, $form_id] = explode( ':', $form_id ); + return FBAPI::get_form_by_id( $form_id, $integration ); + } + + /** + * Retrives the bridge's integration name. + * + * @return string + */ + protected function integration() { + $form_id = $this->form_id; + if ( ! $form_id ) { + return; + } + + if ( ! preg_match( '/^\w+:\d+$/', $form_id ) ) { + return; + } + + [$integration] = explode( ':', $form_id ); + return $integration; + } + + /** + * Gets bridge's default body encoding schema. + * + * @return string|null + */ + protected function content_type() { + if ( ! $this->is_valid ) { + return; + } + + $backend = FBAPI::get_backend( $this->data['backend'] ); + if ( ! $backend ) { + return; + } + + return $backend->content_type; + } + + /** + * Gets bridge's workflow instance. + * + * @return Workflow_Job|null; + */ + protected function workflow() { + if ( ! $this->is_valid ) { + return; + } + + return Job::from_workflow( $this->data['workflow'], $this->addon ); + } + + /** + * Submits payload and attachments to the bridge's backend. + * + * @param array $payload Payload data. + * @param array $attachments Submission's attached files. + * + * @return array|WP_Error Http request response. + */ + public function submit( $payload = array(), $attachments = array() ) { + if ( ! $this->is_valid ) { + return new WP_Error( 'invalid_bridge' ); + } + + $schema = $this->schema(); + + if ( + ! in_array( + $this->method, + $schema['properties']['method']['enum'], + true + ) + ) { + return new WP_Error( + 'method_not_allowed', + sprintf( + /* translators: %s: method name */ + __( 'HTTP method %s is not allowed', 'forms-bridge' ), + sanitize_text_field( $this->method ) + ), + array( 'method' => $this->method ) + ); + } + + $backend = $this->backend(); + if ( ! $backend ) { + return new WP_Error( 'invalid_bridge' ); + } + + $method = $this->method; + + return $backend->$method( $this->endpoint, $payload, array(), $attachments ); + } + + /** + * Apply cast mappers to data. + * + * @param array $data Array of data. + * @param array|null $mutation Array of mappers. + * + * @return array Data modified by the bridge's mappers. + */ + final public function apply_mutation( $data, $mutation = null ) { + if ( ! is_array( $data ) ) { + return $data; + } + + $finger = new JSON_Finger( $data ); + + if ( null === $mutation ) { + $mutation = $this->mutations[0] ?? array(); + } + + foreach ( $mutation as $mapper ) { + $is_valid = + JSON_Finger::validate( $mapper['from'] ) && + JSON_Finger::validate( $mapper['to'] ); + + if ( ! $is_valid ) { + continue; + } + + $isset = $finger->isset( $mapper['from'], $is_conditional ); + if ( ! $isset ) { + if ( $is_conditional ) { + continue; + } + + $value = null; + } else { + $value = $finger->get( $mapper['from'] ); + } + + $unset = 'null' === $mapper['cast']; + + if ( 'copy' !== $mapper['cast'] ) { + $unset = + $unset || + preg_replace( '/^\?/', '', $mapper['from'] ) !== + $mapper['to']; + } + + if ( $unset ) { + $finger->unset( $mapper['from'] ); + } + + if ( 'null' !== $mapper['cast'] ) { + $finger->set( $mapper['to'], $this->cast( $value, $mapper ) ); + } + } + + return $finger->data(); + } + + /** + * Casts value to the given type. + * + * @param mixed $value Original value. + * @param string $mapper Source mapper. + * + * @return mixed + */ + private function cast( $value, $mapper ) { + if ( strpos( $mapper['from'], '[]' ) !== false ) { + return $this->cast_expanded( $value, $mapper ); + } + + switch ( $mapper['cast'] ) { + case 'string': + return (string) $value; + case 'integer': + return (int) $value; + case 'number': + return (float) $value; + case 'boolean': + return (bool) $value; + case 'not': + return ! $value; + case 'and': + return array_reduce( + (array) $value, + fn( $bool, $val ) => $bool && $val, + ! empty( $val ) + ); + case 'or': + return array_reduce( + (array) $value, + fn( $bool, $val ) => $bool || $val, + false + ); + case 'xor': + return array_reduce( + (array) $value, + fn( $bool, $val ) => $bool xor $val, + false + ); + case 'json': + if ( ! is_array( $value ) ) { + return ''; + } + + return wp_json_encode( $value, JSON_UNESCAPED_UNICODE ); + case 'csv': + if ( ! wp_is_numeric_array( $value ) ) { + return ''; + } + + return implode( ',', $value ); + case 'concat': + if ( ! wp_is_numeric_array( $value ) ) { + return ''; + } + + return implode( ' ', $value ); + case 'join': + if ( ! wp_is_numeric_array( $value ) ) { + return ''; + } + + return implode( '', $value ); + case 'sum': + if ( ! wp_is_numeric_array( $value ) ) { + return 0; + } + + return array_reduce( + (array) $value, + static function ( $total, $val ) { + return $total + $val; + }, + 0 + ); + case 'count': + if ( ! is_array( $value ) ) { + return 0; + } + + return count( (array) $value ); + case 'inherit': + return $value; + case 'copy': + return $value; + case 'null': + return; + default: + return (string) $value; + } + } + + private function cast_expanded( $values, $mapper ) { + if ( ! wp_is_numeric_array( $values ) ) { + return array(); + } + + $is_expanded = + strpos( preg_replace( '/\[\]$/', '', $mapper['from'] ), '[]' ) !== + false; + + if ( ! $is_expanded ) { + return array_map( + function ( $value ) use ( $mapper ) { + return $this->cast( + $value, + array( + 'from' => '', + 'to' => '', + 'cast' => $mapper['cast'], + ) + ); + }, + $values + ); + } + + preg_match_all( + '/\[\](?=[^\[])/', + preg_replace( '/\[\]$/', '', $mapper['to'] ), + $to_expansions + ); + preg_match_all( + '/\[\](?=[^\[])/', + preg_replace( '/\[\]$/', '', $mapper['from'] ), + $from_expansions + ); + + if ( empty( $from_expansions ) && count( $to_expansions ) > 1 ) { + return array(); + } elseif ( + ! empty( $from_expansions ) && + count( $to_expansions[0] ) > count( $from_expansions[0] ) + ) { + return array(); + } + + $parts = array_filter( explode( '[]', $mapper['from'] ) ); + $before = $parts[0]; + $after = implode( '[]', array_slice( $parts, 1 ) ); + + $l = count( $values ); + for ( $i = 0; $i < $l; $i++ ) { + $pointer = "{$before}[{$i}]{$after}"; + $values[ $i ] = $this->cast( + $values[ $i ], + array( + 'from' => $pointer, + 'to' => '', + 'cast' => $mapper['cast'], + ) + ); + } + + return $values; + } + + final public function prepare_mappers( $form ) { + foreach ( $form['fields'] as $field ) { + $is_file = $field['is_file'] ?? false; + $is_conditional = $field['conditional'] ?? false; + $is_multi = $field['is_multi'] ?? false; + + $schema = $field['schema'] ?? array( 'type' => '-' ); + + if ( + 'array' === $schema['type'] && + false === ( $schema['additionalItems'] ?? true ) + ) { + $min_items = $field['schema']['minItems'] ?? 0; + $max_items = $field['schema']['maxItems'] ?? 0; + + $is_conditional = $is_conditional || $min_items < $max_items; + } + + if ( $is_conditional ) { + $name = $field['name']; + + $l = count( $this->data['mutations'] ); + for ( $i = 0; $i < $l; $i++ ) { + $mutation = $this->data['mutations'][ $i ]; + + $m = count( $mutation ); + for ( $j = 0; $j < $m; $j++ ) { + $mapper = $this->data['mutations'][ $i ][ $j ]; + + $from = preg_replace( '/\[\d*\]/', '', $mapper['from'] ); + if ( + $from === $name || + ( $is_file && $from === $name . '_filename' ) + ) { + $this->data['mutations'][ $i ][ $j ]['from'] = + '?' . $mapper['from']; + + $name = preg_replace( + '/\[\d*\]/', + '', + $mapper['to'] + ); + } + } + } + } + + if ( $is_file && $is_multi ) { + $name = $field['name']; + + $len = count( $this->data['mutations'][0] ?? array() ); + for ( $i = 0; $i < $len; $i++ ) { + $mapper = $this->data['mutations'][0][ $i ]; + + $from = preg_replace( '/\[\d*\]/', '', $mapper['from'] ); + $from = preg_replace( '/^\?/', '', $mapper['from'] ); + + if ( $from !== $name && $from !== $name . '_filename' ) { + continue; + } + + $this->data['mutations'][0][ $i ]['from'] = + $mapper['from'] . '_1'; + $this->data['mutations'][0][ $i ]['to'] = + $mapper['to'] . '_1'; + + for ( $j = 2; $j < 10; $j++ ) { + $from = + strstr( $mapper['from'], '?' ) ?: + '?' . $mapper['from']; + + $this->data['mutations'][0][] = array( + 'from' => $from . '_' . $j, + 'to' => $mapper['to'] . '_' . $j, + 'cast' => $mapper['cast'], + ); + } + } + } + } + } + + private static function get_tags() { + return array( + 'site_title' => static function () { + return get_bloginfo( 'name' ); + }, + 'site_description' => static function () { + return get_bloginfo( 'description' ); + }, + 'blog_url' => static function () { + return get_bloginfo( 'wpurl' ); + }, + 'site_url' => static function () { + return get_bloginfo( 'url' ); + }, + 'admin_email' => static function () { + return get_bloginfo( 'admin_email' ); + }, + 'wp_version' => static function () { + return get_bloginfo( 'version' ); + }, + 'ip_address' => static function () { + if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { + return sanitize_text_field( + $_SERVER['HTTP_X_FORWARDED_FOR'] + ); + } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) { + return sanitize_text_field( $_SERVER['REMOTE_ADDR'] ); + } + }, + 'referer' => static function () { + if ( isset( $_SERVER['HTTP_REFERER'] ) ) { + return sanitize_text_field( $_SERVER['HTTP_REFERER'] ); + } + }, + 'user_agent' => static function () { + if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) { + return sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ); + } + }, + 'browser_locale' => static function () { + if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) { + return sanitize_text_field( + $_SERVER['HTTP_ACCEPT_LANGUAGE'] + ); + } + }, + 'locale' => static function () { + return apply_filters( + 'wpct_i18n_current_language', + get_locale(), + 'locale' + ); + }, + 'language' => static function () { + include_once ABSPATH . + 'wp-admin/includes/translation-install.php'; + $translations = wp_get_available_translations(); + $locale = get_locale(); + + return $translations[ $locale ]['native_name'] ?? $locale; + }, + 'datetime' => static function () { + return date( 'Y-m-d H:i:s', time() ); + }, + 'gmt_datetime' => static function () { + return gmdate( 'Y-m-d H:i:s', time() ); + }, + 'timestamp' => static function () { + return time(); + }, + 'iso_date' => static function () { + return date( 'c', time() ); + }, + 'gmt_iso_date' => static function () { + return gmdate( 'c', time() ); + }, + 'utc_date' => static function () { + $date = gmdate( 'c', time() ); + return preg_replace( '/\+\d+\:\d+$/', 'Z', $date ); + }, + 'user_id' => static function () { + $user = wp_get_current_user(); + return $user->ID; + }, + 'user_login' => static function () { + $user = wp_get_current_user(); + return $user->user_login; + }, + 'user_name' => static function () { + $user = wp_get_current_user(); + return $user->display_name; + }, + 'user_email' => static function () { + $user = wp_get_current_user(); + return $user->user_email; + }, + 'submission_id' => static function () { + return FBAPI::get_submission_id(); + }, + 'form_title' => static function () { + $form = FBAPI::get_current_form(); + return $form['title'] ?? null; + }, + 'form_id' => static function () { + $form = FBAPI::get_current_form(); + return $form['id'] ?? null; + }, + ); + } + + final public function add_custom_fields( $payload = array() ) { + if ( ! is_array( $payload ) ) { + return $payload; + } + + $finger = new JSON_Finger( $payload ); + + $custom_fields = $this->custom_fields ?: array(); + + foreach ( $custom_fields as $custom_field ) { + $is_value = JSON_Finger::validate( $custom_field['name'] ); + if ( ! $is_value ) { + continue; + } + + $value = $this->replace_field_tags( $custom_field['value'] ); + $finger->set( $custom_field['name'], $value ); + } + + return $finger->data(); + } + + private function replace_field_tags( $value ) { + $tags = self::get_tags(); + foreach ( $tags as $tag => $getter ) { + if ( strstr( $value, '$' . $tag ) !== false ) { + $value = str_replace( '$' . $tag, $getter(), $value ); + } + } + + return $value; + } + + /** + * Returns a clone of the bridge instance with its data patched by + * the partial array. + * + * @param array $partial Bridge data. + * + * @return Form_Bridge + */ + public function patch( $partial = array() ) { + if ( ! $this->is_valid ) { + return $this; + } + + $data = array_merge( $this->data, $partial ); + return new static( $data, $this->addon ); + } + + public function save() { + if ( ! $this->is_valid ) { + return false; + } + + $setting = Settings_Store::setting( $this->addon ); + if ( ! $setting ) { + return false; + } + + $bridges = $setting->bridges ?: array(); + + $index = array_search( $this->name, array_column( $bridges, 'name' ), true ); + + if ( false === $index ) { + $bridges[] = $this->data; + } else { + $bridges[ $index ] = $this->data; + } + + $setting->bridges = $bridges; + + return true; + } + + public function delete() { + if ( ! $this->is_valid ) { + return false; + } + + $setting = Settings_Store::setting( $this->addon ); + if ( ! $setting ) { + return false; + } + + $bridges = $setting->bridges ?: array(); + + $index = array_search( $this->name, array_column( $bridges, 'name' ), true ); + + if ( false === $index ) { + return false; + } + + array_splice( $bridges, $index, 1 ); + $setting->bridges = $bridges; + + return true; + } +} diff --git a/forms-bridge/includes/class-forms-bridge.php b/forms-bridge/includes/class-forms-bridge.php new file mode 100644 index 00000000..3b6c14d3 --- /dev/null +++ b/forms-bridge/includes/class-forms-bridge.php @@ -0,0 +1,651 @@ +%s', + esc_url( $url ), + esc_html( $label ) + ); + array_push( $links, $link ); + + return $links; + }, + 15, + 2 + ); + + add_action( + 'forms_bridge_on_failure', + static function ( $bridge, $error, $payload, $attachments = array() ) { + self::notify_error( $bridge, $error, $payload, $attachments ); + }, + 99, + 4 + ); + + add_action( 'init', array( self::class, 'load_data' ), 0, 0 ); + + add_action( + 'in_plugin_update_message-forms-bridge/forms-bridge.php', + function ( $plugin_data, $response ) { + if ( 'forms-bridge' !== $response->slug ) { + return; + } + + if ( + ! preg_match( + '/^(\d+)\.\d+\.\d+$/', + $response->new_version, + $matches + ) + ) { + return; + } + + $new_version = $matches[1]; + $db_version = get_option( self::DB_VERSION, '1.0.0' ); + + if ( ! preg_match( '/^(\d+)\.\d+\.\d+$/', $db_version, $matches ) ) { + return; + } + + $from_version = $matches[1]; + + if ( $new_version > $from_version ) { + echo '
' . + ' ' . + esc_html( + __( + 'This is a major release and while tested thoroughly you might experience conflicts or lost data. We recommend you back up your data before updating and check your configuration after updating.', + 'forms-bridge' + ), + ) . + ''; + } + }, + 10, + 2 + ); + } + + /** + * Plugin activation callback. Stores the plugin version on the database + * if it doesn't exists. + */ + public static function activate() { + $version = get_option( self::DB_VERSION ); + if ( false === $version ) { + update_option( self::DB_VERSION, self::version(), true ); + } + } + + /** + * Init hook callabck. Checks if the stored db version mismatch the current plugin version + * and, if it is, performs db migrations. + */ + protected static function init() { + $db_version = get_option( self::DB_VERSION ); + if ( self::version() !== $db_version && ! defined( 'WP_TESTS_DOMAIN' ) ) { + self::do_migrations(); + } + } + + /** + * Data loader. + */ + public static function load_data() { + $data_dir = self::path() . '/data'; + + foreach ( array_diff( scandir( $data_dir ), array( '.', '..' ) ) as $file ) { + $filepath = "{$data_dir}/{$file}"; + if ( is_file( $filepath ) && is_readable( $filepath ) ) { + require_once $filepath; + } + } + } + + /** + * Enqueue admin client scripts + */ + private static function admin_enqueue_scripts() { + $version = self::version(); + + wp_enqueue_script( + 'forms-bridge', + plugins_url( 'assets/plugin.bundle.js', self::index() ), + array( + 'react', + 'react-jsx-runtime', + 'wp-api-fetch', + 'wp-components', + 'wp-dom-ready', + 'wp-element', + 'wp-i18n', + 'wp-api', + ), + $version, + array( 'in_footer' => true ) + ); + + wp_set_script_translations( + 'forms-bridge', + 'forms-bridge', + self::path() . 'languages' + ); + + wp_enqueue_style( 'wp-components' ); + + wp_enqueue_style( + 'highlight-js', + 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css', + array(), + '11.11.1' + ); + + wp_enqueue_script( + 'highlight-js', + 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js', + array(), + '11.11.1', + true, + ); + } + + /** + * Public access to the current bridge reference. + * + * @return Form_Bridge|null + */ + public static function current_bridge() { + return self::$current_bridge; + } + + /** + * Proceed with the submission sub-routine. + * + * @throws Error In case email error notification fails. + * @throws Exception In case email error notification fails. + */ + public static function do_submission() { + $form_data = FBAPI::get_current_form(); + + if ( ! $form_data ) { + return; + } + + if ( empty( $form_data['bridges'] ) ) { + return; + } + + Logger::log( 'Form data' ); + Logger::log( + array( + 'id' => $form_data['id'], + 'title' => $form_data['title'], + 'fields' => array_map( + function ( $field ) { + return $field['name'] ?? ''; + }, + $form_data['fields'] + ), + ) + ); + + $bridges = $form_data['bridges']; + + $submission = FBAPI::get_submission(); + Logger::log( 'Form submission' ); + Logger::log( $submission ); + + $uploads = FBAPI::get_uploads(); + Logger::log( 'Submission uploads' ); + Logger::log( $uploads ); + + if ( empty( $submission ) && empty( $uploads ) ) { + return; + } + + foreach ( $bridges as $bridge ) { + if ( ! $bridge->enabled ) { + Logger::log( + 'Skip submission for disabled bridge ' . $bridge->name + ); + continue; + } + + self::$current_bridge = $bridge; + + try { + $attachments = apply_filters( + 'forms_bridge_attachments', + self::attachments( $uploads ), + $bridge + ); + + if ( ! empty( $attachments ) ) { + $content_type = (string) $bridge->content_type; + if ( + in_array( + strtolower( $content_type ), + array( + 'application/json', + 'application/x-www-form-urlencoded', + ), + true + ) + ) { + $attachments = self::stringify_attachments( + $attachments + ); + foreach ( $attachments as $name => $value ) { + $submission[ $name ] = $value; + } + $attachments = array(); + Logger::log( 'Submission after attachments stringify' ); + Logger::log( $submission ); + } + } + + $payload = $bridge->add_custom_fields( $submission ); + Logger::log( 'Submission payload with bridge custom fields' ); + Logger::log( $payload ); + + $bridge->prepare_mappers( $form_data ); + $payload = $bridge->apply_mutation( $payload ); + Logger::log( 'Submission payload after mutation' ); + Logger::log( $payload ); + + $prune_empties = apply_filters( + 'forms_bridge_prune_empties', + true, + $bridge + ); + + if ( $prune_empties ) { + $payload = self::prune_empties( $payload ); + Logger::log( 'Submission payload after prune empties' ); + Logger::log( $payload ); + } + + $workflow = $bridge->workflow; + if ( $workflow ) { + $payload = $workflow->run( $payload, $bridge ); + + if ( empty( $payload ) ) { + Logger::log( 'Skip empty payload after bridge workflow' ); + continue; + } + + Logger::log( 'Payload after workflow' ); + Logger::log( $payload ); + } + + $payload = apply_filters( + 'forms_bridge_payload', + $payload, + $bridge + ); + + if ( empty( $payload ) ) { + Logger::log( 'Skip empty payload after user filter' ); + continue; + } + + Logger::log( 'Bridge payload' ); + Logger::log( $payload ); + + $skip = apply_filters( + 'forms_bridge_skip_submission', + false, + $bridge, + $payload, + $attachments + ); + + if ( $skip ) { + Logger::log( 'Skip submission' ); + continue; + } + + do_action( + 'forms_bridge_before_submission', + $bridge, + $payload, + $attachments + ); + + $response = $bridge->submit( $payload, $attachments ); + + $error = is_wp_error( $response ) ? $response : null; + if ( $error ) { + do_action( + 'forms_bridge_on_failure', + $bridge, + $error, + $payload, + $attachments + ); + } else { + Logger::log( 'Submission response' ); + Logger::log( $response ); + + do_action( + 'forms_bridge_after_submission', + $bridge, + $response, + $payload, + $attachments + ); + } + } catch ( Error | Exception $e ) { + $message = $e->getMessage(); + if ( 'notification_error' === $message ) { + throw $e; + } + + $error = new WP_Error( + 'internal_server_error', + $e->getMessage(), + array( + 'file' => $e->getFile(), + 'line' => $e->getLine(), + ) + ); + + do_action( + 'forms_bridge_on_failure', + $bridge, + $error, + $payload ?? $submission, + $attachments ?? array() + ); + } finally { + self::$current_bridge = null; + } + } + } + + /** + * Clean up submission empty fields. + * + * @param array $submission_data Submission data. + * + * @return array Submission data without empty fields. + */ + private static function prune_empties( $submission_data ) { + foreach ( $submission_data as $key => $val ) { + if ( '' === $val || null === $val ) { + unset( $submission_data[ $key ] ); + } + } + + return $submission_data; + } + + /** + * Transform collection of uploads to an attachments map. + * + * @param array $uploads Collection of uploaded files. + * + * @return array Map of uploaded files. + */ + private static function attachments( $uploads ) { + $attachments = array(); + + foreach ( $uploads as $name => $upload ) { + if ( $upload['is_multi'] ) { + $len = count( $uploads[ $name ]['path'] ); + for ( $i = 1; $i <= $len; $i++ ) { + $attachments[ $name . '_' . $i ] = $upload['path'][ $i - 1 ]; + } + } else { + $attachments[ $name ] = $upload['path']; + } + } + + return $attachments; + } + + /** + * Returns the attachments array with each attachment path replaced with its + * content as a base64 encoded string. For each file on the list, adds an + * additonal field with the file name on the response. + * + * @param array $attachments Submission attachments data. + * + * @return array Array with base64 encoded file contents and file names. + */ + private static function stringify_attachments( $attachments ) { + foreach ( $attachments as $name => $path ) { + if ( ! is_file( $path ) || ! is_readable( $path ) ) { + continue; + } + + $suffix = ''; + if ( preg_match( '/_\d+$/', $name, $matches ) ) { + $suffix = $matches[0]; + $name = substr( $name, 0, -strlen( $suffix ) ); + } + + $filename = basename( $path ); + $content = file_get_contents( $path ); + $attachments[ $name . $suffix ] = base64_encode( $content ); + $attachments[ $name . '_filename' . $suffix ] = $filename; + } + + return $attachments; + } + + /** + * Sends error notifications to the email receiver. + * + * @param Form_Bridge $bridge Bridge instance. + * @param WP_Error $error Error instance. + * @param array $payload Submission data. + * @param array $attachments Submission attachments. + * + * @throws Exception When email notification fails. + */ + private static function notify_error( + $bridge, + $error, + $payload, + $attachments = array() + ) { + $email = Settings_Store::setting( 'general' )->notification_receiver; + + if ( empty( $email ) ) { + return; + } + + $skip = apply_filters( + 'forms_bridge_skip_error_notification', + false, + $error, + $bridge, + $payload, + $attachments + ); + + if ( $skip ) { + Logger::log( 'Skip error notification' ); + return; + } + + $form_data = $bridge->form; + $payload = wp_json_encode( $payload, JSON_PRETTY_PRINT ); + $error = wp_json_encode( + array( + 'error' => $error->get_error_message(), + 'context' => $error->get_error_data(), + ), + JSON_PRETTY_PRINT + ); + + Logger::log( 'Bridge submission error', Logger::ERROR ); + Logger::log( $error, Logger::ERROR ); + + $to = $email; + $subject = 'Forms Bridge Error'; + $body = "Form ID: {$form_data['id']}\n"; + $body .= "Form title: {$form_data['title']}\n"; + $body .= "Bridge name: {$bridge->name}\n"; + $body .= "Payload: {$payload}\n"; + $body .= "Error: {$error}\n"; + + $from_email = get_option( 'admin_email' ); + $headers = array( "From: Forms Bridge <{$from_email}>" ); + + Logger::log( 'Error notification' ); + Logger::log( $body ); + + $success = wp_mail( $to, $subject, $body, $headers, $attachments ); + if ( ! $success ) { + throw new Exception( 'notification_error' ); + } + } + + /** + * Apply db migrations on plugin upgrades. + */ + private static function do_migrations() { + $from = get_option( self::DB_VERSION, self::version() ); + + if ( ! preg_match( '/^\d+\.\d+\.\d+$/', $from ) ) { + Logger::log( 'Invalid db plugin version', Logger::ERROR ); + return; + } + + $to = self::version(); + + $migrations = array(); + $migrations_path = self::path() . '/migrations'; + + $as_int = fn( $version ) => (int) str_replace( '.', '', $version ); + + foreach ( + array_diff( scandir( $migrations_path ), array( '.', '..' ) ) + as $migration + ) { + $version = pathinfo( $migrations_path . '/' . $migration )['filename']; + + if ( $as_int( $version ) > $as_int( $to ) ) { + break; + } + + if ( ! empty( $migrations ) ) { + $migrations[] = $migration; + continue; + } + + if ( + $as_int( $version ) > $as_int( $from ) && + $as_int( $version ) <= $as_int( $to ) + ) { + $migrations[] = $migration; + } + } + + sort( $migrations ); + foreach ( $migrations as $migration ) { + include $migrations_path . '/' . $migration; + } + + update_option( self::DB_VERSION, $to ); + } + + /** + * Gets the path to the plugin namespaced upload directory. + * + * @return string + */ + public static function upload_dir() { + $dir = wp_upload_dir()['basedir'] . '/forms-bridge'; + + if ( ! is_dir( $dir ) ) { + if ( ! wp_mkdir_p( $dir ) ) { + return; + } + } + + return $dir; + } +} diff --git a/forms-bridge/includes/class-integration.php b/forms-bridge/includes/class-integration.php new file mode 100644 index 00000000..df744287 --- /dev/null +++ b/forms-bridge/includes/class-integration.php @@ -0,0 +1,513 @@ + $integrations. + */ + private static $integrations = array(); + + /** + * Checks if the integration plugin is active. + * + * @param string $integration Integration slug. + * + * @return bool + */ + private static function check_dependencies( $integration ) { + switch ( $integration ) { + case 'wpcf7': + $dep = 'contact-form-7/wp-contact-form-7.php'; + break; + case 'gf': + $dep = 'gravityforms/gravityforms.php'; + break; + case 'wpforms': + $dep = 'wpforms/wpforms.php'; + break; + case 'ninja': + $dep = 'ninja-forms/ninja-forms.php'; + break; + case 'woo': + $dep = 'woocommerce/woocommerce.php'; + break; + // case 'formidable': + // $plugin = 'formidable/formidable.php'; + // break; + // case 'forminator': + // $plugin = 'forminator/forminator.php'; + // break; + default: + return false; + } + + return Forms_Bridge::is_plugin_active( $dep ) || defined( 'WP_TESTS_DOMAIN' ); + } + + /** + * Public integrations registry getter. + * + * @return array Integration registry state. + */ + private static function registry() { + $state = get_option( self::REGISTRY, array() ) ?: array(); + $integrations_dir = FORMS_BRIDGE_INTEGRATIONS_DIR; + $integrations = array_diff( scandir( $integrations_dir ), array( '.', '..' ) ); + + $with_deps = array(); + $registry = array(); + foreach ( $integrations as $integration ) { + $integration_dir = "{$integrations_dir}/{$integration}"; + if ( ! is_dir( $integration_dir ) ) { + continue; + } + + $has_deps = self::check_dependencies( $integration ); + if ( $has_deps ) { + $with_deps[] = $integration; + } + + $index = "{$integration_dir}/class-{$integration}-integration.php"; + + if ( is_file( $index ) && is_readable( $index ) ) { + $registry[ $integration ] = + boolval( $state[ $integration ] ?? false ) && $has_deps; + } + } + + if ( count( $with_deps ) === 1 ) { + $registry[ $with_deps[0] ] = true; + } + + return $registry; + } + + /** + * Updates the integration's registry state. + * + * @param array $integrations New integrations' registry state. + */ + public static function update_registry( $integrations = array() ) { + $registry = self::registry(); + foreach ( $integrations as $name => $enabled ) { + if ( ! isset( $registry[ $name ] ) ) { + continue; + } + + $registry[ $name ] = (bool) $enabled; + } + + update_option( self::REGISTRY, $registry ); + } + + /** + * Public active integration instances getter. + * + * @return array List with integration instances. + */ + final public static function integrations() { + $integrations = array(); + foreach ( self::$integrations as $instance ) { + if ( $instance->enabled ) { + $integrations[] = $instance; + } + } + + return $integrations; + } + + /** + * Public getter of integration addapter instances. + * + * @param string $name Integration name. + * + * @return Integration|null + */ + final public static function integration( $name ) { + return self::$integrations[ $name ] ?? null; + } + + /** + * Public integrations loader. + */ + public static function load_integrations() { + $integrations_dir = FORMS_BRIDGE_INTEGRATIONS_DIR; + $registry = self::registry(); + + foreach ( $registry as $integration => $enabled ) { + $has_dependencies = self::check_dependencies( $integration ); + + if ( $has_dependencies ) { + require_once "{$integrations_dir}/{$integration}/class-{$integration}-integration.php"; + + if ( $enabled ) { + self::$integrations[ $integration ]->load(); + } + } + } + + Settings_Store::ready( + function ( $store ) { + $store::use_getter( + 'general', + function ( $data ) { + $registry = self::registry(); + $integrations = array(); + foreach ( self::$integrations as $name => $integration ) { + $integrations[ $name ] = array( + 'name' => $name, + 'title' => $integration::TITLE, + 'enabled' => $registry[ $name ] ?? false, + ); + } + + ksort( $integrations ); + $integrations = array_values( $integrations ); + + if ( count( $integrations ) === 1 ) { + $integrations[0]['enabled'] = true; + } + + $integrations = apply_filters( + 'forms_bridge_integrations', + $integrations + ); + return array_merge( $data, array( 'integrations' => $integrations ) ); + } + ); + + $store::use_setter( + 'general', + function ( $data ) { + if ( + ! isset( $data['integrations'] ) || + ! is_array( $data['integrations'] ) + ) { + return $data; + } + + $registry = array(); + foreach ( $data['integrations'] as $integration ) { + $registry[ $integration['name'] ] = + (bool) $integration['enabled']; + } + + self::update_registry( $registry ); + + unset( $data['integrations'] ); + return $data; + }, + 9 + ); + } + ); + + add_filter( + 'forms_bridge_load_templates', + static function ( $templates ) use ( $registry ) { + $integrations = array(); + foreach ( $registry as $integration => $enabled ) { + if ( $enabled ) { + $integrations[] = $integration; + } + } + + $woomode = + 1 === count( $integrations ) && 'woo' === $integrations[0]; + + $filtered_templates = array(); + foreach ( $templates as $template ) { + if ( ! isset( $template['integrations'] ) ) { + if ( $woomode ) { + continue; + } + + $filtered_templates[] = $template; + } elseif ( + count( + array_intersect( + $integrations, + $template['integrations'] + ) + ) + ) { + $filtered_templates[] = $template; + } + } + + return $filtered_templates; + }, + 5, + 1 + ); + } + + /** + * Alias to the singleton get_instance method. + * + * @param mixed[] ...$args Constructor arguments. + * + * @return Integration + */ + public static function setup( ...$args ) { + return static::get_instance( ...$args ); + } + + /** + * Handles the integration enabled state as a boolean value. + * + * @var boolean + */ + public $enabled = false; + + /** + * Integration constructor. + * + * @param mixed[] ...$args Array of constructor arguments. + */ + protected function construct( ...$args ) { + self::$integrations[ static::NAME ] = $this; + } + + /** + * Binds the integration to the WP hooks system. + */ + public function load() { + add_action( + 'init', + function () { + $this->init(); + } + ); + + // Gets available forms' data. + add_filter( + 'forms_bridge_forms', + function ( $forms, $integration = null ) { + if ( ! wp_is_numeric_array( $forms ) ) { + $forms = array(); + } + + if ( $integration && static::NAME !== $integration ) { + return $forms; + } + + $forms = array_merge( $forms, $this->forms() ); + return $forms; + }, + 9, + 2 + ); + + // Gets form data by context or by ID. + add_filter( + 'forms_bridge_form', + function ( $form, $form_id = null, $integration = null ) { + if ( is_array( $form ) && isset( $form['id'] ) && $form['id'] ) { + return $form; + } + + if ( $form_id ) { + if ( preg_match( '/^(\w+):(\d+)$/', $form_id, $matches ) ) { + [, $integration, $form_id] = $matches; + $form_id = (int) $form_id; + } elseif ( empty( $integration ) ) { + return $form; + } + } + + if ( $integration && static::NAME !== $integration ) { + return $form; + } + + if ( $form_id ) { + return $this->get_form_by_id( $form_id ); + } + + return $this->form(); + }, + 9, + 3 + ); + + // Gets current submission data. + add_filter( + 'forms_bridge_submission', + function ( $submission, $raw = false ) { + return $this->submission( $raw ) ?: $submission; + }, + 9, + 2 + ); + + add_filter( + 'forms_bridge_submission_id', + function ( $submission_id ) { + return $this->submission_id() ?: $submission_id; + }, + 9, + 1 + ); + + // Gets curent submission uploads. + add_filter( + 'forms_bridge_uploads', + function ( $uploads ) { + return $this->uploads() ?: $uploads; + }, + 9, + 1 + ); + + $this->enabled = true; + } + + /** + * Integration initializer to be fired on wp init. + */ + protected function init() {} + + /** + * Retrives the current form. + * + * @return array|null Form data. + */ + public function form() { + return null; + } + + /** + * Retrives form by ID. + * + * @param string $form_id Form ID. It could be prefixed or not. + * + * @return arra|nully Form data. + */ + public function get_form_by_id( $form_id ) { + return null; + } + + /** + * Retrives available forms. + * + * @return array Collection of form data. + */ + public function forms() { + return array(); + } + + /** + * Creates a form from a given template fields. + * + * @param array $data Form template data. + * + * @return int|null ID of the new form. + */ + public function create_form( $data ) { + return null; + } + + /** + * Removes a form by ID. + * + * @param integer $form_id Form ID. + * + * @return boolean Removal result. + */ + public function remove_form( $form_id ) { + return false; + } + + /** + * Retrives the current submission ID. + * + * @return string|null + */ + public function submission_id() { + return null; + } + + /** + * Retrives the current form submission. + * + * @param boolean $raw Control if the submission is serialized before exit. + * + * @return array|null Submission data. + */ + public function submission( $raw ) { + return null; + } + + /** + * Retrives the current submission uploaded files. + * + * @return array|null Collection of uploaded files. + */ + public function uploads() { + return null; + } + + /** + * Serializes form data. + * NOTE: To be overwritten. + * + * @param mixed $form Form representation. + * + * @return array The form serialized as array of data. + */ + public function serialize_form( $form ) { + return array(); + } + + /** + * Serializes the current form's submission data. + * NOTE: To be overwritten. + * + * @param mixed $submission Form submission representation. + * @param array $form_data Serialized form data. + * + * @return array Serialized form submission data. + */ + public function serialize_submission( $submission, $form_data ) { + return array(); + } +} diff --git a/forms-bridge/includes/class-job.php b/forms-bridge/includes/class-job.php new file mode 100644 index 00000000..e5638012 --- /dev/null +++ b/forms-bridge/includes/class-job.php @@ -0,0 +1,717 @@ + 'http://json-schema.org/draft-04/schema#', + 'title' => 'job', + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'title' => _x( 'Name', 'Job schema', 'forms-bridge' ), + 'description' => __( + 'Internal name of the job', + 'forms-bridge' + ), + 'type' => 'string', + 'minLength' => 1, + ), + 'title' => array( + 'title' => _x( 'Title', 'Job schema', 'forms-bridge' ), + 'description' => __( + 'Public title of the job', + 'forms-bridge' + ), + 'type' => 'string', + 'minLength' => 1, + ), + 'description' => array( + 'title' => _x( 'Description', 'Job schema', 'forms-bridge' ), + 'description' => __( + 'Short description of the job effects', + 'forms-bridge' + ), + 'type' => 'string', + 'default' => '', + ), + 'method' => array( + 'title' => _x( 'Method', 'Job schema', 'forms-bridge' ), + 'description' => __( + 'Name of the function with the job subroutine', + 'forms-bridge' + ), + 'type' => 'string', + 'minLength' => 1, + ), + 'input' => array( + 'title' => _x( 'Input', 'Job schema', 'forms-bridge' ), + 'description' => __( + 'Input fields interface schema of the job', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'type' => 'string', + 'minLength' => 1, + ), + 'required' => array( 'type' => 'boolean' ), + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'type' => array( + 'type' => 'string', + 'enum' => array( + 'string', + 'integer', + 'number', + 'array', + 'object', + 'boolean', + 'null', + ), + ), + 'items' => array( + 'type' => array( 'array', 'object' ), + 'additionalProperties' => true, + 'additionalItems' => true, + ), + 'properties' => array( + 'type' => 'object', + 'additionalProperties' => true, + ), + 'maxItems' => array( 'type' => 'integer' ), + 'minItems' => array( 'type' => 'integer' ), + 'additionalProperties' => array( + 'type' => 'boolean', + ), + 'additionalItems' => array( 'type' => 'boolean' ), + 'required' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'additionalItems' => true, + ), + ), + 'required' => array( 'type' ), + 'additionalProperties' => false, + 'default' => array( 'type' => 'string' ), + ), + ), + 'required' => array( 'name', 'schema' ), + 'additionalProperties' => false, + ), + 'default' => array(), + ), + 'output' => array( + 'title' => _x( 'Output', 'Job schema', 'forms-bridge' ), + 'description' => __( + 'Output fields interface schema of the job', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'name' => array( + 'type' => 'string', + 'minLength' => 1, + ), + 'requires' => array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + ), + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'type' => array( + 'type' => 'string', + 'enum' => array( + 'string', + 'integer', + 'number', + 'array', + 'object', + 'boolean', + 'null', + ), + ), + 'items' => array( + 'type' => array( 'array', 'object' ), + 'additionalProperties' => true, + 'additionalItems' => true, + ), + 'properties' => array( + 'type' => 'object', + 'additionalProperties' => true, + ), + 'maxItems' => array( 'type' => 'integer' ), + 'minItems' => array( 'type' => 'integer' ), + 'additionalProperties' => array( + 'type' => 'boolean', + ), + 'additionalItems' => array( 'type' => 'boolean' ), + ), + 'required' => array( 'type' ), + 'additionalProperties' => false, + 'default' => array( 'type' => 'string' ), + ), + ), + 'required' => array( 'name', 'schema' ), + 'additionalProperties' => false, + ), + 'default' => array(), + ), + 'snippet' => array( + 'title' => _x( 'Snippet', 'Job schema', 'forms-bridge' ), + 'description' => __( + 'PHP code representation of the job subroutine', + 'forms-bridge' + ), + 'type' => 'string', + ), + 'post_id' => array( 'type' => 'integer' ), + ), + 'additionalProperties' => false, + 'required' => array( + 'name', + 'title', + 'description', + 'input', + 'output', + 'method', + 'snippet', + ), + ); + } + + /** + * Enqueue the job instance as the last element of the workflow chain. + * + * @param string[] $workflow Array with job names. + * @param string $addon Job addon namespace. + * + * @return Job|null + */ + public static function from_workflow( $workflow, $addon ) { + $workflow_jobs = array(); + $jobs = FBAPI::get_addon_jobs( $addon ); + + $i = count( $workflow ) - 1; + while ( isset( $workflow[ $i ] ) ) { + $job_name = $workflow[ $i ]; + + foreach ( $jobs as $job ) { + if ( $job->name === $job_name ) { + $workflow_jobs[] = $job; + break; + } + } + + --$i; + } + + $next = null; + foreach ( $workflow_jobs as $job ) { + $job = clone $job; + $job->chain( $next ); + $next = $job; + } + + return $next; + } + + /** + * Returns the source code of the body of a function. + * + * @param string $method Function name. + * + * @return string Function body source code. + */ + private static function reflect_method( $method ) { + if ( ! function_exists( $method ) ) { + return ''; + } + + $reflection = new ReflectionFunction( $method ); + + $file = $reflection->getFileName(); + $from_line = $reflection->getStartLine(); + $to_line = $reflection->getEndLine(); + + $snippet = implode( + '', + array_slice( file( $file ), $from_line - 1, $to_line - $from_line + 1 ) + ); + + $_snippet = strstr( $snippet, '{' ); + if ( $_snippet ) { + $snippet = substr( $_snippet, 1 ); + } + + $i = strlen( $snippet ); + while ( true ) { + --$i; + + if ( '}' === $snippet[ $i ] || $i <= 0 ) { + break; + } + } + + $snippet = substr( $snippet, 0, $i ); + + $indentation = ''; + if ( preg_match( '/^\s+/', $snippet, $matches ) ) { + $indentation = preg_replace( '/(\n|\t)+/', '', $matches[0] ); + } + + $snippet = trim( $snippet ); + $snippet = preg_replace( '/return \$payload;$/', '', $snippet ); + + return $indentation . trim( $snippet ); + } + + /** + * Wraps a code snippet inside a function declaration and evaluate the code to register + * it on the process. The function name is based on the job and addon names. + * + * @param string $snippet Code snippet. + * @param string $name Name of the job. + * @param string $addon Name of the addon. + * + * @return string Method name. + */ + private static function load_snippet( $snippet, $name, $addon ) { + $id = $addon . '_' . $name; + + try { + $method_name = str_replace( '-', '_', "forms_bridge_job_{$id}" ); + + $method = + 'if (!function_exists(\'' . $method_name . '\')) {' . "\n"; + $method .= + 'function ' . $method_name . '($payload, $bridge) {' . "\n"; + $method .= $snippet . "\n"; + $method .= 'return $payload;' . "\n"; + $method .= "}\n}\n"; + + // phpcs:disable Squiz.PHP.Eval.Discouraged + eval( $method ); + return $method_name; + } catch ( ParseError $e ) { + Logger::log( "Syntax error on {$id} job snippet", Logger::ERROR ); + Logger::log( $e, Logger::ERROR ); + } catch ( Error $e ) { + Logger::log( "Error while loading {$id} job snippet", Logger::ERROR ); + Logger::log( $e, Logger::ERROR ); + } + } + + /** + * Gets the job config data from a post. + * + * @param WP_Post $post Post instance. + * + * @return array Job config data. + */ + private static function data_from_post( $post ) { + return array( + 'name' => $post->post_name, + 'title' => $post->post_title, + 'description' => $post->post_excerpt, + 'input' => + (array) ( get_post_meta( $post->ID, '_job-input', true ) ?: array() ), + 'output' => + (array) ( get_post_meta( $post->ID, '_job-output', true ) ?: array() ), + 'snippet' => $post->post_content, + 'post_id' => $post->ID, + ); + } + + /** + * Sets the job addon and name attributes, validates the data and enqueue themself to the job public + * filter getters. + * + * @param array $data Job data. + * @param string $addon Addon name. + */ + public function __construct( $data, $addon ) { + if ( $data instanceof WP_Post ) { + $data = self::data_from_post( $data ); + } + + $this->addon = $addon; + $this->data = $this->validate( $data ); + + if ( $this->is_valid ) { + $this->id = $addon . '-' . $data['name']; + } + } + + /** + * Magic method to proxy private attributes. + * + * @param string $name Attribute name. + * + * @return mixed Attribute value or null. + */ + public function __get( $name ) { + switch ( $name ) { + case 'id': + return $this->id; + case 'addon': + return $this->addon; + case 'next': + return $this->next; + case 'data': + return $this->data; + case 'is_valid': + return ! is_wp_error( $this->data ) && + Addon::addon( $this->addon ) !== null; + default: + if ( ! $this->is_valid ) { + return; + } + + return $this->data[ $name ] ?? null; + } + } + + /** + * Sets the next job on the chain. + * + * @param Job $job Job instance to be queued as the next item of a workflow chain. + */ + public function chain( $job ) { + $this->next = $job; + } + + /** + * Gets the payload from the previous workflow stage and runs the job against it. + * + * @param array $payload Payload data. + * @param Form_Bridge $bridge Workflow's bridge owner instance. + * @param array $mutations Bridge's mutations. + * + * @return array|null Payload after job. + */ + public function run( $payload, $bridge, $mutations = null ) { + $original = $payload; + + if ( null === $mutations ) { + $mutations = array_slice( $bridge->mutations, 1 ); + } + + if ( $this->missing_requireds( $payload ) ) { + $next_job = $this->next; + if ( $next_job ) { + $mutations = array_slice( $mutations, 1 ); + $payload = $next_job->run( $payload, $bridge, $mutations ); + } + + return $payload; + } + + $method = $this->method; + $payload = $method( $payload, $bridge, $this ); + + if ( empty( $payload ) ) { + return; + } elseif ( is_wp_error( $payload ) ) { + $error = $payload; + do_action( 'forms_bridge_on_failure', $bridge, $error, $original ); + return; + } + + $payload = $this->output_payload( $payload ); + + $mutation = array_shift( $mutations ) ?: array(); + $payload = $bridge->apply_mutation( $payload, $mutation ); + + $next_job = $this->next; + if ( $next_job ) { + $payload = $next_job->run( $payload, $bridge, $mutations ); + } + + return $payload; + } + + /** + * Job data serializer to be used on REST API response. + * + * @return array + */ + public function data() { + if ( ! $this->is_valid ) { + return; + } + + return array_merge( + array( + 'id' => $this->id, + 'addon' => $this->addon, + ), + $this->data + ); + } + + /** + * Gets the job's post ID from database. + * + * @return int|null Int if it's stored on the database, null otherwise. + */ + private function get_post_id() { + // phpcs:disable WordPress.DB.SlowDBQuery + $ids = get_posts( + array( + 'post_type' => self::TYPE, + 'name' => $this->name, + 'meta_key' => '_fb-addon', + 'meta_value' => $this->addon, + 'fields' => 'ids', + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'update_menu_item_cache' => false, + ) + ); + // phpcs:enable + + if ( count( $ids ) ) { + return $ids[0]; + } + } + + /** + * Save the job config to the database as a custom post entry. + * + * @return int|WP_Error Post ID or WP_Error in case of insert errors. + */ + public function save() { + if ( ! $this->is_valid ) { + return $this->data; + } + + $post_arr = array( + 'post_type' => self::TYPE, + 'post_name' => $this->name, + 'post_title' => $this->title, + 'post_excerpt' => $this->description, + 'post_content' => $this->snippet, + 'post_status' => 'publish', + ); + + $post_id = $this->get_post_id(); + if ( $post_id ) { + $post_arr['ID'] = $post_id; + $post_id = wp_update_post( $post_arr, true ); + } else { + $post_id = wp_insert_post( $post_arr, true ); + } + + if ( ! is_wp_error( $post_id ) ) { + update_post_meta( $post_id, '_fb-addon', $this->addon ); + update_post_meta( $post_id, '_job-input', $this->input ); + update_post_meta( $post_id, '_job-output', $this->output ); + } + + return $post_id; + } + + /** + * Delete the job config from the database and restore its default configuration. + * If the job does not exists a file-based configuration, the job will be deleted. + * + * @return bool + */ + public function reset() { + $post_id = $this->get_post_id(); + + if ( ! $post_id ) { + return false; + } + + return wp_delete_post( $post_id, true ) instanceof WP_Post; + } + + /** + * Vaildates the data against the job schema. + * + * @param array $data Job data. + * + * @return array|WP_Error Validation result. + */ + private function validate( $data ) { + $schema = self::schema(); + + if ( + isset( $data['name'], $data['snippet'] ) && + is_string( $data['snippet'] ) + ) { + $data['method'] = self::load_snippet( + $data['snippet'], + $data['name'], + $this->addon + ); + } elseif ( isset( $data['method'] ) && function_exists( $data['method'] ) ) { + $data['snippet'] = self::reflect_method( $data['method'] ); + } else { + $data['method'] = array( '\FORMS_BRIDGE\Job', 'noop' ); + $data['snippet'] = ''; + } + + $data = wpct_plugin_sanitize_with_schema( $data, $schema ); + if ( is_wp_error( $data ) ) { + return $data; + } + + if ( ! function_exists( $data['method'] ) ) { + return new WP_Error( + 'method_is_not_function', + __( 'Job method is not a function', 'forms-bridge' ), + $data + ); + } + + return $data; + } + + /** + * Checks if payload compains with the required fields of the job. + * + * @param array $payload Input payload of the job. + * + * @return boolean + */ + private function missing_requireds( $payload ) { + $requireds = array_filter( + $this->input, + function ( $input_field ) { + return $input_field['required'] ?? false; + } + ); + + foreach ( $requireds as $required ) { + if ( ! isset( $payload[ $required['name'] ] ) ) { + return true; + } + } + + return false; + } + + /** + * Removes attributes from the payload that are not present on the job output config. + * + * @param array $payload Job result payload. + * + * @return array Filtered payload. + */ + private function output_payload( $payload ) { + $input_fields = array_column( $this->input, 'name' ); + + foreach ( $input_fields as $input_field ) { + foreach ( $this->output as $output_field ) { + if ( $input_field === $output_field['name'] ) { + if ( is_array( $output_field['requires'] ?? null ) ) { + $requires = array_filter( + $output_field['requires'], + function ( $name ) use ( $input_fields ) { + return false === array_search( $name, $input_fields, true ); + } + ); + + if ( count( $requires ) ) { + break; + } + } + + $persist = true; + break; + } + } + + if ( ! isset( $persist ) ) { + unset( $payload[ $input_field ] ); + } + } + + return $payload; + } +} diff --git a/forms-bridge/includes/class-json-finger.php b/forms-bridge/includes/class-json-finger.php new file mode 100644 index 00000000..412e8402 --- /dev/null +++ b/forms-bridge/includes/class-json-finger.php @@ -0,0 +1,578 @@ + $i ) { + if ( '.' !== $pointer[ $i + 1 ] && '[' !== $pointer[ $i + 1 ] ) { + self::$cache[ $pointer ] = array(); + return array(); + } + } + } else { + $key .= $char; + } + } + + if ( $key ) { + $keys[] = $key; + } + + self::$cache[ $pointer ] = $keys; + return $keys; + } + + /** + * Sanitize a key to be a valid finger key. + * + * @param string|int $key Finger key value. + * + * @return string Sanitized key value. + */ + public static function sanitize_key( $key ) { + if ( INF === $key ) { + $key = '[]'; + } elseif ( intval( $key ) == $key ) { + $key = "[{$key}]"; + } else { + $key = trim( $key ); + + if ( + preg_match( '/( |\.|")/', $key ) && + ! preg_match( '/^\["[^"]+"\]$/', $key ) + ) { + $key = "[\"{$key}\"]"; + } + } + + return $key; + } + + /** + * Validates the finger pointer. + * + * @param string $pointer Finger pointer. + * + * @return boolean Validation result. + */ + public static function validate( $pointer ) { + $pointer = (string) $pointer; + + if ( ! strlen( $pointer ) ) { + return false; + } + + return count( self::parse( $pointer ) ) > 0; + } + + /** + * Returns a finger pointer from an array of keys after keys validation and sanitization. + * + * @param array $keys Array with finger keys. + * @param bool $is_conditional Indicates if the output should be prefixed with the '?' mark. + * + * @return string Finger pointer result. + */ + public static function pointer( $keys, $is_conditional = false ) { + if ( ! is_array( $keys ) ) { + return ''; + } + + $pointer = array_reduce( + $keys, + static function ( $pointer, $key ) { + if ( INF === $key ) { + $key = '[]'; + } elseif ( intval( $key ) == $key ) { + $key = "[{$key}]"; + } else { + $key = self::sanitize_key( $key ); + + if ( '[' !== $key[0] && strlen( $pointer ) > 0 ) { + $key = '.' . $key; + } + } + + return $pointer . $key; + }, + '' + ); + + if ( $is_conditional ) { + $pointer = '?' . $pointer; + } + + return $pointer; + } + + /** + * Binds data to the handler instance. + * + * @param array $data Target data. + * + * @throws TypeError In case $data param is not an array. + */ + public function __construct( $data ) { + if ( ! is_array( $data ) ) { + throw new TypeError( 'Input data isn\'t an array' ); + } + + $this->data = $data; + } + + /** + * Proxy handler attributes to the data. + * + * @param string $name Attribute name. + * + * @return mixed Attribute value or null. + */ + public function __get( $name ) { + if ( isset( $this->data[ $name ] ) ) { + return $this->data[ $name ]; + } + } + + /** + * Proxy handler attribute updates to the data. + * + * @param string $name Attribute name. + * @param mixed $value Attribute value. + */ + public function __set( $name, $value ) { + $this->data[ $name ] = $value; + } + + /** + * Returns de current data. + * + * @return array Current data. + */ + public function data() { + return $this->data; + } + + /** + * Gets the attribute from the data. + * + * @param string $pointer JSON finger pointer. + * @param array $expansion In case pointer needs expansion, this handles an flat array + * with the expansion values. + * + * @return mixed Attribute value. + */ + public function get( $pointer, &$expansion = array() ) { + $pointer = (string) $pointer; + + if ( ! $pointer ) { + return $this->data; + } + + if ( isset( $this->data[ $pointer ] ) ) { + return $this->data[ $pointer ]; + } + + if ( strstr( $pointer, '[]' ) !== false ) { + return $this->get_expanded( $pointer, $expansion ); + } + + $value = null; + try { + $keys = self::parse( $pointer ); + + $value = $this->data; + foreach ( $keys as $key ) { + if ( ! isset( $value[ $key ] ) ) { + return; + } + + $value = $value[ $key ]; + } + } catch ( Error ) { + return; + } + + $expansion[] = $value; + return $value; + } + + /** + * Gets values from an expanded finger pointer. + * + * @param string $pointer Finger pointer. + * @param array $expansion Handle for the expansion's flat array of values. + * + * @return array Hierarchical structure of values result of the expansion. + */ + private function get_expanded( $pointer, &$expansion = array() ) { + $flat = preg_match( '/\[\]$/', $pointer ); + + $parts = explode( '[]', $pointer ); + $before = $parts[0]; + + $after = array_slice( $parts, 1 ); + if ( count( $after ) && ! $after[ count( $after ) - 1 ] ) { + array_pop( $after ); + } + $after = implode( '[]', $after ); + + if ( ! $before ) { + if ( ! wp_is_numeric_array( $this->data ) ) { + return array(); + } + + $items = $this->data; + } else { + $items = $this->get( $before ); + } + + if ( empty( $after ) || ! wp_is_numeric_array( $items ) ) { + return $items; + } + + $l = count( $items ); + for ( $i = 0; $i < $l; $i++ ) { + $pointer = "{$before}[$i]{$after}"; + $items[ $i ] = $this->get( $pointer, $expansion ); + } + + if ( $flat ) { + return $expansion; + } + + return $items; + } + + /** + * Sets the attribute value on the data. + * + * @param string $pointer JSON finger pointer. + * @param mixed $value Attribute value. + * @param boolean $unset If true, unsets the attribute. + * + * @return array Updated data. + */ + public function set( $pointer, $value, $unset = false ) { + $pointer = (string) $pointer; + + if ( ! $pointer ) { + return $this->data; + } + + if ( $this->$pointer ) { + $this->$pointer = $value; + return $this->data; + } + + if ( strstr( $pointer, '[]' ) !== false ) { + return $this->set_expanded( $pointer, $value, $unset ); + } + + $data = $this->data; + $breadcrumb = array(); + + try { + $keys = self::parse( $pointer ); + if ( count( $keys ) === 1 ) { + if ( $unset ) { + unset( $data[ $keys[0] ] ); + } else { + $data[ $keys[0] ] = $value; + } + + $this->data = $data; + return $data; + } + + $partial = &$data; + + $l = count( $keys ) - 1; + for ( $i = 0; $i < $l; $i++ ) { + if ( ! is_array( $partial ) ) { + return $data; + } + + $key = $keys[ $i ]; + if ( intval( $key ) == $key ) { + if ( ! wp_is_numeric_array( $partial ) ) { + return $data; + } + + $key = intval( $key ); + } + + if ( ! isset( $partial[ $key ] ) ) { + $partial[ $key ] = array(); + } + + $breadcrumb[] = array( + 'partial' => &$partial, + 'key' => $key, + ); + $partial = &$partial[ $key ]; + } + + $key = array_pop( $keys ); + if ( $unset ) { + if ( wp_is_numeric_array( $partial ) ) { + array_splice( $partial, $key, 1 ); + } elseif ( is_array( $partial ) ) { + unset( $partial[ $key ] ); + } + + for ( $i = count( $breadcrumb ) - 1; $i >= 0; $i-- ) { + $step = &$breadcrumb[ $i ]; + $partial = &$step['partial']; + $key = $step['key']; + + if ( ! empty( $partial[ $key ] ) ) { + break; + } + + if ( wp_is_numeric_array( $partial ) ) { + array_splice( $partial, $key, 1 ); + } else { + unset( $partial[ $key ] ); + } + } + } else { + $partial[ $key ] = $value; + } + } catch ( Error $e ) { + error_log( $e->getMessage() ); + return $this->data; + } + + $this->data = $data; + return $data; + } + + /** + * Sets values based on the expansion of the finger pointer. + * + * @param string $pointer Finger pointer. + * @param array $values Array of values. + * @param boolean $unset If true, unsets the attributes. + * + * @return array + */ + private function set_expanded( $pointer, $values, $unset ) { + $parts = explode( '[]', $pointer ); + $before = $parts[0]; + $after = array_slice( $parts, 1 ); + + if ( empty( $after[ count( $after ) - 1 ] ) ) { + array_pop( $after ); + } + + $after = implode( '[]', $after ); + + $from = $this->get( $before ); + + if ( $unset ) { + $values = $from; + } + + $is_numeric_array = wp_is_numeric_array( $values ); + + if ( ! wp_is_numeric_array( $from ) && $is_numeric_array ) { + $from = array(); + $this->set( $before, $from ); + } + + if ( ! $is_numeric_array && ! $unset ) { + $value = $values; + $values = array(); + + $l = count( $from ); + for ( $i = 0; $i < $l; $i++ ) { + $values[] = $value; + } + } + + $l = count( $values ) - 1; + for ( $i = $l; $i >= 0; $i-- ) { + $pointer = "{$before}[{$i}]{$after}"; + + if ( $unset ) { + $this->unset( $pointer ); + } else { + $this->set( $pointer, $values[ $i ] ); + } + } + + $values = $this->get( $before ); + + if ( wp_is_numeric_array( $values ) ) { + ksort( $values ); + $this->set( $before, $values ); + } + + return $this->data; + } + + /** + * Unsets the attribute from the data. + * + * @param string $pointer JSON finger pointer. + */ + public function unset( $pointer ) { + if ( isset( $this->data[ $pointer ] ) ) { + if ( intval( $pointer ) == $pointer ) { + if ( wp_is_numeric_array( $this->data ) ) { + array_splice( $this->data, $pointer, 1 ); + } + } else { + unset( $this->data[ $pointer ] ); + } + + return $this->data; + } + + return $this->set( $pointer, null, true ); + } + + /** + * Checks if the json finger is set on the data. + * + * @param string $pointer JSON finger pointer. + * @param boolean &$is_conditional Reference to handle if the pointer is + * conditional. + * + * @return boolean True if attribute is set. + */ + public function isset( $pointer, &$is_conditional = false ) { + $keys = self::parse( $pointer, $is_conditional ); + + switch ( count( $keys ) ) { + case 0: + return false; + case 1: + $key = $keys[0]; + return isset( $this->data[ $key ] ); + default: + $key = array_pop( $keys ); + $pointer = self::pointer( $keys ); + $parent = $this->get( $pointer ); + + if ( strstr( $pointer, '[]' ) === false ) { + if ( INF === $key && is_array( $parent ) ) { + return true; + } + + return isset( $parent[ $key ] ); + } + + if ( ! wp_is_numeric_array( $parent ) ) { + return false; + } + + if ( INF === $key ) { + return true; + } + + foreach ( $parent as $item ) { + if ( isset( $item[ $key ] ) ) { + return true; + } + } + + return false; + } + } +} diff --git a/forms-bridge/includes/class-logger.php b/forms-bridge/includes/class-logger.php new file mode 100644 index 00000000..9fda27d7 --- /dev/null +++ b/forms-bridge/includes/class-logger.php @@ -0,0 +1,303 @@ + 0 && $lines >= 0 ) { + $seek = min( ftell( $socket ), $buffer ); + + fseek( $socket, -$seek, SEEK_CUR ); + + $chunk = fread( $socket, $seek ); + $output = $chunk . $output; + + fseek( $socket, -mb_strlen( $chunk, '8bit' ), SEEK_CUR ); + + $lines -= substr_count( $chunk, "\n" ); + } + + while ( $lines++ < 0 ) { + $output = substr( $output, strpos( $output, "\n" ) + 1 ); + } + + fclose( $socket ); + + $output = trim( $output ); + return (array) preg_split( '/(\n|\r)+/', $output ); + } + + /** + * Write log lines to the log file if debug mode is active. + * + * @param mixed $data Log line data. + * @param string $level Log level, DEBUG as default. + */ + public static function log( $data, $level = 'DEBUG' ) { + if ( ! self::is_active() ) { + return; + } + + if ( ! in_array( $level, array( 'DEBUG', 'ERROR', 'INFO' ), true ) ) { + $level = 'DEBUG'; + } + + if ( is_object( $data ) ) { + $data = (array) $data; + } + + if ( is_array( $data ) ) { + $data = json_encode( + $data, + JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE + ); + } + + // phpcs:disable WordPress.PHP.DevelopmentFunctions + $message = print_r( $data, true ); + $line = sprintf( "[%s] %s\n", $level, $message ); + // phpcs:enable WordPress.PHP.DevelopmentFunctions + + $socket = fopen( self::log_path(), 'a+' ); + fwrite( $socket, $line, strlen( $line ) ); + fclose( $socket ); + } + + /** + * Check if the debug mode is active. + * + * @return boolean + */ + public static function is_active() { + $log_path = self::log_path(); + return is_file( $log_path ); + } + + /** + * Debug mode activator. + */ + public static function activate() { + if ( ! self::is_active() ) { + $log_path = self::log_path(); + if ( ! is_file( $log_path ) ) { + touch( $log_path ); + } + } + } + + /** + * Debug mode deactivator. + */ + public static function deactivate() { + if ( self::is_active() ) { + $log_path = self::log_path(); + if ( is_file( $log_path ) ) { + wp_delete_file( $log_path ); + } + } + } + + /** + * Logger's setup method. Initializes php log configurations. + * + * @return Logger + */ + public static function setup() { + if ( self::is_active() ) { + // phpcs:disable WordPress.PHP.IniSet + // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions + // phpcs:disable WordPress.PHP.DevelopmentFunctions + error_reporting( E_ALL ); + // phpcs:enable WordPress.PHP.DevelopmentFunctions + // phpcs:enable WordPress.PHP.DiscouragedPHPFunctions + ini_set( 'log_errors', 1 ); + ini_set( 'display_errors', 0 ); + ini_set( 'error_log', self::log_path() ); + // phpcs:enable WordPress.PHP.IniSet + } + + return self::get_instance(); + } + + /** + * Logger singleton constructor. Binds the logger to wp and custom hooks + * + * @param mixed[] ...$args Array of constructor arguments. + */ + protected function construct( ...$args ) { + add_action( + 'rest_api_init', + static function () { + self::register_log_route(); + }, + 10, + 0 + ); + + Settings_Store::ready( + function ( $store ) { + $store::use_getter( + 'general', + static function ( $data ) { + $data['debug'] = self::is_active(); + return $data; + } + ); + + $store::use_setter( + 'general', + static function ( $data ) { + if ( ! isset( $data['debug'] ) ) { + return $data; + } + + if ( true === $data['debug'] ) { + self::activate(); + } else { + self::deactivate(); + } + + unset( $data['debug'] ); + return $data; + }, + 9 + ); + } + ); + } + + /** + * Registers the logger REST API route. + */ + private static function register_log_route() { + register_rest_route( + 'forms-bridge/v1', + '/logs/', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => static function () { + $lines = isset( $_GET['lines'] ) ? intval( $_GET['lines'] ) : 500; + $logs = self::logs( $lines ); + + if ( empty( $logs ) ) { + return array(); + } + + return $logs; + }, + 'permission_callback' => static function () { + return self::permission_callback(); + }, + ) + ); + } + + /** + * REST API route's permission callback. + * + * @return boolean|WP_Error + */ + private static function permission_callback() { + return current_user_can( 'manage_options' ) ?: + new WP_Error( + 'rest_unauthorized', + 'You can\'t manage wp options', + array( + 'status' => 403, + ) + ); + } +} + +Logger::setup(); diff --git a/forms-bridge/includes/class-menu.php b/forms-bridge/includes/class-menu.php new file mode 100644 index 00000000..97d6cc35 --- /dev/null +++ b/forms-bridge/includes/class-menu.php @@ -0,0 +1,30 @@ +%s', + esc_html__( 'Loading', 'forms-bridge' ) + ); + } +} diff --git a/forms-bridge/includes/class-rest-settings-controller.php b/forms-bridge/includes/class-rest-settings-controller.php new file mode 100644 index 00000000..07688389 --- /dev/null +++ b/forms-bridge/includes/class-rest-settings-controller.php @@ -0,0 +1,828 @@ + WP_REST_Server::READABLE, + 'callback' => static function () { + return self::forms(); + }, + 'permission_callback' => array( self::class, 'permission_callback' ), + ) + ); + } + + /** + * Registers json schemas REST API routes. + */ + private static function register_schema_route() { + foreach ( Addon::addons() as $addon ) { + if ( ! $addon->enabled ) { + continue; + } + + $addon = $addon::NAME; + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/schemas", + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => static function () use ( $addon ) { + return self::addon_schemas( $addon ); + }, + 'permission_callback' => array( self::class, 'permission_callback' ), + ) + ); + } + + register_rest_route( + 'forms-bridge/v1', + '/http/schemas', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => static function () { + return self::http_schemas(); + }, + 'permission_callback' => array( self::class, 'permission_callback' ), + ) + ); + } + + /** + * Registers templates REST API routes. + */ + private static function register_template_routes() { + foreach ( Addon::addons() as $addon ) { + if ( ! $addon->enabled ) { + continue; + } + + $addon = $addon::NAME; + + $schema = Form_Bridge_Template::schema( $addon ); + $args = array(); + + foreach ( $schema['properties'] as $name => $prop_schema ) { + $args[ $name ] = $prop_schema; + $args[ $name ]['required'] = in_array( + $name, + $schema['required'], + true + ); + } + + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/templates/(?P[a-zA-Z0-9-_]+)", + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::get_template( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'name' => $args['name'], + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::save_template( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => $args, + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::reset_template( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'name' => $args['name'], + ), + ), + ) + ); + + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/templates/(?P[a-zA-Z0-9-_]+)/use", + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::use_template( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'name' => $args['name'], + 'integration' => array( + 'description' => __( + 'Target integration', + 'forms-bridge' + ), + 'type' => 'string', + 'required' => true, + ), + 'fields' => $args['fields'], + ), + ) + ); + + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/templates/(?P[a-zA-Z0-9-_]+)/options", + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::get_template_options( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'name' => $args['name'], + 'backend' => FBAPI::get_backend_schema(), + 'credential' => FBAPI::get_credential_schema(), + ), + ) + ); + } + } + + /** + * Registers jobs REST API routes. + */ + private static function register_job_routes() { + foreach ( Addon::addons() as $addon ) { + if ( ! $addon->enabled ) { + continue; + } + + $addon = $addon::NAME; + + $schema = Job::schema(); + $args = array(); + + foreach ( $schema['properties'] as $name => $prop_schema ) { + $args[ $name ] = $prop_schema; + $args[ $name ]['required'] = in_array( + $name, + $schema['required'], + true + ); + } + + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/jobs/workflow", + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::get_jobs( $addon, $request ); + }, + 'permission_callback' => array( self::class, 'permission_callback' ), + 'args' => array( + 'jobs' => array( + 'description' => __( + 'Array of job names', + 'forms-bridge' + ), + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'uniqueItems' => true, + 'minItems' => 1, + 'required' => true, + ), + ), + ) + ); + + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/jobs/(?P[a-zA-Z0-9-_]+)", + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::get_job( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'name' => $args['name'], + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::save_job( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => $args, + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::reset_job( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'name' => $args['name'], + ), + ), + ) + ); + } + } + + /** + * Registers http backends REST API routes. + */ + private static function register_backend_routes() { + foreach ( Addon::addons() as $addon ) { + if ( ! $addon->enabled ) { + continue; + } + + $addon = $addon::NAME; + + // $schema = Form_Bridge_Template::schema($addon); + // $args = []; + + // foreach ($schema['properties'] as $name => $prop_schema) { + // $args[$name] = $prop_schema; + // $args[$name]['required'] = in_array( + // $name, + // $schema['required'], + // true + // ); + // } + + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/backend/ping", + array( + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::ping_backend( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'backend' => FBAPI::get_backend_schema(), + 'credential' => FBAPI::get_credential_schema(), + ), + ), + ) + ); + + register_rest_route( + 'forms-bridge/v1', + "/{$addon}/backend/endpoint/schema", + array( + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => static function ( $request ) use ( $addon ) { + return self::get_endpoint_schema( $addon, $request ); + }, + 'permission_callback' => array( + self::class, + 'permission_callback', + ), + 'args' => array( + 'backend' => FBAPI::get_backend_schema(), + 'endpoint' => array( + 'description' => __( + 'Target endpoint name', + 'forms-bridge' + ), + 'type' => 'string', + 'required' => true, + ), + ), + ), + ) + ); + } + } + + /** + * Callback for GET requests to the forms endpoint. + * + * @return array + */ + private static function forms() { + $forms = FBAPI::get_forms(); + return array_map( + static function ( $form ) { + unset( $form['bridges'] ); + return $form; + }, + $forms + ); + } + + /** + * Callback for GET requests to the job endpoint. Retrive a job from + * the database. + * + * @param string $addon Addon name. + * @param WP_REST_Request $request Current REST request instance. + * + * @return array|WP_Error + */ + private static function get_job( $addon, $request ) { + $job = FBAPI::get_job( $request['name'], $addon ); + if ( empty( $job ) ) { + return self::not_found(); + } + + return $job->data(); + } + + /** + * Callback for POST requests to the job endpoint. Inserts a new job + * on the database. + * + * @param string $addon Addon name. + * @param WP_REST_Request $request Current REST request instance. + * + * @return array|WP_Error + */ + private static function save_job( $addon, $request ) { + $data = $request->get_json_params(); + $data['name'] = $request['name']; + + $post_id = FBAPI::save_job( $data, $addon ); + if ( ! $post_id ) { + return self::bad_request(); + } + + $post = get_post( $post_id ); + return ( new Job( $post, $addon ) )->data(); + } + + /** + * Callback for DELETE requests to the job endpoint. Removes a job + * from the database. + * + * @param string $addon Addon name. + * @param WP_REST_Request $request Current REST request instance. + * + * @return array|WP_Error + */ + private static function reset_job( $addon, $request ) { + $job = FBAPI::get_job( $request['name'], $addon ); + + if ( ! $job ) { + return self::not_found(); + } + + $reset = $job->reset(); + if ( ! $reset ) { + return $job->data(); + } + + $job = FBAPI::get_job( $request['name'], $addon ); + if ( $job ) { + return $job->data(); + } + + return array(); + } + + /** + * Callback for GET requests to the jobs endpoint. Retrives the list + * of available addon jobs. + * + * @param string $addon Addon name. + * @param WP_REST_Request $request Current REST request instance. + * + * @return array|WP_Error Jobs data. + */ + private static function get_jobs( $addon, $request ) { + $jobs = array(); + foreach ( FBAPI::get_addon_jobs( $addon ) as $job ) { + if ( in_array( $job->name, $request['jobs'], true ) ) { + $jobs[] = $job->data(); + } + } + + if ( count( $jobs ) !== count( $request['jobs'] ) ) { + $backup = $jobs; + + $jobs = array(); + $index = 0; + foreach ( $request['jobs'] as $name ) { + foreach ( $backup as $job ) { + if ( $job['name'] === $name ) { + $jobs[] = $job; + break; + } + } + + if ( ! isset( $jobs[ $index ] ) || $jobs[ $index ]['name'] !== $name ) { + $jobs[] = array( + 'addon' => $addon, + 'id' => $addon . '-' . $name, + 'name' => $name, + 'title' => '', + 'description' => '', + 'method' => '', + 'input' => array(), + 'output' => array(), + ); + } + + ++$index; + } + } + + return $jobs; + } + + /** + * Callback for GET requests to the templates endpoint. + * + * @param string $addon Addon name. + * @param WP_REST_Request $request Current REST request instance. + * + * @return array|WP_Error Template data. + */ + private static function get_template( $addon, $request ) { + $template = FBAPI::get_template( $request['name'], $addon ); + if ( empty( $template ) ) { + return self::not_found( + __( 'Template is unknown', 'forms-bridge' ), + array( + 'name' => $request['name'], + 'addon' => $addon, + ) + ); + } + + return $template->data(); + } + + /** + * Callback for POST requests to the template endpoint. Inserts a new template + * in the database. + * + * @param string $addon Addon name. + * @param WP_REST_Request $request Current REST request instance. + * + * @return array|WP_Error + */ + private static function save_template( $addon, $request ) { + $data = $request->get_json_params(); + $data['name'] = $request['name']; + + $result = FBAPI::save_template( $data, $addon ); + if ( is_wp_error( $result ) ) { + return $result; + } + + return array( 'success' => true ); + } + + /** + * Callback for DELETE requests to the template endpoint. Removes a template + * from the database. + * + * @param string $addon Addon name. + * @param string $name Template name. + * + * @return array|WP_Error + */ + private static function reset_template( $addon, $name ) { + $template = FBAPI::get_template( $name, $addon ); + + if ( ! $template ) { + return self::not_found(); + } + + $result = $template->reset(); + + if ( ! $result ) { + return self::internal_server_error(); + } + + $template = FBAPI::get_template( $name, $addon ); + if ( $template ) { + return $template->data(); + } + } + + /** + * Callback for POST requests to the templates endpoint. + * + * @param string $addon Name of the owner addon of the template. + * @param REST_Request $request CUrrent REST request instance. + * + * @return array|WP_Error Template use result. + */ + private static function use_template( $addon, $request ) { + $name = $request['name']; + $fields = $request['fields']; + $integration = $request['integration']; + + $template = FBAPI::get_template( $name, $addon ); + if ( empty( $template ) ) { + return self::not_found(); + } + + if ( ! in_array( $integration, $template->integrations, true ) ) { + return self::bad_request(); + } + + $result = $template->use( $fields, $integration ); + + if ( is_wp_error( $result ) ) { + return $result; + } + + return array( 'success' => true === $result ); + } + + private static function get_template_options( $addon, $request ) { + $handler = self::prepare_addon_backend_request_handler( + $addon, + $request + ); + + if ( is_wp_error( $handler ) ) { + return $handler; + } + + [$addon, $backend] = $handler; + + $template = FBAPI::get_template( $request['name'], $addon::NAME ); + if ( ! $template ) { + return self::not_found(); + } + + if ( ! $template->is_valid ) { + return self::bad_request(); + } + + $field_options = array(); + $fields = $template->fields; + foreach ( $fields as $field ) { + $endpoint = $field['options']['endpoint'] ?? null; + if ( $endpoint ) { + if ( is_string( $field['options']['finger'] ) ) { + $finger = array( + 'value' => $field['options']['finger'], + ); + } else { + $finger = $field['options']['finger']; + } + + $value_pointer = $finger['value']; + + if ( ! JSON_Finger::validate( $value_pointer ) ) { + return self::internal_server_error(); + } + + $label_pointer = $finger['label'] ?? $finger['value']; + + if ( ! JSON_Finger::validate( $label_pointer ) ) { + return self::internal_server_error(); + } + + $response = $addon->fetch( $endpoint, $backend ); + + if ( is_wp_error( $response ) ) { + $error = self::internal_server_error(); + $error->add( + $response->get_error_code(), + $response->get_error_message(), + $response->get_error_data() + ); + + return $error; + } + + $options = array(); + $data = $response['data']; + + $json_finger = new JSON_Finger( $data ); + + $values = $json_finger->get( $value_pointer ); + + if ( ! wp_is_numeric_array( $values ) ) { + return self::internal_server_error(); + } + + foreach ( $values as $value ) { + $options[] = array( + 'value' => $value, + 'label' => $value, + ); + } + + $labels = $json_finger->get( $label_pointer ); + if ( + wp_is_numeric_array( $labels ) && + count( $labels ) === count( $values ) + ) { + $l = count( $labels ); + for ( $i = 0; $i < $l; $i++ ) { + $options[ $i ]['label'] = $labels[ $i ]; + } + } + + $field_options[] = array( + 'ref' => $field['ref'], + 'name' => $field['name'], + 'options' => $options, + ); + } + } + + return $field_options; + } + + /** + * Performs a request validation and sanitization + * + * @param string $addon Target addon name. + * @param WP_REST_Request $request Request instance. + * + * @return [Addon, string, string|null]|WP_Error + */ + private static function prepare_addon_backend_request_handler( + $addon, + $request + ) { + $backend = wpct_plugin_sanitize_with_schema( + $request['backend'], + FBAPI::get_backend_schema() + ); + + if ( is_wp_error( $backend ) ) { + return self::bad_request(); + } + + $credential = $request['credential']; + if ( ! empty( $credential ) ) { + $credential = wpct_plugin_sanitize_with_schema( + $credential, + FBAPI::get_credential_schema( $addon ) + ); + + if ( is_wp_error( $credential ) ) { + return self::bad_request(); + } + + $backend['credential'] = $credential['name']; + } + + $addon = FBAPI::get_addon( $addon ); + if ( ! $addon ) { + return self::bad_request(); + } + + Backend::temp_registration( $backend ); + Credential::temp_registration( $credential ); + + return array( $addon, $backend['name'], $credential['name'] ?? null ); + } + + private static function ping_backend( $addon, $request ) { + $handler = self::prepare_addon_backend_request_handler( + $addon, + $request + ); + + if ( is_wp_error( $handler ) ) { + return $handler; + } + + [$addon, $backend] = $handler; + + $result = $addon->ping( $backend ); + + if ( is_wp_error( $result ) ) { + $error = self::bad_request(); + $error->add( + $result->get_error_code(), + $result->get_error_message(), + $result->get_error_data() + ); + + return $error; + } + + return array( 'success' => $result ); + } + + private static function get_endpoint_schema( $addon, $request ) { + $handler = self::prepare_addon_backend_request_handler( + $addon, + $request + ); + if ( is_wp_error( $handler ) ) { + return $handler; + } + + [$addon, $backend] = $handler; + + $schema = $addon->get_endpoint_schema( $request['endpoint'], $backend ); + + if ( is_wp_error( $schema ) ) { + $error = self::internal_server_error(); + $error->add( + $schema->get_error_code(), + $schema->get_error_message(), + $schema->get_error_data() + ); + + return $error; + } + + return $schema; + } + + private static function addon_schemas( $name ) { + $bridge = FBAPI::get_bridge_schema( $name ); + return array( 'bridge' => $bridge ); + } + + private static function http_schemas() { + $backend = FBAPI::get_backend_schema(); + $credential = FBAPI::get_credential_schema(); + return array( + 'backend' => $backend, + 'credential' => $credential, + ); + } +} diff --git a/forms-bridge/includes/class-settings-store.php b/forms-bridge/includes/class-settings-store.php new file mode 100644 index 00000000..36f121d6 --- /dev/null +++ b/forms-bridge/includes/class-settings-store.php @@ -0,0 +1,53 @@ + 'general', + 'properties' => array( + 'notification_receiver' => array( + 'type' => 'string', + 'format' => 'email', + 'default' => $admin_email, + ), + ), + 'required' => array( 'notification_receiver' ), + 'default' => array( + 'notification_receiver' => $admin_email, + ), + ) + ); + + Http_Setting::register( $this ); + } +} diff --git a/forms-bridge/includes/jobs/date-fields-to-date.php b/forms-bridge/includes/jobs/date-fields-to-date.php new file mode 100644 index 00000000..e3b08339 --- /dev/null +++ b/forms-bridge/includes/jobs/date-fields-to-date.php @@ -0,0 +1,104 @@ + 'date-fields-to-date', + 'title' => __( 'Format date fields', 'forms-bridge' ), + 'description' => __( + 'Gets date, hour and minute fields and merge its values into a date with format Y-m-d H:M:S', + 'forms-bridge' + ), + 'method' => 'forms_bridge_job_format_date_fields', + 'input' => array( + array( + 'name' => 'date', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'hour', + 'schema' => array( 'type' => 'string' ), + ), + array( + 'name' => 'minute', + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'datetime', + 'schema' => array( 'type' => 'string' ), + ), + ), +); + +/** + * Date fields to date job method. + * + * @param array $payload Bridge payload. + * + * @return array|WP_Error + */ +function forms_bridge_job_format_date_fields( $payload ) { + $date = $payload['date']; + $hour = $payload['hour'] ?? '00'; + $minute = $payload['minute'] ?? '00'; + + $form_data = FBAPI::get_current_form(); + $date_index = array_search( + 'date', + array_column( $form_data['fields'], 'type' ), + true + ); + + $date_format = $form_data['fields'][ $date_index ]['format'] ?? ''; + + if ( strstr( $date_format, '-' ) ) { + $separator = '-'; + } elseif ( strstr( $date_format, '.' ) ) { + $separator = '.'; + } elseif ( strstr( $date_format, '/' ) ) { + $separator = '/'; + } + + switch ( substr( $date_format, 0, 1 ) ) { + case 'y': + [$year, $month, $day] = explode( $separator, $date ); + break; + case 'm': + [$month, $day, $year] = explode( $separator, $date ); + break; + case 'd': + [$day, $month, $year] = explode( $separator, $date ); + break; + } + + $date = "{$year}-{$month}-{$day}"; + + if ( preg_match( '/(am|pm)/i', $hour, $matches ) ) { + $hour = (int) $hour; + if ( strtolower( $matches[0] ) === 'pm' ) { + $hour += 12; + } + } + + $time = strtotime( "{$date} {$hour}:{$minute}" ); + + if ( false === $time ) { + return new WP_Error( + 'invalid-date', + __( 'Invalid date format', 'forms-bridge' ) + ); + } + + $payload['datetime'] = date( 'Y-m-d H:i:s', $time ); + return $payload; +} diff --git a/forms-bridge/includes/jobs/iso2-country-code.php b/forms-bridge/includes/jobs/iso2-country-code.php new file mode 100644 index 00000000..2e511d12 --- /dev/null +++ b/forms-bridge/includes/jobs/iso2-country-code.php @@ -0,0 +1,50 @@ + 'iso2-country-code', + 'title' => __( 'ISO2 country code', 'forms-bridge' ), + 'description' => __( + 'Gets the ISO2 country code from country names and replace its value', + 'forms-bridge' + ), + 'method' => 'forms_bridge_job_iso2_country_code', + 'input' => array( + array( + 'name' => 'country', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + ), + ), +); + +function forms_bridge_job_iso2_country_code( $payload ) { + global $forms_bridge_iso2_countries; + $country_code = strtoupper( $payload['country'] ); + + if ( ! isset( $forms_bridge_iso2_countries[ $country_code ] ) ) { + $countries = array(); + foreach ( $forms_bridge_iso2_countries as $country_code => $country ) { + $countries[ $country ] = $country_code; + } + + if ( isset( $countries[ $payload['country'] ] ) ) { + $payload['country'] = $countries[ $payload['country'] ]; + } else { + $payload['country'] = null; + } + } else { + $payload['country'] = $country_code; + } + + return $payload; +} diff --git a/forms-bridge/includes/jobs/iso3-country-code.php b/forms-bridge/includes/jobs/iso3-country-code.php new file mode 100644 index 00000000..893b72c8 --- /dev/null +++ b/forms-bridge/includes/jobs/iso3-country-code.php @@ -0,0 +1,50 @@ + 'iso3-country-code', + 'title' => __( 'ISO3 country code', 'forms-bridge' ), + 'description' => __( + 'Gets the ISO3 country code from country names and replace its value', + 'forms-bridge' + ), + 'method' => 'forms_bridge_job_iso3_country_code', + 'input' => array( + array( + 'name' => 'country', + 'required' => true, + 'schema' => array( 'type' => 'string' ), + ), + ), + 'output' => array( + array( + 'name' => 'country', + 'schema' => array( 'type' => 'string' ), + ), + ), +); + +function forms_bridge_job_iso3_country_code( $payload ) { + global $forms_bridge_iso3_countries; + $country_code = strtoupper( $payload['country'] ); + + if ( ! isset( $forms_bridge_iso3_countries[ $country_code ] ) ) { + $countries = array(); + foreach ( $forms_bridge_iso3_countries as $country_code => $country ) { + $countries[ $country ] = $country_code; + } + + if ( isset( $countries[ $payload['country'] ] ) ) { + $payload['country'] = $countries[ $payload['country'] ]; + } else { + $payload['country'] = null; + } + } else { + $payload['country'] = $country_code; + } + + return $payload; +} diff --git a/forms-bridge/includes/jobs/skip-submission.php b/forms-bridge/includes/jobs/skip-submission.php new file mode 100644 index 00000000..734000d8 --- /dev/null +++ b/forms-bridge/includes/jobs/skip-submission.php @@ -0,0 +1,30 @@ + __( 'Skip submission', 'forms-bridge' ), + 'description' => __( + 'Skip submission if condition is not truthy', + 'forms-bridge' + ), + 'method' => 'forms_bridge_job_skip_if_not_condition', + 'input' => array( + array( + 'name' => 'condition', + 'schema' => array( 'type' => 'boolean' ), + 'required' => true, + ), + ), + 'output' => array(), +); + +function forms_bridge_job_skip_if_not_condition( $payload ) { + if ( empty( $payload['condition'] ) ) { + return; + } + + return $payload; +} diff --git a/forms-bridge/index.php b/forms-bridge/index.php new file mode 100644 index 00000000..eea59b98 --- /dev/null +++ b/forms-bridge/index.php @@ -0,0 +1,2 @@ +serialize_form( $form ); + } + + /** + * Retrives a form's data by ID. + * + * @param int $form_id Form ID. + * + * @return array + */ + public function get_form_by_id( $form_id ) { + $form = GFAPI::get_form( $form_id ); + if ( ! $form ) { + return null; + } + + return $this->serialize_form( $form ); + } + + /** + * Retrives available forms' data. + * + * @return array Collection of form data array representations. + */ + public function forms() { + $forms = GFAPI::get_forms(); + return array_map( + function ( $form ) { + return $this->serialize_form( $form ); + }, + array_filter( + $forms, + function ( $form ) { + return $form['is_active'] && ! $form['is_trash']; + } + ) + ); + } + + /** + * Creates a form from a given template fields. + * + * @param array $data Form template data. + * + * @return int|null ID of the new form. + */ + public function create_form( $data ) { + if ( empty( $data['title'] ) || empty( $data['fields'] ) ) { + return; + } + + $data = array_merge( + $data, + array( + 'id' => 1, + 'fields' => $this->prepare_fields( $data['fields'] ), + 'labelPlacement' => 'top_label', + 'useCurrentUserAsAuthor' => '1', + 'postAuthor' => '1', + 'postCategory' => '1', + 'postStatus' => 'publish', + 'button' => array( + 'type' => 'text', + 'text' => esc_html__( 'Submit', 'forms-bridge' ), + 'imageUrl' => '', + 'conditionalLogic' => null, + ), + 'version' => '2.7', + ) + ); + + $form_id = GFAPI::add_form( $data ); + + if ( is_wp_error( $form_id ) ) { + return; + } + + return $form_id; + } + + /** + * Removes a form by ID. + * + * @param integer $form_id Form ID. + * + * @return boolean Removal result. + */ + public function remove_form( $form_id ) { + return GFFormsModel::delete_form( $form_id ); + } + + /** + * Retrives the current submission ID. + * + * @return string|null + */ + public function submission_id() { + $submission = $this->submission( true ); + if ( $submission ) { + return (string) $submission['id']; + } + } + + /** + * Retrives the current submission data. + * + * @param boolean $raw Control if the submission is serialized before exit. + * + * @return array|null + */ + public function submission( $raw = false ) { + $form_data = $this->form(); + if ( ! $form_data ) { + return null; + } + + $submission = GFFormsModel::get_current_lead( + GFAPI::get_form( $form_data['id'] ) + ); + + if ( ! $submission ) { + return; + } elseif ( $raw ) { + return $submission; + } + + $form = $this->form(); + return $this->serialize_submission( $submission, $form ); + } + + /** + * Retrives the current submission uploaded files. + * + * @return array|null Collection of uploaded files. + */ + public function uploads() { + $form_data = $this->form(); + if ( ! $form_data ) { + return null; + } + + $submission = GFFormsModel::get_current_lead( + GFAPI::get_form( $form_data['id'] ) + ); + + if ( ! $submission ) { + return null; + } + + $form = $this->form(); + return $this->submission_uploads( $submission, $form ); + } + + /** + * Serializes gf form's data. + * + * @param array $form GF form data. + * + * @return array + */ + public function serialize_form( $form ) { + $form_id = (int) $form['id']; + + $fields = array(); + foreach ( $form['fields'] as $field ) { + $field = $this->serialize_field( $field ); + + if ( $field ) { + $field = wp_is_numeric_array( $field ) ? $field : array( $field ); + $fields = array_merge( $fields, $field ); + } + } + + return apply_filters( + 'forms_bridge_form_data', + array( + '_id' => 'gf:' . $form_id, + 'id' => $form_id, + 'title' => $form['title'], + 'bridges' => FBAPI::get_form_bridges( $form_id, 'gf' ), + 'fields' => $fields, + ), + $form, + 'gf' + ); + } + + /** + * Serializes a GFField as array data. + * + * @param GFField $field Field object instance. + * + * @return array|null + */ + private function serialize_field( $field ) { + if ( + in_array( + $field->type, + array( + 'page', + 'section', + 'html', + 'submit', + 'captcha', + ), + true + ) + ) { + return null; + } + + if ( strstr( $field->type, 'post_' ) ) { + return null; + } + + $label = $field->adminLabel ?: $field->label; + $name = $field->inputName ?: $label; + + $allowsPrepopulate = $field->allowsPrepopulate ?? false; + + $choices = $field->choices ?: array(); + $options = array(); + foreach ( $choices as $choice ) { + $options[] = array( + 'value' => $choice['value'], + 'label' => $choice['label'] ?? $choice['text'] ?? '', + ); + } + + try { + $inputs = array(); + $entry_inputs = $field->get_entry_inputs() ?: array(); + foreach ( $entry_inputs as $input ) { + $input['name'] = $allowsPrepopulate ? $input['name'] : ''; + $inputs[] = $input; + } + } catch ( Error ) { + $inputs = array(); + } + + $inputs = array_values( + array_filter( + $inputs, + fn ( $input ) => ! isset( $input['isHidden'] ) || ! $input['isHidden'] + ) + ); + + $named_inputs = array_filter( + $inputs, + fn ( $input ) => ! empty( $input['name'] ) + ); + + $subfields = array(); + if ( count( $named_inputs ) ) { + $count = count( $inputs ); + for ( $i = 1; $i <= $count; $i++ ) { + $input = $inputs[ $i - 1 ]; + + $input_label = implode( + ' ', + array( + $label, + $input['label'] ? "({$input['label']})" : "($i)", + ) + ); + + $input_name = $input['name'] ?: $input_label; + + $subfields[] = $this->serialize_field( + (object) array_merge( + (array) $field, + $input, + array( + 'id' => $input['id'], + 'inputName' => $input_name, + 'label' => $input_label, + 'adminLabel' => $input_label, + 'type' => 'text', + 'schema' => array( 'type' => 'string' ), + ) + ) + ); + } + } + + switch ( $field->type ) { + case 'list': + case 'checkbox': + case 'multiselect': + case 'multi_choice': + case 'image_choice': + case 'option': + case 'select': + case 'radio': + $type = 'select'; + break; + case 'number': + case 'total': + case 'quantity': + $type = 'number'; + break; + case 'consent': + $type = 'checkbox'; + break; + case 'fileupload': + $type = 'file'; + break; + case 'phone': + $type = 'tel'; + break; + case 'email': + $type = 'email'; + break; + case 'website': + $type = 'url'; + break; + case 'date': + $type = 'date'; + break; + case 'textarea': + $type = $field->type; + break; + case 'address': + case 'product': + case 'name': + case 'shipping': + default: + $type = 'text'; + break; + } + + $field = apply_filters( + 'forms_bridge_form_field_data', + array( + 'id' => $field->id, + 'type' => $type, + 'name' => trim( $name ), + 'label' => trim( $label ), + 'required' => $field->isRequired, + 'options' => $options, + 'inputs' => $inputs, + 'is_file' => 'fileupload' === $field->type, + 'is_multi' => $this->is_multi_field( $field ), + 'conditional' => $field->conditionalLogic['enabled'] ?? false, + 'format' => 'date' === $field->type ? 'yyyy-mm-dd' : '', + 'schema' => $this->field_value_schema( $field ), + 'basetype' => $field->type, + ), + $field, + 'gf' + ); + + if ( ! empty( $subfields ) && ( $allowsPrepopulate || 'list' === $field['type'] ) ) { + foreach ( $subfields as &$sf ) { + $sf['parent'] = $field; + } + } + + return $field; + } + + /** + * Checks if a filed is multi value field. + * + * @param GF_Field $field Target field instance. + * + * @return boolean + */ + private function is_multi_field( $field ) { + if ( 'fileupload' === $field->type ) { + return $field->multipleFiles ?? false; + } + + if ( isset( $field->storageType ) && 'json' === $field->storageType ) { + return true; + } + + // if (isset($field->choiceLimit)) { + // if ($field->choiceLimit === 'unlimited') { + // return true; + // } elseif ($field->choiceLimit === 'exactly' && $field->choiceLimitNumber > 1) { + // return true; + // } + // } + + if ( 'list' === $field->type ) { + return true; + } + + if ( in_array( $field->inputType, array( 'list', 'checkbox' ), true ) ) { + return true; + } + + return false; + } + + /** + * Gets the field value JSON schema. + * + * @param GF_Field $field Field instance. + * + * @return array JSON schema of the value of the field. + */ + private function field_value_schema( $field ) { + switch ( $field->type ) { + case 'list': + if ( ! empty( $field->choices ) ) { + return array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array_reduce( + $field->choices, + static function ( $choices, $choice ) { + $choices[ $choice['value'] ] = array( + 'type' => 'string', + ); + return $choices; + }, + array() + ), + ), + 'additionalItems' => true, + ); + } + + return array( + 'type' => 'array', + 'items' => array( 'type' => 'string' ), + 'additionalItems' => true, + ); + case 'checkbox': + case 'multiselect': + $items = array(); + $count = count( $field->choices ); + for ( $i = 0; $i < $count; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + ); + case 'multi_choice': + case 'image_choice': + case 'option': + if ( $this->is_multi_field( $field ) ) { + if ( 'range' === $field->choiceLimit ) { + $maxItems = $field->choiceLimitMax; + } elseif ( 'exactly' === $field->choiceLimit ) { + $maxItems = $field->choiceLimitNumber; + } else { + $maxItems = count( $field->choices ); + } + + $items = array(); + for ( $i = 0; $i < $maxItems; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + ); + } + + return array( 'type' => 'string' ); + case 'select': + case 'radio': + case 'address': + case 'website': + case 'product': + case 'email': + case 'textarea': + case 'name': + case 'shipping': + return array( 'type' => 'string' ); + case 'number': + case 'total': + case 'quantity': + return array( 'type' => 'number' ); + case 'fileupload': + return; + case 'consent': + return array( 'type' => 'boolean' ); + default: + return array( 'type' => 'string' ); + } + } + + /** + * Serializes the current form's submission data. + * + * @param array $submission GF form submission. + * @param array $form_data Form data. + * + * @return array + */ + public function serialize_submission( $submission, $form_data ) { + $data = array(); + + $has_total = array_search( + 'total', + array_map( + static function ( $field ) { + return $field['basetype']; + }, + $form_data['fields'] + ) + ); + + $has_quantity = array_search( + 'quantity', + array_map( + static function ( $field ) { + return $field['basetype']; + }, + $form_data['fields'] + ), + true + ); + + foreach ( $form_data['fields'] as $field ) { + if ( $field['is_file'] ) { + continue; + } + + $input_name = $field['name']; + $inputs = $field['inputs']; + + if ( ! empty( $inputs ) ) { + $values = array(); + foreach ( $inputs as $input ) { + if ( ! $this->isset( $input['id'] ) ) { + continue; + } + + $value = rgar( $submission, (string) $input['id'] ); + if ( $input_name && $value ) { + $value = $this->format_value( + $value, + $field['basetype'], + $input + ); + + if ( null !== $value ) { + $values[] = $value; + } + } + } + + if ( 'consent' === $field['basetype'] ) { + $data[ $input_name ] = boolval( $values[0] ?? false ); + } elseif ( 'name' === $field['basetype'] ) { + $data[ $input_name ] = implode( ' ', $values ); + } elseif ( 'product' === $field['basetype'] ) { + if ( $has_total ) { + $data[ $input_name ] = $values[0]; + } else { + if ( $has_quantity ) { + $values = array_slice( $values, 0, 2 ); + } + + $data[ $input_name ] = implode( '|', $values ); + } + } elseif ( 'address' === $field['basetype'] ) { + $data[ $input_name ] = implode( ', ', $values ); + } else { + $data[ $input_name ] = $values; + } + } else { + /* simple fields */ + $isset = $this->isset( $field['id'] ); + if ( ! $isset ) { + continue; + } + + if ( $input_name ) { + $raw_value = rgar( $submission, (string) $field['id'] ); + $data[ $input_name ] = $this->format_value( + $raw_value, + $field['basetype'] + ); + } + } + } + + return $data; + } + + /** + * Formats field values with noop fallback. + * + * @param mixed $value Field's value. + * @param string $field_type GF field type. + * @param array $input Field's input data. + * + * @return mixed Formatted value. + */ + private function format_value( $value, $field_type, $input = null ) { + try { + switch ( $field_type ) { + case 'consent': + if ( preg_match( '/\.1$/', $input['id'] ) ) { + return '1' === $value; + } + + return null; + case 'hidden': + $number_val = (float) $value; + if ( strval( $number_val ) === $value ) { + return $number_val; + } + break; + case 'quantity': + case 'number': + return (float) preg_replace( '/[^0-9\.,]/', '', $value ); + case 'list': + return maybe_unserialize( $value ); + case 'multiselect': + return json_decode( $value ); + case 'product': + case 'option': + case 'shipping': + return $value; + // return explode('|', $value)[0]; + } + // phpcs:disable Generic.CodeAnalysis.EmptyStatement + } catch ( TypeError ) { + /* do nothing */ + } + // phpcs:enable Generic.CodeAnalysis.EmptyStatement + + return $value; + } + + /** + * Gets the current submission's uploaded files. + * + * @param array $submission GF submission data. + * @param array $form_data Form data. + * + * @return array Uploaded files data. + */ + protected function submission_uploads( $submission, $form_data ) { + return array_reduce( + array_filter( + $form_data['fields'], + function ( $field ) { + return $field['is_file']; + } + ), + function ( $carry, $field ) use ( $submission, $form_data ) { + $upload_path = GFFormsModel::get_upload_path( $form_data['id'] ); + $upload_url = GFFormsModel::get_upload_url( $form_data['id'] ); + + $urls = $submission[ $field['id'] ] ?? array(); + $urls = $field['is_multi'] ? json_decode( $urls, true ) : array( $urls ); + + if ( ! is_array( $urls ) ) { + return $carry; + } + + $paths = array(); + foreach ( $urls as $url ) { + $path = str_replace( $upload_url, $upload_path, $url ); + if ( is_file( $path ) ) { + $paths[] = $path; + } + } + + if ( ! empty( $paths ) ) { + $carry[ $field['name'] ] = array( + 'path' => $field['is_multi'] ? $paths : $paths[0], + 'is_multi' => $field['is_multi'], + ); + } + + return $carry; + }, + array() + ); + } + + /** + * Helper function to check if field is set on $_POST super global. + * + * @param string $field_id ID of the field. + * + * @return boolean + */ + private function isset( $field_id ) { + $key = 'input_' . implode( '_', explode( '.', $field_id ) ); + return isset( $_POST[ $key ] ) || defined( 'WP_TESTS_DOMAIN' ); + } + + /** + * Decorate bridge's tempalte form fields data to be created as gf fields. + * + * @param array $fields Array with bridge's template form fields data. + * + * @return array Decorated array of fields. + */ + private function prepare_fields( $fields ) { + $gf_fields = array(); + $count = count( $fields ); + for ( $i = 0; $i < $count; $i++ ) { + $id = $i + 1; + $field = $fields[ $i ]; + $args = array( + $id, + $field['name'], + $field['label'] ?? '', + $field['required'] ?? false, + ); + + switch ( $field['type'] ) { + case 'hidden': + if ( isset( $field['value'] ) ) { + if ( is_bool( $field['value'] ) ) { + $field['value'] = $field['value'] ? '1' : '0'; + } + + $args[] = (string) $field['value']; + $gf_fields[] = $this->hidden_field( ...$args ); + } + + break; + case 'number': + $constraints = array( + 'rangeMin' => $field['min'] ?? '', + 'rangeMax' => $filed['max'] ?? '', + 'rangeStep' => $field['step'] ?? '1', + 'defaultValue' => floatval( $field['default'] ?? 0 ), + ); + + $args[] = $constraints; + $gf_fields[] = $this->number_field( ...$args ); + break; + case 'email': + $gf_fields[] = $this->email_field( ...$args ); + break; + case 'tel': + $gf_fields[] = $this->tel_field( ...$args ); + break; + case 'select': + $args[] = $field['options'] ?? array(); + $args[] = $field['is_multi'] ?? false; + $gf_fields[] = $this->select_field( ...$args ); + break; + case 'checkbox': + $gf_fields[] = $this->checkbox_field( ...$args ); + break; + case 'textarea': + $gf_fields[] = $this->textarea_field( ...$args ); + break; + case 'url': + $gf_fields[] = $this->url_field( ...$args ); + break; + case 'date': + $gf_fields[] = $this->date_field( ...$args ); + break; + case 'file': + $args[] = $field['is_multi'] ?? false; + $args[] = $field['filetypes'] ?? ''; + $gf_fields[] = $this->file_field( ...$args ); + break; + // case 'text': + default: + $gf_fields[] = $this->text_field( ...$args ); + } + } + + return $gf_fields; + } + + /** + * Returns a default field array data. Used as template for the field creation methods. + * + * @param string $type Field type. + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function field_template( $type, $id, $name, $label, $required ) { + return array( + 'type' => $type, + 'id' => (int) $id, + 'isRequired' => (bool) $required, + 'size' => 'large', + 'errorMessage' => __( 'Please supply a valid value', 'forms-bridge' ), + 'label' => $label, + 'formId' => 84, + 'inputType' => '', + 'displayOnly' => '', + 'inputs' => null, + 'choices' => '', + 'conditionalLogic' => '', + 'labelPlacement' => '', + 'descriptionPlacement' => '', + 'subLabelPlacement' => '', + 'placeholder' => '', + 'multipleFiles' => false, + 'maxFiles' => '', + 'calculationFormula' => '', + 'calculationRounding' => '', + 'enableCalculation' => '', + 'disableQuantity' => false, + 'displayAllCategories' => false, + 'inputMask' => false, + 'inputMaskValue' => '', + 'allowsPrepopulate' => true, + 'useRichTextEditor' => false, + 'visibility' => 'visible', + 'fields' => '', + 'inputMaskIsCustom' => false, + 'layoutGroupId' => '17f293c9', + 'autocompleteAttribute' => '', + 'emailConfirmEnabled' => false, + 'adminLabel' => '', + 'description' => '', + 'maxLength' => '', + 'cssClass' => '', + 'inputName' => $name, + 'noDuplicates' => false, + 'defaultValue' => '', + 'enableAutocomplete' => false, + ); + } + + /** + * Returns a valid email field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function email_field( $id, $name, $label, $required ) { + return array_merge( + $this->field_template( 'email', $id, $name, $label, $required ), + array( + 'errorMessage' => __( + 'please supply a valid email address', + 'forms-bridge' + ), + 'enableAutocomplete' => true, + ) + ); + } + + /** + * Returns a valid tel field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function tel_field( $id, $name, $label, $required ) { + return array_merge( + $this->field_template( 'phone', $id, $name, $label, $required ), + array(), + ); + } + + /** + * Returns a valid textarea field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function textarea_field( $id, $name, $label, $required ) { + return $this->field_template( 'textarea', $id, $name, $label, $required ); + } + + /** + * Returns a valid multi select field data, as a select field if is single, as + * a checkbox field if is multiple. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * @param array $options Options data. + * @param bool $is_multi Is field multi value. + * + * @return array + */ + private function select_field( + $id, + $name, + $label, + $required, + $options, + $is_multi + ) { + $choices = array_map( + function ( $opt ) { + return array( + 'text' => esc_html( $opt['label'] ), + 'value' => $opt['value'], + 'isSelected' => false, + 'price' => '', + ); + }, + $options + ); + + if ( $is_multi ) { + $inputs = array(); + $count = count( $choices ); + for ( $i = 0; $i < $count; $i++ ) { + $input_id = $i + 1; + $inputs[] = array( + 'id' => $id . '.' . $input_id, + 'label' => $choices[ $i ]['label'], + 'name' => '', + ); + } + + return array_merge( + $this->field_template( + 'checkbox', + $id, + $name, + $label, + $required + ), + array( + 'choices' => $choices, + 'inputs' => $inputs, + 'enableChoiceValue' => true, + ) + ); + } else { + return array_merge( + $this->field_template( 'select', $id, $name, $label, $required ), + array( + 'choices' => $choices, + 'enableChoiceValue' => true, + ) + ); + } + } + + /** + * Returns a valid file-upload field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * @param boolean $is_multi Indicates if the field supports multiple values. + * @param string $filetypes String with allowed file extensions separated by commas. + * + * @return array + */ + private function file_field( + $id, + $name, + $label, + $required, + $is_multi, + $filetypes + ) { + return array_merge( + $this->field_template( 'fileupload', $id, $name, $label, $required ), + array( + 'allowedExtensions' => (string) $filetypes, + 'multipleFiles' => (bool) $is_multi, + ) + ); + } + + /** + * Returns a valid hidden field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label (unused). + * @param boolean $required Is field required (unused). + * @param string $value Field's default value. + * + * @return array + */ + private function hidden_field( $id, $name, $label, $required, $value ) { + return array_merge( + $this->field_template( 'hidden', $id, $name, $name, true ), + array( + 'inputType' => 'hidden', + 'defaultValue' => $value, + ) + ); + } + + /** + * Returns a valid hidden field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function url_field( $id, $name, $label, $required ) { + return $this->field_template( 'website', $id, $name, $label, $required ); + } + + /** + * Returns a valid hidden field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function text_field( $id, $name, $label, $required ) { + return array_merge( + $this->field_template( 'text', $id, $name, $label, $required ), + array( + 'inputType' => 'text', + ) + ); + } + + /** + * Returns a valid date field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function date_field( $id, $name, $label, $required ) { + return array_merge( + $this->field_template( 'date', $id, $name, $label, $required ), + array( + 'dateType' => 'datepicker', + 'calendarIconType' => 'none', + 'dateFormatPlacement' => 'below', + 'dateFormat' => 'mdy', + ) + ); + } + + /** + * Returns a valid hidden field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * @param array $constraints Input constraints. + * + * @return array + */ + private function number_field( $id, $name, $label, $required, $constraints ) { + return array_merge( + $this->field_template( 'number', $id, $name, $label, $required ), + array_merge( + $constraints, + array( + 'inputType' => 'number', + 'numberFormat' => 'decimal_dot', + ) + ) + ); + } + + /** + * Returns a valid checkbox field data. + * + * @param int $id Field id. + * @param string $name Input name. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function checkbox_field( $id, $name, $label, $required ) { + return array_merge( + $this->field_template( 'consent', $id, $name, $label, $required ), + array( + 'choices' => array( + array( + 'isSelected' => false, + 'price' => '', + 'text' => __( 'Checked', 'forms-bridge' ), + 'value' => '1', + ), + ), + ), + ); + } +} + +add_filter( + 'gform_field_content', + function ( $field_content, $field, $value, $entry_id, $form_id ) { + if ( 'number' !== $field->type ) { + return $field_content; + } + + if ( empty( $field->rangeStep ) ) { + return $field_content; + } + + $step = (int) $field->rangeStep; + return str_replace( "step='any'", "step='{$step}'", $field_content ); + }, + 10, + 5 +); + +GF_Integration::setup(); diff --git a/forms-bridge/integrations/ninja/class-ninja-integration.php b/forms-bridge/integrations/ninja/class-ninja-integration.php new file mode 100644 index 00000000..4e1275a2 --- /dev/null +++ b/forms-bridge/integrations/ninja/class-ninja-integration.php @@ -0,0 +1,1317 @@ + null ); + $form_id = (int) $form_data['id']; + if ( empty( $form_id ) ) { + return null; + } + + return $this->get_form_by_id( $form_id ); + } + + /** + * Retrives a form model's data by ID. + * + * @param integer $form_id Form ID. + * + * @return array|null Form data. + */ + public function get_form_by_id( $form_id ) { + $form = Ninja_Forms()->form( $form_id ); + + if ( ! $form ) { + return null; + } + + return $this->serialize_form( $form ); + } + + /** + * Retrives available form models' data. + * + * @return array Collection of forms data. + */ + public function forms() { + $forms = Ninja_Forms()->form()->get_forms(); + + return array_map( + function ( $form ) { + $form = Ninja_Forms()->form( $form->get_id() ); + return $this->serialize_form( $form ); + }, + $forms + ); + } + + /** + * Creates a form from the given template fields. + * + * @param array $data Form template data. + * + * @return int|null ID of the new form. + * + * @todo Implement this routine. + */ + public function create_form( $data ) { + $title = sanitize_text_field( $data['title'] ); + $form_data = $this->form_template( $title ); + $form_data['fields'] = $this->decorate_form_fields( $data['fields'] ); + + foreach ( $form_data['fields'] as $field ) { + $form_data['settings']['formContentData'] = $field['settings']['key']; + } + + $form = Ninja_Forms()->form()->get(); + $form->save(); + + $form_data['id'] = $form->get_id(); + + $form->update_settings( $form_data['settings'] )->save(); + + $db_fields_controller = new NF_Database_FieldsController( + $form_data['id'], + $form_data['fields'] + ); + $db_fields_controller->run(); + $form_data['fields'] = $db_fields_controller->get_updated_fields_data(); + + foreach ( $form_data['actions'] as &$action_data ) { + $action_data['parent_id'] = $form_data['id']; + $action = Ninja_Forms()->form()->action()->get(); + $action->save(); + $action_data['id'] = $action->get_id(); + $action->update_settings( $action_data )->save(); + } + + WPN_Helper::update_nf_cache( $form_data['id'], $form_data ); + + return $form_data['id']; + } + + /** + * Removes a form by ID. + * + * @param integer $form_id Form ID. + * + * @return boolean Removal result. + */ + public function remove_form( $form_id ) { + $form = Ninja_Forms()->form( $form_id )->get(); + if ( $form ) { + return $form->delete(); + } + + return false; + } + + /** + * Retrives the current submission ID. + * + * @return string|null + */ + public function submission_id() { + $submission = $this->submission( true ); + if ( $submission ) { + return (string) $submission['actions']['save']['sub_id']; + } + } + + /** + * Retrives the current form submission data. + * + * @param boolean $raw Control if the submission is serialized before exit. + * + * @return array + */ + public function submission( $raw = false ) { + if ( empty( self::$submission ) ) { + return null; + } elseif ( $raw ) { + return self::$submission; + } + + $form = $this->form(); + return $this->serialize_submission( self::$submission, $form ); + } + + /** + * Retrives the current submission uploaded files. + * + * @return array Collection of uploaded files. + * + * @todo Adapt to premium version with available upload field. + */ + public function uploads() { + $form_data = $this->form(); + if ( ! $form_data ) { + return null; + } + + $submission = self::$submission; + if ( empty( $submission ) ) { + return null; + } + + return $this->submission_uploads( $submission, $form_data ); + } + + /** + * Serializes a ninja form model instance as array data. + * + * @param NF_Abstracts_ModelFactory $form_factory Form factory instance. + * + * @return array + */ + public function serialize_form( $form_factory ) { + $form = $form_factory->get(); + $form_id = (int) $form->get_id(); + $form_settings = $form->get_settings(); + + $fields = array(); + foreach ( $form_factory->get_fields() as $field ) { + $field = $this->serialize_field( $field, $form_settings ); + + if ( $field ) { + $fields[] = $field; + } + } + + return apply_filters( + 'forms_bridge_form_data', + array( + '_id' => 'ninja:' . $form_id, + 'id' => $form_id, + 'title' => $form->get_setting( 'title' ), + 'bridges' => FBAPI::get_form_bridges( $form_id, 'ninja' ), + 'fields' => array_values( $fields ), + ), + $form, + 'ninja' + ); + } + + /** + * Serializes a form field model instance as array data. + * + * @param NF_Database_Models_Field $field Form field model instance. + * @param array $form_settings Form settings data. + * + * @return array + */ + private function serialize_field( $field, $form_settings ) { + if ( + in_array( + $field->get_setting( 'type' ), + array( + 'html', + 'hr', + 'confirm', + 'recaptcha', + 'spam', + 'submit', + ), + true + ) + ) { + return; + } + + return apply_filters( + 'forms_bridge_form_field_data', + $this->serialize_field_settings( + $field->get_id(), + $field->get_settings(), + $form_settings + ), + $field, + 'ninja' + ); + } + + /** + * Serializes field settings as field data array. + * + * @param int $id Field id. + * @param array $settings Field settings data. + * @param array $form_settings Form settings data. + * + * @return array + */ + public function serialize_field_settings( $id, $settings, $form_settings ) { + $name = + $settings['key'] ?? + ( $settings['admin_label'] ?? $settings['label'] ); + + $children = isset( $settings['fields'] ) + ? array_map( + function ( $setting ) use ( $form_settings ) { + return $this->serialize_field_settings( + $setting['id'], + $setting, + $form_settings + ); + }, + $settings['fields'] + ) + : array(); + + $is_conditional = false; + $conditions = $form_settings['conditions'] ?? array(); + foreach ( (array) $conditions as $condition ) { + $then = $condition['then'] ?? array(); + $else = $condition['else'] ?? array(); + foreach ( array_merge( $then, $else ) as $effect ) { + if ( 'field' !== $effect['type'] ) { + continue; + } + + $is_conditional = $effect['key'] === $settings['key'] && 'hide_field' === $effect['trigger']; + if ( $is_conditional ) { + break; + } + } + + if ( $is_conditional ) { + break; + } + } + + switch ( $settings['type'] ) { + case 'email': + $type = 'email'; + break; + case 'phone': + $type = 'tel'; + break; + case 'checkbox': + $type = 'checkbox'; + break; + case 'date': + $type = 'date'; + break; + case 'select': + case 'radio': + case 'listradio': + case 'listselect': + case 'listcountry': + case 'liststate': + case 'listimage': + case 'listmultiselect': + case 'listcheckbox': + $type = 'select'; + break; + case 'starrating': + case 'number': + $type = 'number'; + break; + case 'repeater': + $type = 'mixed'; + break; + case 'file_upload': + $type = 'file'; + break; + case 'textarea': + $type = 'textarea'; + break; + case 'textbox': + case 'lastname': + case 'firstname': + case 'address': + case 'zip': + case 'city': + case 'spam': + default: + $type = 'text'; + break; + } + + return array( + 'id' => $id, + 'type' => $type, + 'name' => trim( $name ), + 'label' => trim( $settings['label'] ?? $name ), + 'required' => isset( $settings['required'] ) + ? '1' === $settings['required'] + : false, + 'options' => isset( $settings['options'] ) + ? $settings['options'] + : array(), + 'is_file' => 'file_upload' === $settings['type'], + 'is_multi' => $this->is_multi_field( $settings ), + 'conditional' => $is_conditional, + 'children' => $children, + 'format' => strtolower( $settings['date_format'] ?? '' ), + 'schema' => $this->field_value_schema( $settings, $children ), + 'basetype' => $settings['type'], + ); + } + + /** + * Checks if a filed is multi value field. + * + * @param array $settings Field settings data. + * + * @return boolean + */ + private function is_multi_field( $settings ) { + if ( + in_array( + $settings['type'], + array( 'listmultiselect', 'listcheckbox', 'repeater' ), + true + ) + ) { + return true; + } + + if ( + 'listimage' === $settings['type'] && + ( $settings['allow_multi_select'] ?? false ) + ) { + return true; + } + + if ( + 'file_upload' === $settings['type'] && + $settings['upload_multi_count'] > 1 + ) { + return true; + } + + return false; + } + + /** + * Gets the field value JSON schema. + * + * @param array $settings Field settings data. + * @param array $children Children fields. + * + * @return array JSON schema of the value of the field. + */ + private function field_value_schema( $settings, $children = array() ) { + switch ( $settings['type'] ) { + case 'checkbox': + return array( 'type' => 'boolean' ); + case 'textbox': + case 'lastname': + case 'firstname': + case 'address': + case 'zip': + case 'city': + case 'spam': + case 'phone': + case 'email': + case 'textarea': + case 'select': + case 'radio': + case 'checkbox': + case 'date': + case 'listradio': + case 'listselect': + case 'listcountry': + case 'liststate': + return array( 'type' => 'string' ); + case 'listimage': + if ( $settings['allow_multi_select'] ?? false ) { + $items = array(); + + $l = count( $settings['image_options'] ); + for ( $i = 0; $i < $l; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + ); + } + + return array( 'type' => 'string' ); + case 'listmultiselect': + case 'listcheckbox': + $items = array(); + + $l = count( $settings['options'] ); + for ( $i = 0; $i < $l; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + ); + case 'starrating': + case 'number': + return array( 'type' => 'number' ); + case 'repeater': + $i = 0; + $properties = array_reduce( + $children, + function ( $props, $child ) use ( $settings, &$i ) { + $field = $settings['fields'][ $i ]; + $props[ $child['name'] ] = $this->field_value_schema( + $field + ); + $i++; + return $props; + }, + array() + ); + + return array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => $properties, + ), + 'additionalItems' => true, + ); + case 'file_upload': + return; + default: + return array( 'type' => 'string' ); + } + } + + /** + * Serialize the form's submission data. + * + * @param array $submission Submission data. + * @param array $form_data Form data. + * + * @return array. + */ + public function serialize_submission( $submission, $form_data ) { + $data = array(); + + foreach ( $form_data['fields'] as $field_data ) { + if ( ! $field_data ) { + continue; + } + + $is_file = $field_data['is_file'] ?? false; + if ( $is_file ) { + continue; + } + + $field = $submission['fields'][ (int) $field_data['id'] ] ?? null; + if ( ! $field ) { + continue; + } + + if ( 'repeater' === $field_data['basetype'] ) { + $subfields = $field['fields']; + $values = $field['value']; + $fieldset = array(); + + $i = 0; + foreach ( array_values( $values ) as $value ) { + $row_index = floor( $i / count( $subfields ) ); + $row = + count( $fieldset ) === +$row_index + ? array() + : $fieldset[ $row_index ]; + + $field_index = $i % count( $subfields ); + $child_field = $field['fields'][ $field_index ]; + $child_data = $field_data['children'][ $field_index ]; + + $row[ $child_data['name'] ] = $this->format_field_value( + $child_field['type'], + $value['value'] + ); + + $fieldset[ $row_index ] = $row; + ++$i; + } + + $data[ $field_data['name'] ] = $fieldset; + } else { + $data[ $field_data['name'] ] = $this->format_field_value( + $field_data['basetype'], + $field['value'] + ); + + if ( + false === $data[ $field_data['name'] ] && + 'boolean' !== $field_data['schema']['type'] && + $field_data['conditional'] + ) { + $data[ $field_data['name'] ] = null; + } + } + } + + return $data; + } + + /** + * Formats field values based on its type. + * + * @param string $type Field type. + * @param mixed $value Raw field value. + * + * @return mixed Formated value. + */ + private function format_field_value( $type, $value ) { + if ( 'hidden' === $type ) { + $number_val = (float) $value; + if ( strval( $number_val ) === $value ) { + return $number_val; + } + + return $value; + } elseif ( 'number' === $type || 'starrating' === $type ) { + return (float) $value; + } elseif ( 'date' === $type ) { + if ( is_string( $value ) ) { + return $value; + } + + $_value = ''; + + if ( ! empty( $value['date'] ) ) { + $_value .= $value['date'] . ' '; + } + + if ( isset( $value['hour'] ) ) { + $_value .= "{$value['hour']}:{$value['minute']}"; + + if ( isset( $value['ampm'] ) ) { + $_value .= ' ' . $value['ampm']; + } + } + + return trim( $_value ); + } else { + return $value; + } + } + + /** + * Gets submission uploaded files. + * + * @param WPCF7_Submission $submission Submission instance. + * @param array $form_data Form data. + * + * @return array Uploaded files data. + * + * @todo Ninja forms uploads addon for premium licenses + */ + protected function submission_uploads( $submission, $form_data ) { + $uploads = array(); + + foreach ( $form_data['fields'] as $field_data ) { + $is_file = $field_data['is_file'] ?? false; + if ( ! $is_file ) { + continue; + } + + $field = $submission['fields'][ (int) $field_data['id'] ]; + + $uploads_path = wp_upload_dir()['basedir'] . '/ninja-forms/' . $form_data['id']; + $urls = (array) $field['value']; + $paths = array(); + foreach ( $urls as $url ) { + $basename = basename( $url ); + $path = $uploads_path . '/' . $basename; + if ( is_file( $path ) ) { + $paths[] = $path; + } + } + + if ( empty( $paths ) ) { + continue; + } + + if ( $field_data['is_multi'] ) { + $uploads[ $field_data['name'] ] = array( + 'path' => $paths, + 'is_multi' => true, + ); + } else { + $uploads[ $field_data['name'] ] = array( + 'path' => $paths[0], + 'is_multi' => false, + ); + } + } + + return $uploads; + } + + /** + * Prepares form data fields to be created as a new form. + * + * @param array $fields Form data fields. + * + * @return array Decorated fields. + */ + private function decorate_form_fields( $fields ) { + $nf_fields = array(); + + $l = count( $fields ); + for ( $i = 0; $i < $l; $i++ ) { + $order = $i + 1; + $field = $fields[ $i ]; + + $args = array( + $order, + $field['name'], + $field['label'] ?? $field['name'], + $field['required'] ?? false, + ); + + switch ( $field['type'] ) { + case 'number': + $constraints = array(); + if ( isset( $field['min'] ) ) { + $constraints['num_min'] = $field['min']; + } + + if ( isset( $field['max'] ) ) { + $constraints['num_max'] = $field['max']; + } + + if ( isset( $field['step'] ) ) { + $constraints['num_step'] = $field['step']; + } + + $args[] = $constraints; + $nf_fields[] = $this->number_field( ...$args ); + break; + case 'text': + $nf_fields[] = $this->text_field( ...$args ); + break; + case 'textarea': + $nf_fields[] = $this->textarea_field( ...$args ); + break; + case 'tel': + $nf_fields[] = $this->phone_field( ...$args ); + break; + case 'email': + $nf_fields[] = $this->email_field( ...$args ); + break; + case 'date': + $nf_fields[] = $this->date_field( ...$args ); + break; + case 'file': + $args[] = $field['is_multi'] ?? false; + $args[] = $field['filetypes'] ?? ''; + $nf_fields[] = $this->upload_field( ...$args ); + break; + case 'hidden': + if ( isset( $field['value'] ) ) { + if ( is_bool( $field['value'] ) ) { + $field['value'] = $field['value'] ? '1' : '0'; + } + + $args[] = (string) $field['value']; + $nf_fields[] = $this->hidden_field( ...$args ); + } + + break; + case 'select': + $args[] = $field['options'] ?? array(); + $args[] = $field['is_multi'] ?? false; + $nf_fields[] = $this->select_field( ...$args ); + break; + case 'checkbox': + $nf_fields[] = $this->checkbox_field( ...$args ); + break; + default: + $args = array_merge( array( $field['type'] ), $args ); + $nf_fields[] = $this->field_template( ...$args ); + } + } + + $nf_fields[] = array( + 'objectType' => 'Field', + 'objectDomain' => 'fields', + 'editActive' => false, + 'order' => count( $nf_fields ) + 1, + 'type' => 'submit', + 'label' => __( 'Submit', 'forms-bridge' ), + 'processing_label' => __( 'Processing', 'forms-bridge' ), + 'key' => 'submit', + ); + + return array_map( + function ( $nf_field ) { + return array( + 'id' => 'tmp-' . $nf_field['order'], + 'settings' => $nf_field, + ); + }, + $nf_fields + ); + } + + /** + * Ninja form field array representation template to be used by field generators. + * + * @param string $type Field type. + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * + * @return array Ninja form field data. + */ + private function field_template( $type, $order, $name, $label, $required ) { + return array( + 'objectType' => 'Field', + 'objectDomain' => 'fields', + 'editActive' => false, + 'order' => (string) $order, + 'type' => $type, + 'label' => $label, + 'key' => $name, + // 'custom_name_attribute' => $name, + 'admin_label' => '', + 'required' => $required ? '1' : '', + 'default' => '', + 'placeholder' => '', + 'container_class' => '', + 'label_pos' => 'default', + ); + } + + /** + * Upload field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Is field required. + * @param boolean $is_multi Supports multiple files. + * @param string $filetypes File types constraints. + * + * @return array Ninja form upload field data. + */ + private function upload_field( + $order, + $name, + $label, + $required, + $is_multi, + $filetypes = '' + ) { + $filetypes = preg_replace( '/\.(?=[A-Za-z]{2})/', '', $filetypes ); + + return array_merge( + $this->field_template( + 'file_upload', + $order, + $name, + $label, + $required + ), + array( + 'upload_types' => $filetypes, + 'upload_multi_count' => $is_multi ? 2 : 1, + ) + ); + } + + /** + * Hidden field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * @param string $value Field value. + * + * @return array Ninja form hidden field data. + */ + private function hidden_field( $order, $name, $label, $required, $value ) { + return array_merge( + $this->field_template( 'hidden', $order, $name, $label, $required ), + array( + 'required' => '1', + 'default' => $value, + 'value' => $value, + ) + ); + } + + /** + * Select field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * @param array $options Response options. + * @param boolean $is_multi Field supports multiple responses. + * + * @return array Ninja form select field data. + */ + private function select_field( + $order, + $name, + $label, + $required, + $options, + $is_multi + ) { + $_options = array(); + + $l = count( $options ); + for ( $i = 0; $i < $l; $i++ ) { + $_options[] = array( + 'label' => $options[ $i ]['label'], + 'value' => $options[ $i ]['value'], + 'order' => (string) $i, + 'calc' => '', + 'selected' => 0, + 'max_options' => 0, + 'errors' => array(), + 'settingModel' => array( + 'settings' => false, + 'hide_merge_tags' => false, + 'error' => false, + 'name' => 'options', + 'type' => 'option-repeater', + 'label' => + 'Options Add New Import', + 'width' => 'full', + 'group' => '', + 'value' => array( + array( + 'label' => 'One', + 'value' => 'one', + 'calc' => '', + 'selected' => 0, + 'order' => 0, + ), + array( + 'label' => 'Two', + 'value' => 'two', + 'calc' => '', + 'selected' => 0, + 'order' => 0, + ), + array( + 'label' => 'Three', + 'value' => 'three', + 'calc' => '', + 'selected' => 0, + 'order' => 0, + ), + ), + 'columns' => array( + 'label' => array( + 'header' => __( 'Label', 'forms-bridge' ), + 'default' => '', + ), + 'value' => array( + 'header' => __( 'Value', 'forms-bridge' ), + 'default' => '', + ), + 'calc' => array( + 'header' => __( 'Calc value', 'forms-bridge' ), + 'default' => '', + ), + 'selected' => array( + 'header' => + '', + 'default' => 0, + ), + ), + ), + ); + } + + $type = $is_multi ? 'listcheckbox' : 'listselect'; + + return array_merge( + $this->field_template( $type, $order, $name, $label, $required ), + array( + 'options' => $_options, + ) + ); + } + + /** + * Text field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * + * @return array Ninja form text field data. + */ + private function text_field( $order, $name, $label, $required ) { + return array_merge( + $this->field_template( 'textbox', $order, $name, $label, $required ), + array( + 'input_limit_type' => 'characters', + 'input_limit_msg' => __( 'Character(s) left', 'forms-bridge' ), + 'drawerDisabled' => false, + 'manual_key' => false, + ) + ); + } + + /** + * Number field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * @param array $constraints Field constraints. + * + * @return array Ninja form number field data. + */ + private function number_field( + $order, + $name, + $label, + $required, + $constraints + ) { + return array_merge( + $this->field_template( 'number', $order, $name, $label, $required ), + $constraints + ); + } + + /** + * Checkbox field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * + * @return array Ninja form checkbox field data. + */ + private function checkbox_field( + $order, + $name, + $label, + $required + ) { + return array_merge( + $this->field_template( 'checkbox', $order, $name, $label, $required ), + array( + 'default_value' => 'unchecked', + 'checked_value' => 'Checked', + 'unchecked_value' => 'Unchecked', + ), + ); + } + + /** + * Textarea field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * + * @return array Ninja form textarea field data. + */ + private function textarea_field( $order, $name, $label, $required ) { + return array_merge( + $this->field_template( 'textarea', $order, $name, $label, $required ), + array( + 'input_limit_type' => 'characters', + 'input_limit_msg' => __( 'Character(s) left', 'forms-bridge' ), + 'drawerDisabled' => false, + 'manual_key' => false, + ) + ); + } + + /** + * Email field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * + * @return array Ninja form email field data. + */ + private function email_field( $order, $name, $label, $required ) { + return array_merge( + $this->field_template( 'email', $order, $name, $label, $required ), + array() + ); + } + + /** + * Phone field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * + * @return array Ninja form phone field data. + */ + private function phone_field( $order, $name, $label, $required ) { + return array_merge( + $this->field_template( 'phone', $order, $name, $label, $required ), + array() + ); + } + + /** + * Date field config generator. + * + * @param integer $order Field order. + * @param string $name Field name. + * @param string $label Field label. + * @param boolean $required Field required. + * + * @return array Ninja form date field data. + */ + private function date_field( $order, $name, $label, $required ) { + return array_merge( + $this->field_template( 'date', $order, $name, $label, $required ), + array( + 'date_format' => 'DD/MM/YYYY', + 'date_mode' => 'date_only', + 'date_default' => 1, + 'hours_24' => 0, + 'year_range_start' => '', + 'year_range_end' => '', + 'minute_increment' => 5, + ) + ); + } + + /** + * Ninja form settings data template. + * + * @param string $title Form title. + * + * @return array Form settings. + */ + private function form_template( $title ) { + return array( + 'id' => 'tmp-1', + 'settings' => array( + 'objectType' => 'Form Setting', + 'editActive' => true, + 'key' => '', + 'title' => $title, + 'clear_complete' => '1', + 'hide_complete' => '1', + 'default_label_pos' => 'above', + 'show_title' => '0', + 'wrapper_class' => '', + 'element_class' => '', + 'add_submit' => '1', + 'calculations' => array(), + 'formContentData' => array(), + 'drawerDisabled' => false, + 'unique_field_error' => __( + 'A form with this value has already been submitted.', + 'forms-bridge' + ), + 'sub_limit_msg' => __( + 'The form has reached its submission limit.', + 'forms-bridge' + ), + 'logged_in' => false, + 'not_logged_in_msg' => '', + ), + 'fields' => array(), + 'actions' => array( + array( + 'title' => '', + 'key' => '', + 'type' => 'save', + 'active' => '1', + 'created_at' => date( 'Y-m-d h:i:s' ), + 'label' => __( 'Record Submission', 'forms-bridge' ), + 'objectType' => 'Action', + 'objectDomain' => 'actions', + 'editActive' => '', + 'conditions' => array( + 'collapsed' => '', + 'process' => '1', + 'connector' => 'all', + 'when' => array( + array( + 'connector' => 'AND', + 'key' => '', + 'comparator' => '', + 'value' => '', + 'type' => 'field', + 'modelType' => 'when', + ), + ), + 'then' => array( + array( + 'key' => '', + 'trigger' => '', + 'value' => '', + 'type' => 'field', + 'modelType' => 'then', + ), + ), + 'else' => array(), + ), + 'payment_gateways' => '', + 'payment_total' => '', + 'payment_total_type' => '', + 'tag' => '', + 'to' => '', + 'email_subject' => '', + 'email_message' => '', + 'from_name' => '', + 'from_address' => '', + 'reply_to' => '', + 'email_format' => 'html', + 'cc' => '', + 'bcc' => '', + 'attach_csv' => '', + 'redirect_url' => '', + 'email_message_plain' => '', + 'fields-save-toggle' => 'save_all', + 'exception_fields' => array(), + ), + array( + 'title' => '', + 'key' => '', + 'type' => 'successmessage', + 'active' => '1', + 'created_at' => date( 'Y-m-d h:i:s' ), + 'label' => __( 'Success message', 'forms-bridge' ), + 'message' => __( + 'Thank you for filling out my form!', + 'forms-bridge' + ), + 'objectType' => 'Action', + 'objectDomain' => 'actions', + 'editActive' => '', + 'conditions' => array( + 'collapsed' => '', + 'process' => '1', + 'connector' => 'all', + 'when' => array( + array( + 'connector' => 'AND', + 'key' => '', + 'comparator' => '', + 'value' => '', + 'type' => 'field', + 'modelType' => 'when', + ), + ), + 'then' => array( + array( + 'key' => '', + 'trigger' => '', + 'value' => '', + 'type' => 'field', + 'modelType' => 'then', + ), + ), + 'else' => array(), + ), + 'payment_gateways' => '', + 'payment_total' => '', + 'payment_total_type' => '', + 'tag' => '', + 'to' => '', + 'email_subject' => '', + 'email_message' => '', + 'from_name' => '', + 'from_address' => '', + 'reply_to' => '', + 'email_format' => 'html', + 'cc' => '', + 'bcc' => '', + 'attach_csv' => '', + 'redirect_url' => '', + 'success_msg' => __( + '

Form submitted successfully.

', + 'forms-bridge' + ), + 'email_message_plain' => '', + 'exception_fields' => array(), + ), + ), + ); + } +} + +Ninja_Integration::setup(); diff --git a/forms-bridge/integrations/woo/class-woo-integration.php b/forms-bridge/integrations/woo/class-woo-integration.php new file mode 100644 index 00000000..7bc77028 --- /dev/null +++ b/forms-bridge/integrations/woo/class-woo-integration.php @@ -0,0 +1,839 @@ + 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'status' => array( 'type' => 'string' ), + 'currency' => array( 'type' => 'string' ), + 'version' => array( 'type' => 'string' ), + 'prices_include_tax' => array( 'type' => 'boolean' ), + 'date_created' => array( 'type' => 'string' ), + 'date_modified' => array( 'type' => 'string' ), + 'discount_total' => array( 'type' => 'number' ), + 'discount_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'shipping_total' => array( 'type' => 'number' ), + 'shipping_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'cart_total' => array( 'type' => 'number' ), + 'cart_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'total' => array( 'type' => 'number' ), + 'total_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'customer_id' => array( 'type' => 'integer' ), + 'order_key' => array( 'type' => 'string' ), + 'billing' => array( + 'type' => 'object', + 'properties' => array( + 'first_name' => array( 'type' => 'string' ), + 'last_name' => array( 'type' => 'string' ), + 'company' => array( 'type' => 'string' ), + 'address_1' => array( 'type' => 'string' ), + 'address_2' => array( 'type' => 'string' ), + 'city' => array( 'type' => 'string' ), + 'state' => array( 'type' => 'string' ), + 'postcode' => array( 'type' => 'string' ), + 'country' => array( 'type' => 'string' ), + 'email' => array( 'type' => 'string' ), + 'phone' => array( 'type' => 'string' ), + ), + 'additionalProperties' => true, + ), + 'shipping' => array( + 'type' => 'object', + 'properties' => array( + 'first_name' => array( 'type' => 'string' ), + 'last_name' => array( 'type' => 'string' ), + 'company' => array( 'type' => 'string' ), + 'address_1' => array( 'type' => 'string' ), + 'address_2' => array( 'type' => 'string' ), + 'city' => array( 'type' => 'string' ), + 'state' => array( 'type' => 'string' ), + 'postcode' => array( 'type' => 'string' ), + 'country' => array( 'type' => 'string' ), + 'phone' => array( 'type' => 'string' ), + ), + 'additionalProperties' => true, + ), + 'payment_method' => array( 'type' => 'string' ), + 'payment_method_title' => array( 'type' => 'string' ), + 'transaction_id' => array( 'type' => 'string' ), + 'customer_ip_address' => array( 'type' => 'string' ), + 'customer_user_agent' => array( 'type' => 'string' ), + 'created_via' => array( 'type' => 'string' ), + 'customer_note' => array( 'type' => 'string' ), + 'date_completed' => array( 'type' => 'string' ), + 'date_paid' => array( 'type' => 'string' ), + 'cart_hash' => array( 'type' => 'string' ), + 'order_stock_reduced' => array( 'type' => 'boolean' ), + 'download_permissions_granted' => array( 'type' => 'boolean' ), + 'new_order_email_sent' => array( 'type' => 'boolean' ), + 'recorded_sales' => array( 'type' => 'boolean' ), + 'recorded_coupon_usage_counts' => array( 'type' => 'boolean' ), + 'number' => array( 'type' => 'integer' ), + // 'meta_data' => [ + // 'type' => 'array', + // 'items' => [ + // 'type' => 'object', + // 'properties' => [ + // 'id' => ['type' => 'integer'], + // 'key' => ['type' => 'string'], + // 'value' => ['type' => 'string'], + // ], + // 'required' => ['id', 'key', 'value'], + // 'additionalProperties' => false, + // ], + // 'additionalItems' => true, + // ], + 'line_items' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'order_id' => array( 'type' => 'integer' ), + 'name' => array( 'type' => 'string' ), + 'product_id' => array( 'type' => 'integer' ), + 'variation_id' => array( 'type' => 'integer' ), + 'quantity' => array( 'type' => 'integer' ), + 'tax_class' => array( 'type' => 'string' ), + 'subtotal' => array( 'type' => 'number' ), + 'subtotal_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'total' => array( 'type' => 'number' ), + 'total_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'taxes' => array( + 'type' => 'object', + 'properties' => array( + 'subtotal' => array( + 'type' => 'array', + 'items' => array( 'type' => 'number' ), + 'additionalItems' => true, + ), + 'total' => array( + 'type' => 'array', + 'items' => array( 'type' => 'number' ), + 'additionalItems' => true, + ), + ), + ), + 'product' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'parent_id' => array( 'type' => 'integer' ), + 'sku' => array( 'type' => 'string' ), + 'name' => array( 'type' => 'string' ), + 'slug' => array( 'type' => 'string' ), + 'price' => array( 'type' => 'number' ), + 'sale_price' => array( 'type' => 'number' ), + 'regular_price' => array( 'type' => 'number' ), + 'stock_quantity' => array( 'type' => 'number' ), + 'stock_status' => array( 'type' => 'string' ), + ), + ), + // 'meta_data' => [ + // 'type' => 'array', + // 'items' => [ + // 'type' => 'object', + // 'properties' => [ + // 'id' => ['type' => 'integer'], + // 'key' => ['type' => 'string'], + // 'value' => ['type' => 'string'], + // ], + // ], + // ], + ), + ), + 'additionalItems' => true, + 'minItems' => 1, + ), + 'tax_lines' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'order_id' => array( 'type' => 'integer' ), + 'name' => array( 'type' => 'string' ), + 'rate_code' => array( 'type' => 'string' ), + 'rate_id' => array( 'type' => 'integer' ), + 'label' => array( 'type' => 'string' ), + 'compound' => array( 'type' => 'boolean' ), + 'tax_total' => array( 'type' => 'number' ), + 'shipping_tax_total' => array( 'type' => 'number' ), + 'rate_percent' => array( 'type' => 'number' ), + // 'meta_data' => [ + // 'type' => 'array', + // 'items' => [ + // 'type' => 'object', + // 'properties' => [ + // 'id' => ['type' => 'integer'], + // 'key' => ['type' => 'string'], + // 'value' => ['type' => 'string'], + // ], + // ], + // 'additionalItems' => true, + // ], + ), + ), + 'additionalItems' => true, + ), + 'shipping_lines' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'order_id' => array( 'type' => 'integer' ), + 'name' => array( 'type' => 'string' ), + 'method_id' => array( 'type' => 'string' ), + 'method_title' => array( 'type' => 'string' ), + 'instance_id' => array( 'type' => 'integer' ), + 'total' => array( 'type' => 'number' ), + 'total_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'tax_status' => array( 'type' => 'string' ), + 'taxes' => array( + 'type' => 'object', + 'properties' => array( + 'total' => array( 'type' => 'number' ), + 'subtotal' => array( 'type' => 'number' ), + ), + 'required' => array( 'total' ), + ), + // 'meta_data' => [ + // 'type' => 'array', + // 'items' => [ + // 'type' => 'object', + // 'properties' => [ + // 'id' => ['type' => 'integer'], + // 'key' => ['type' => 'string'], + // 'value' => ['type' => 'string'], + // ], + // ], + // ], + ), + ), + 'additionalItems' => true, + ), + 'fee_lines' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'order_id' => array( 'type' => 'integer' ), + 'name' => array( 'type' => 'string' ), + 'tax_class' => array( 'type' => 'string' ), + 'tax_status' => array( 'type' => 'string' ), + 'amount' => array( 'type' => 'number' ), + 'total' => array( 'type' => 'number' ), + 'total_tax' => array( + 'type' => 'object', + 'properties' => array( + 'amount' => array( 'type' => 'number' ), + 'rate' => array( 'type' => 'number' ), + 'percentage' => array( 'type' => 'number' ), + ), + ), + 'taxes' => array( + 'type' => 'object', + 'properties' => array( + 'total' => array( + 'type' => 'array', + 'items' => array( 'type' => 'number' ), + 'additionalItems' => true, + ), + ), + 'required' => array( 'total' ), + ), + // 'meta_data' => [ + // 'type' => 'array', + // 'items' => [ + // 'type' => 'object', + // 'properties' => [ + // 'id' => ['type' => 'integer'], + // 'key' => ['type' => 'string'], + // 'value' => ['type' => 'string'], + // ], + // ], + // ], + ), + ), + 'additionalItems' => true, + ), + 'coupon_lines' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( 'type' => 'integer' ), + 'order_id' => array( 'type' => 'integer' ), + 'name' => array( 'type' => 'string' ), + 'code' => array( 'type' => 'string' ), + 'discount' => array( 'type' => 'number' ), + 'discount_tax' => array( 'type' => 'number' ), + // 'meta_data' => [ + // 'type' => 'array', + // 'items' => [ + // 'type' => 'object', + // 'properties' => [ + // 'id' => ['type' => 'integer'], + // 'key' => ['type' => 'string'], + // 'value' => ['type' => 'string'], + // ], + // ], + // ], + ), + ), + 'additionalItems' => true, + ), + ), + 'additionalProperties' => false, + ); + + /** + * Wraps a tax amount in a tax descriptor array. + * + * @param float $tax Tax amount. + * @param float $total Tax total import. + * + * @return array Tax descriptor. + */ + private static function decorate_tax( $tax, $total ) { + try { + $tax = (float) $tax; + $rate = $tax / $total; + $rate = floor( $rate * 1000 ) / 1000; + + return array( + 'amount' => $tax, + 'rate' => $rate, + 'percentage' => $rate * 100, + ); + } catch ( TypeError | DivisionByZeroError ) { + return array( + 'amount' => 0, + 'rate' => 0, + 'percentage' => 0, + ); + } + } + + /** + * Integration initializer. Hooks the integration to woocommerce events. + */ + public function init() { + add_action( + 'woocommerce_order_status_changed', + static function ( $order_id, $old_status, $new_status ) { + $is_bridged = get_post_meta( + $order_id, + self::ORDER_BRIDGED_CF, + true + ); + + $trigger_submission = apply_filters( + 'forms_bridge_woo_trigger_submission', + '1' !== $is_bridged && 'completed' === $new_status, + $order_id, + $new_status, + $old_status, + $is_bridged + ); + + if ( $trigger_submission ) { + self::$order_id = $order_id; + + add_action( + 'forms_bridge_after_submission', + function () { + update_post_meta( + self::$order_id, + self::ORDER_BRIDGED_CF, + '1' + ); + }, + 90 + ); + + Forms_Bridge::do_submission(); + } + }, + 10, + 3 + ); + } + + /** + * Retrives the checkout form data if an order is being processed. + * + * @return array|null + */ + public function form() { + if ( empty( self::$order_id ) ) { + return; + } + + return $this->get_form_by_id( 1 ); + } + + /** + * Retrives checkout form data by ID. + * + * @param int $form_id Form ID. + * + * @return array|null Form data if ID is 1, null otherwise. + */ + public function get_form_by_id( $form_id ) { + if ( 1 !== +$form_id ) { + return null; + } + + WC()->session = new WC_Session_Handler(); + WC()->customer = new WC_Customer(); + + return apply_filters( + 'forms_bridge_form_data', + array( + '_id' => 'woo:1', + 'id' => 1, + 'title' => __( 'Woo Checkout', 'forms-bridge' ), + 'bridges' => FBAPI::get_form_bridges( 1, 'woo' ), + 'fields' => $this->serialize_form( null ), + ), + WC()->checkout, + 'woo' + ); + } + + /** + * Retrives available forms' data. + * + * @return array Collection of form data array representations. + */ + public function forms() { + return array( $this->get_form_by_id( 1 ) ); + } + + /** + * Skips form creation and return a success result. + * + * @param array $data Form template data, ignored. + * + * @return int 1, the checkout form internal ID. + */ + public function create_form( $data ) { + return 1; + } + + /** + * Skips form removal and return a success result. + * + * @param integer $form_id Form ID, ignored. + * + * @return boolean + */ + public function remove_form( $form_id ) { + return true; + } + + /** + * Retrives the current order ID. + * + * @return string|null + */ + public function submission_id() { + if ( self::$order_id ) { + return (string) self::$order_id; + } + } + + /** + * Retrives the current order data. + * + * @param boolean $raw Control if the order is serialized before exit. + * + * @return array|null + */ + public function submission( $raw ) { + if ( empty( self::$order_id ) ) { + return; + } + + return $this->serialize_order( self::$order_id ); + } + + /** + * Return an empty array as checkout form does not supports file uploads. + * + * @return array + */ + public function uploads() { + return array(); + } + + /** + * Alias to the serialize_order_fields method. + * + * @param mixed $form Ignored argument. + * + * @return array The order fields serialized as array of data. + */ + public function serialize_form( $form ) { + return $this->serialize_order_fields(); + } + + /** + * Serialize the order fields. + * + * @return array Order fields as form data. + */ + private function serialize_order_fields() { + $checkout_fields = WC()->checkout->checkout_fields; + + $fields = array(); + foreach ( + self::$order_data_schema['properties'] + as $name => $field_schema + ) { + $fields[] = self::decorate_order_field( $name, $field_schema ); + } + + foreach ( array_keys( $checkout_fields['billing'] ) as $name ) { + $name = str_replace( 'billing_', '', $name ); + if ( isset( self::$order_data_schema['billing'][ $name ] ) ) { + continue; + } + + $index = array_search( 'billing', array_column( $fields, 'name' ), true ); + + $billing_field = &$fields[ $index ]; + $billing_field['schema']['properties'][ $name ] = array( + 'type' => 'text', + ); + } + + foreach ( array_keys( $checkout_fields['shipping'] ) as $name ) { + $name = str_replace( 'shipping_', '', $name ); + if ( isset( self::$order_data_schema['shipping'][ $name ] ) ) { + continue; + } + + $index = array_search( 'shipping', array_column( $fields, 'name' ), true ); + + $fields[ $index ]['schema']['properties'][ $name ] = array( + 'type' => 'text', + ); + } + + return $fields; + } + + /** + * Decorates order fields as form data fields. + * + * @param string $name Field name. + * @param array $schema Field schema. + * + * @return array Field data. + */ + private function decorate_order_field( $name, $schema ) { + switch ( $schema['type'] ) { + case 'string': + $field_type = 'text'; + break; + case 'number': + case 'integer': + $field_type = 'number'; + break; + case 'boolean': + $field_type = 'boolean'; + break; + case 'array': + $field_type = 'select'; + break; + default: + $field_type = $schema['type']; + } + + return array( + 'id' => null, + 'name' => $name, + 'label' => $name, + 'type' => $field_type, + 'required' => true, + 'is_file' => false, + 'is_multi' => 'array' === $schema['type'], + 'conditional' => false, + 'schema' => $schema, + ); + } + + /** + * Alias to the serialize_order method. + * + * @param array $submission Ignored argument. + * @param array $form_data Ignored argument. + * + * @return array|null + */ + public function serialize_submission( $submission, $form_data ) { + return $this->serialize_order( self::$order_id ); + } + + /** + * Serialize the current WC_Order as a payload array. + * + * @param integer $order_id ID of the order to serialize. + * + * @return array + */ + public function serialize_order( $order_id ) { + $order = wc_get_order( $order_id ); + if ( empty( $order ) ) { + return; + } + + $checkout = WC()->checkout; + + $data = $order->get_data(); + unset( $data['meta_data'] ); + + $checkout_fields = $checkout->checkout_fields; + foreach ( array_keys( $checkout_fields['billing'] ) as $name ) { + $unprefixed = str_replace( 'billing_', '', $name ); + if ( ! isset( $data['billing'][ $unprefixed ] ) ) { + $data['billing'][ $unprefixed ] = $checkout->get_value( $name ); + } + } + + foreach ( array_keys( $checkout_fields['shipping'] ) as $name ) { + $unprefixed = str_replace( 'shipping_', '', $name ); + if ( ! isset( $data['shipping'][ $unprefixed ] ) ) { + $data['shipping'][ $unprefixed ] = $checkout->get_value( $name ); + } + } + + $tax_lines = array(); + foreach ( $data['tax_lines'] as $tax_line ) { + $line_data = $tax_line->get_data(); + unset( $line_data['meta_data'] ); + $tax_lines[] = $line_data; + } + + $data['tax_lines'] = $tax_lines; + + $line_items = array(); + foreach ( $data['line_items'] as $line_item ) { + $item_data = $line_item->get_data(); + unset( $item_data['meta_data'] ); + + $product = $line_item->get_product(); + $item_data['product'] = array( + 'id' => $product->get_id(), + 'parent_id' => $product->get_parent_id(), + 'slug' => $product->get_slug(), + 'sku' => $product->get_sku(), + 'name' => $product->get_name(), + 'price' => $product->get_price(), + 'sale_price' => $product->get_sale_price(), + 'regular_price' => $product->get_regular_price(), + 'stock_quantity' => $product->get_stock_quantity(), + 'stock_status' => $product->get_stock_status(), + ); + + $item_data['total_tax'] = self::decorate_tax( + $line_item['total_tax'], + $line_item['total'] + ); + + $item_data['subtotal_tax'] = self::decorate_tax( + $line_item['subtotal_tax'], + $line_item['subtotal'] + ); + + $line_items[] = $item_data; + } + + $data['line_items'] = $line_items; + + $shipping_lines = array(); + foreach ( $data['shipping_lines'] as $shipping_line ) { + $line_data = $shipping_line->get_data(); + unset( $line_data['meta_data'] ); + + $line_data['total_tax'] = self::decorate_tax( + $line_data['total_tax'], + $line_data['total'] + ); + + $shipping_lines[] = $line_data; + } + + $data['shipping_lines'] = $shipping_lines; + + $coupon_lines = array(); + foreach ( $data['coupon_lines'] ?? array() as $coupon_line ) { + $line_data = $coupon_line->get_data(); + unset( $line_data['meta_data'] ); + + $line_data['discount_tax'] = self::decorate_tax( + $line_data['discount_tax'], + $line_data['discount'] + ); + + $coupon_lines[] = $line_data; + } + + $data['coupon_lines'] = $coupon_lines; + + $fee_lines = array(); + foreach ( $data['fee_lines'] ?? array() as $fee_line ) { + $line_data = $fee_line->get_data(); + unset( $line_data['meta_data'] ); + + $line_data['total_tax'] = self::decorate_tax( + $line_data['total_tax'], + $line_data['total'] + ); + + $fee_lines[] = $line_data; + } + + $data['fee_lines'] = $fee_lines; + + $data['discount_tax'] = self::decorate_tax( + $data['discount_tax'], + $data['discount_total'] + ); + + $data['shipping_tax'] = self::decorate_tax( + $data['shipping_tax'], + $data['shipping_total'] + ); + + $data['total_tax'] = self::decorate_tax( + $data['total_tax'], + $data['total'] + ); + + $cart_total = 0; + foreach ( $data['line_items'] as $line_data ) { + $cart_total += $line_data['total']; + } + + foreach ( $data['fee_lines'] as $line_data ) { + $cart_total += $line_data['total']; + } + + $data['cart_total'] = $cart_total; + $data['cart_tax'] = self::decorate_tax( + $data['cart_tax'], + $data['cart_total'] + ); + + return rest_sanitize_value_from_schema( $data, self::$order_data_schema ); + } +} + +Woo_Integration::setup(); diff --git a/forms-bridge/integrations/wpcf7/class-wpcf7-integration.php b/forms-bridge/integrations/wpcf7/class-wpcf7-integration.php new file mode 100644 index 00000000..cbf2e283 --- /dev/null +++ b/forms-bridge/integrations/wpcf7/class-wpcf7-integration.php @@ -0,0 +1,591 @@ +serialize_form( $form ); + } + + /** + * Retrives a contact form's data by ID. + * + * @param int $form_id Form ID. + * @return array $form_data Form data. + */ + public function get_form_by_id( $form_id ) { + $form = WPCF7_ContactForm::get_instance( $form_id ); + if ( ! $form ) { + return null; + } + + return $this->serialize_form( $form ); + } + + /** + * Retrives available constact forms as form data. + * + * @return array $forms Collection of form data. + */ + public function forms() { + $forms = WPCF7_ContactForm::find( array( 'post_status', 'publish' ) ); + return array_map( + function ( $form ) { + return $this->serialize_form( $form ); + }, + $forms + ); + } + + /** + * Creates a form from the given template fields. + * + * @param array $data Form template data. + * + * @return int|null ID of the new form. + * + * @todo Fix form email attribute. + */ + public function create_form( $data ) { + if ( empty( $data['title'] ) || empty( $data['fields'] ) ) { + return; + } + + $form = $this->fields_to_form( $data['fields'] ); + $email = $this->form_email( $data['title'], $data['fields'] ); + + $contact_form = wpcf7_save_contact_form( + array( + 'title' => $data['title'], + 'locale' => get_locale(), + 'form' => $form, + 'mail' => $email, + ) + ); + + if ( ! $contact_form ) { + return; + } + + return $contact_form->id(); + } + + /** + * Removes a form by ID. + * + * @param integer $form_id Form ID. + * + * @return boolean Removal result. + */ + public function remove_form( $form_id ) { + $result = wp_delete_post( $form_id ); + return (bool) $result; + } + + /** + * Retrives the current submission ID. + * + * @return string|null + */ + public function submission_id() { + $submission = $this->submission( true ); + if ( $submission ) { + return $submission->get_posted_data_hash(); + } + } + + /** + * Retrives the current submission data. + * + * @param boolean $raw Control if the submission is serialized before exit. + * + * @return WPCF7_Submission|array Submission data. + */ + public function submission( $raw = false ) { + $submission = WPCF7_Submission::get_instance(); + + if ( ! $submission ) { + return null; + } elseif ( $raw ) { + return $submission; + } + + $form = $this->form(); + return $this->serialize_submission( $submission, $form ); + } + + /** + * Retrives the current submission uploaded files. + * + * @return array Uploaded files data. + */ + public function uploads() { + $submission = WPCF7_Submission::get_instance(); + if ( ! $submission ) { + return null; + } + + return $this->submission_uploads( $submission ); + } + + /** + * Serializes a contact form instance as array data. + * + * @param WPCF7_ContactForm $form Form instance. + * + * @return array. + */ + public function serialize_form( $form ) { + $form_id = (int) $form->id(); + $fields = array_filter( + array_map( + function ( $field ) { + return $this->serialize_field( $field ); + }, + $form->scan_form_tags() + ) + ); + + return apply_filters( + 'forms_bridge_form_data', + array( + '_id' => 'wpcf7:' . $form_id, + 'id' => $form_id, + 'title' => $form->title(), + 'bridges' => FBAPI::get_form_bridges( $form_id, 'wpcf7' ), + 'fields' => array_values( $fields ), + ), + $form, + 'wpcf7' + ); + } + + /** + * Serializes a form tags as array data. + * + * @param WPCF7_FormTag $field Form tag instance. + * + * @return array. + */ + private function serialize_field( $field ) { + if ( in_array( $field->basetype, array( 'response', 'submit', 'quiz' ), true ) ) { + return; + } + + $basetype = $field->basetype; + $type = $basetype; + + if ( 'conditional' === $basetype ) { + $type = $field->get_option( 'type' )[0]; + } + + switch ( $basetype ) { + case 'radio': + case 'checkbox': + $type = 'select'; + break; + case 'acceptance': + $type = 'checkbox'; + break; + case 'hidden': + $type = 'text'; + break; + } + + $options = array(); + if ( is_array( $field->values ) ) { + $values = $field->pipes->collect_afters(); + + $l = count( $field->raw_values ); + for ( $i = 0; $i < $l; $i++ ) { + $options[] = array( + 'value' => $values[ $i ], + 'label' => $field->labels[ $i ], + ); + } + } + + $format = 'date' === $type ? 'yyyy-mm-dd' : ''; + + return apply_filters( + 'forms_bridge_form_field_data', + array( + 'id' => $field->get_id_option(), + 'type' => $type, + 'name' => $field->raw_name, + 'label' => $field->name, + 'required' => $field->is_required(), + 'options' => $options, + 'is_file' => 'file' === $type, + 'is_multi' => $this->is_multi_field( $field ), + 'conditional' => in_array( $field->basetype, array( 'conditional', 'fileconditional' ), true ), + 'format' => $format, + 'schema' => $this->field_value_schema( $field ), + 'basetype' => $basetype, + ), + $field, + 'wpcf7' + ); + } + + /** + * Checks if a filed is multi value field. + * + * @param WPCF7_FormTag $tag Target tag instance. + * + * @return bool + */ + private function is_multi_field( $tag ) { + $type = str_replace( '*', '', $tag->type ); + + if ( 'checkbox' === $type ) { + return ! $tag->has_option( 'exclusive' ); + } + + if ( 'select' === $type ) { + return $tag->has_option( 'multiple' ); + } + + return false; + } + + /** + * Gets the field value JSON schema. + * + * @param WPCF7_FormTag $tag Tag instance. + * + * @return array JSON schema of the value of the field. + */ + private function field_value_schema( $tag ) { + $type = str_replace( '*', '', $tag->type ); + + switch ( $type ) { + case 'text': + case 'textarea': + case 'date': + case 'email': + case 'url': + case 'quiz': + case 'radio': + return array( 'type' => 'string' ); + case 'select': + if ( $tag->has_option( 'multiple' ) ) { + $items = array(); + + $l = count( $tag->values ); + for ( $i = 0; $i < $l; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + 'minItems' => $tag->is_required() ? 1 : 0, + 'maxItems' => count( $tag->values ), + ); + } + + return array( 'type' => 'string' ); + case 'checkbox': + if ( $tag->has_option( 'exclusive' ) ) { + return array( 'type' => 'string' ); + } + + $items = array(); + + $l = count( $tag->values ); + for ( $i = 0; $i < $l; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + 'minItems' => $tag->is_required() ? 1 : 0, + 'maxItems' => count( $tag->values ), + ); + case 'file': + case 'files': + return; + case 'acceptance': + return array( 'type' => 'boolean' ); + case 'number': + return array( 'type' => 'number' ); + default: + return array( 'type' => 'string' ); + } + } + + /** + * Serializes the form's submission data. + * + * @param WPCF7_Submission $submission Submission instance. + * @param array $form_data Form data. + * + * @return array Submission data. + */ + public function serialize_submission( $submission, $form_data ) { + $data = $submission->get_posted_data(); + + foreach ( $data as $key => $val ) { + $index = array_search( $key, array_column( $form_data['fields'], 'name' ), true ); + $field = $form_data['fields'][ $index ]; + + if ( is_array( $val ) && ! $field['is_multi'] ) { + $data[ $key ] = $val[0]; + $val = $data[ $key ]; + } + + if ( 'hidden' === $field['basetype'] ) { + $number_val = (float) $val; + if ( strval( $number_val ) === $val ) { + $data[ $key ] = $number_val; + } else { + $data[ $key ] = $val; + } + } elseif ( 'number' === $field['basetype'] ) { + $data[ $key ] = (float) $val; + } elseif ( 'file' === $field['basetype'] ) { + unset( $data[ $key ] ); + } elseif ( 'acceptance' === $field['basetype'] ) { + $data[ $key ] = (bool) $val; + } + } + + return $data; + } + + /** + * Gets submission uploaded files. + * + * @param WPCF7_Submission $submission Submission instance. + * + * @return array Uploaded files data. + */ + protected function submission_uploads( $submission ) { + $uploads = array(); + $uploads = $submission->uploaded_files(); + foreach ( $uploads as $file_name => $paths ) { + if ( ! empty( $paths ) ) { + $is_multi = count( $paths ) > 1; + + $uploads[ $file_name ] = array( + 'path' => $is_multi ? $paths : $paths[0], + 'is_multi' => $is_multi, + ); + } + } + + return $uploads; + } + + /** + * Gets form fields from a template and return a contact form content string. + * + * @param array $fields Form data fields array. + * + * @return string Form content. + */ + private function fields_to_form( $fields ) { + $form = ''; + foreach ( $fields as $field ) { + if ( 'hidden' === $field['type'] ) { + if ( isset( $field['value'] ) ) { + if ( is_bool( $field['value'] ) ) { + $field['value'] = $field['value'] ? '1' : '0'; + } + + $form .= $this->field_to_tag( $field ) . "\n\n"; + } + } else { + $form .= "\n\n"; + } + } + + $form .= sprintf( '[submit "%s"]', __( 'Submit', 'forms-bridge' ) ); + return $form; + } + + /** + * Gets a field template data and returns a form tag string. + * + * @param array $field Field data. + * + * @return string. + */ + private function field_to_tag( $field ) { + if ( isset( $field['value'] ) ) { + $type = 'hidden'; + } else { + $type = sanitize_text_field( $field['type'] ); + + switch ( $type ) { + case 'checkbox': + $type = 'acceptance'; + break; + case 'select': + if ( $field['is_multi'] ?? false ) { + $type = 'checkbox'; + } + + break; + } + + if ( ( $field['required'] ?? false ) && 'hidden' !== $type ) { + $type .= '*'; + } + } + + $name = sanitize_text_field( $field['name'] ); + $tag = "[{$type} {$name} "; + + foreach ( $field as $key => $val ) { + if ( ! in_array( $key, array( 'name', 'type', 'value', 'required', 'label' ), true ) ) { + $key = sanitize_text_field( $key ); + $val = trim( sanitize_text_field( $val ) ); + $tag .= "{$key}:{$val} "; + } + } + + $value = null; + + if ( strstr( $type, 'select' ) !== false || strstr( $type, 'checkbox' ) !== false ) { + $options = array(); + foreach ( (array) $field['options'] as $opt ) { + $value = $opt['value']; + $label = $opt['label'] ?? $value; + $options[] = $label . '|' . $value; + } + + $value = implode( '" "', $options ); + } elseif ( ! empty( $field['value'] ) ) { + $value = sanitize_text_field( (string) $field['value'] ); + } + + if ( $value ) { + $tag .= "\"{$value}\""; + } + + return $tag . ']'; + } + + /** + * Serialize the email data of a contact form. + * + * @param string $title Form title. + * @param array $fields Form fields. + */ + private function form_email( $title, $fields ) { + $site_url = get_option( 'siteurl' ); + $host = wp_parse_url( $site_url )['host'] ?? 'example.coop'; + + $email_index = array_search( 'email', array_column( $fields, 'type' ), true ); + if ( $email_index ) { + $replay_to = 'Replay-To: [' . $fields[ $email_index ]['name'] . ']'; + } else { + $replay_to = ''; + } + + $body = __( + "This are the responses to the contact form:\n\n", + 'forms-bridge' + ); + + foreach ( $fields as $field ) { + $label = $field['label'] ?? $field['name']; + $body .= ' * ' . esc_html( $label ) . ': [' . $field['name'] . "]\n"; + } + + $notice = sprintf( + /* translators: 1: blog name, 2: blog URL */ + __( + 'This is a notification that a contact form was submitted on your website (%1$s %2$s).', + 'forms-bridge' + ), + '[_site_title]', + '[_site_url]' + ); + + $body .= "\n---\n{$notice}"; + + return array( + 'recipient' => '[_site_admin_email]', + 'sender' => "[_site_title] ", + 'subject' => "[_site_title] \"{$title}\"", + 'additional_headers' => $replay_to, + 'body' => $body, + 'attachments' => '', + ); + } +} + +WPCF7_Integration::setup(); diff --git a/forms-bridge/integrations/wpforms/class-wpforms-integration.php b/forms-bridge/integrations/wpforms/class-wpforms-integration.php new file mode 100644 index 00000000..421c35f3 --- /dev/null +++ b/forms-bridge/integrations/wpforms/class-wpforms-integration.php @@ -0,0 +1,1093 @@ +obj( 'form' )->get( $form_id ); + + if ( ! $form ) { + return; + } + + return $this->serialize_form( $form ); + } + + /** + * Retrives a WPForms_Form_Handler's data by ID. + * + * @param int $form_id ID of the form. + * + * @return array. + */ + public function get_form_by_id( $form_id ) { + $form = wpforms()->obj( 'form' )->get( $form_id ); + + if ( ! $form ) { + return null; + } + + return $this->serialize_form( $form ); + } + + /** + * Retrives available form instances' data. + * + * @return array Collection of forms data. + */ + public function forms() { + $forms = array_filter( (array) wpforms()->obj( 'form' )->get() ); + + $forms_data = array(); + foreach ( $forms as $form ) { + $forms_data[] = $this->serialize_form( $form ); + } + + return $forms_data; + } + + /** + * Creates a form from the given template fields. + * + * @param array $data Form template data. + * + * @return int|null ID of the new form. + */ + public function create_form( $data ) { + $form_title = esc_html( $data['title'] ); + $title_query = new WP_Query( + array( + 'post_type' => 'wpforms', + 'title' => $form_title, + 'posts_per_page' => 1, + 'fields' => 'ids', + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'no_found_rows' => true, + ) + ); + $title_exists = $title_query->post_count > 0; + + add_filter( + 'wpforms_create_form_args', + function ( $args, $create_data ) use ( $data ) { + if ( 'forms-bridge' === $create_data['template'] ) { + $args['post_content'] = $this->encode_form_data( $data ); + } + return $args; + }, + 99, + 2 + ); + + $form_id = wpforms() + ->obj( 'form' ) + ->add( + esc_html( $data['title'] ), + array(), + array( + 'template' => 'forms-bridge', + 'category' => 'all', + 'subcategory' => 'all', + ) + ); + + if ( $title_exists ) { + // $form_title = $form_title . ' (ID #' . $form_id . ')'; + remove_action( 'post_updated', 'wp_save_post_revision' ); + wp_update_post( + array( + 'ID' => $form_id, + 'post_title' => $form_title . ' (ID #' . $form_id . ')', + ) + ); + add_action( 'post_updated', 'wp_save_post_revision' ); + } + + $form = wpforms()->obj( 'form' )->get( $form_id ); + $form_data = wpforms_decode( $form->post_content ); + $form_data['id'] = $form_id; + $form_data['settings']['form_title'] = $form_title; + + wpforms() + ->obj( 'form' ) + ->update( $form_id, $form_data, array( 'context' => 'save_form' ) ); + + return $form_id; + } + + /** + * Removes a form by ID. + * + * @param integer $form_id Form ID. + * + * @return boolean Removal result. + */ + public function remove_form( $form_id ) { + $post = wp_delete_post( $form_id ); + return boolval( $post->ID ?? false ); + } + + public function submission_id() { + $submission = $this->submission( true ); + if ( $submission ) { + return $submission['entry_id']; + } + } + + /** + * Retrives the current form submission data. + * + * @param boolean $raw Control if the submission is serialized before exit. + * + * @return array Submission data. + */ + public function submission( $raw = false ) { + $form = $this->form(); + + if ( ! $form ) { + return; + } + + if ( empty( self::$submission ) ) { + return; + } elseif ( $raw ) { + return self::$submission; + } + + return $this->serialize_submission( self::$submission, $form ); + } + + /** + * Retrives the current submission uploaded files. + * + * @return array Uploaded files data. + */ + public function uploads() { + $submission = self::$submission; + if ( ! $submission ) { + return null; + } + + return $this->submission_uploads( $submission, $this->form() ); + } + + /** + * Serializes a wp form post instance as array data. + * + * @param WP_Post $form Form post instance. + * + * @return array + */ + public function serialize_form( $form ) { + $data = $form instanceof WP_Post + ? wpforms_decode( $form->post_content ) + : $form; + + $form_id = isset( $data['id'] ) ? (int) $data['id'] : $form->ID; + + $fields = array(); + foreach ( $data['fields'] as $field_data ) { + $field = $this->serialize_field( $field_data, $fields, $data['fields'] ); + + if ( $field ) { + $fields[] = $field; + } + } + + return apply_filters( + 'forms_bridge_form_data', + array( + '_id' => 'wpforms:' . $form_id, + 'id' => $form_id, + 'title' => $data['settings']['form_title'] ?? '', + 'bridges' => FBAPI::get_form_bridges( $form_id, 'wpforms' ), + 'fields' => $fields, + ), + $data, + 'wpforms' + ); + } + + /** + * Serializes a field as array data. + * + * @param array $field Field config. + * @param array[] $fields List of serialized fields. + * @param array[] $all_fields Complete list of form data fields. + * + * @return array + */ + private function serialize_field( $field, $fields = array(), $all_fields = array() ) { + if ( + in_array( + $field['type'], + array( + 'submit', + 'pagebreak', + 'layout', + 'captcha', + 'content', + 'entry-preview', + 'html', + 'divider', + ), + true + ) + ) { + return; + } + + $repeaters = array(); + foreach ( $fields as $candidate ) { + if ( 'repeater' === $candidate['type'] ) { + $repeaters[] = $candidate; + } + } + + $fields_in_repeater = array(); + foreach ( $repeaters as $repeater ) { + foreach ( $repeater['children'] as $child ) { + $fields_in_repeaters[] = $child['id']; + } + } + + $children = array(); + $children_ids = array(); + if ( 'repeater' === $field['type'] ) { + foreach ( $field['columns'] as $column ) { + foreach ( $column['fields'] as $field_id ) { + $children_ids[] = $field_id; + } + } + + foreach ( $all_fields as $candidate ) { + if ( in_array( $candidate['id'], $children_ids, true ) ) { + $children[] = $this->serialize_field( $candidate ); + } + } + } elseif ( in_array( $field['id'], $fields_in_repeater, true ) ) { + return; + } + + $format = $field['date_format'] ?? ''; + if ( $format ) { + $format = + array( + 'd/m/Y' => 'dd/mm/yyyy', + 'm/d/Y' => 'mm/dd/yyyy', + )[ $format ] ?? ''; + } + + $options = array(); + if ( ! empty( $field['choices'] ) ) { + foreach ( $field['choices'] as $choice ) { + $options[] = array( + 'label' => $choice['label'], + 'value' => $choice['value'] ?: $choice['label'], + ); + } + } + + switch ( $field['type'] ) { + case 'url': + $type = 'url'; + break; + case 'email': + $type = 'email'; + break; + case 'phone': + $type = 'tel'; + break; + case 'radio': + case 'payment-select': + case 'payment-multiple': + case 'payment-checkbox': + case 'select': + case 'checkbox': + $type = 'select'; + break; + case 'number-slider': + case 'number': + $type = 'number'; + break; + case 'file-upload': + $type = 'file'; + break; + case 'repeater': + $type = 'mixed'; + break; + case 'date-time': + $type = 'date'; + break; + case 'textarea': + $type = 'textarea'; + break; + case 'name': + case 'text': + case 'password': + case 'payment-total': + case 'payment-single': + case 'address': + default: + $type = 'text'; + break; + } + + return apply_filters( + 'forms_bridge_form_field_data', + array( + 'id' => (int) ( $field['id'] ?? 0 ), + 'type' => $type, + 'name' => trim( $field['label'] ?? '' ), + 'label' => trim( $field['label'] ?? '' ), + 'required' => '1' === ( $field['required'] ?? '' ), + 'options' => $options, + 'is_file' => 'file-upload' === $field['type'], + 'is_multi' => $this->is_multi_field( $field ), + 'conditional' => false, + 'format' => $format, + 'children' => array_values( $children ), + 'schema' => $this->field_value_schema( $field, $children ), + 'basetype' => $field['type'], + ), + $field, + 'wpforms' + ); + } + + /** + * Checks if a filed is multi value field. + * + * @param array $field Target field instance. + * + * @return boolean + */ + private function is_multi_field( $field ) { + if ( 'checkbox' === $field['type'] || 'repeater' === $field['type'] ) { + return true; + } + + if ( 'select' === $field['type'] && ( $field['multiple'] ?? false ) ) { + return true; + } + + if ( + 'file-upload' === $field['type'] && + '1' !== ( $field['max_file_number'] ?? 1 ) + ) { + return true; + } + + return false; + } + + /** + * Gets the field value JSON schema. + * + * @param array $field Field instance. + * @param array $children Children fields. + * + * @return array JSON schema of the value of the field. + */ + private function field_value_schema( $field, $children = array() ) { + switch ( $field['type'] ) { + case 'name': + case 'email': + case 'text': + case 'textarea': + case 'payment-total': + case 'payment-single': + case 'radio': + case 'password': + case 'url': + return array( 'type' => 'string' ); + case 'number-slider': + case 'number': + return array( 'type' => 'number' ); + case 'payment-select': + case 'payment-multiple': + case 'payment-checkbox': + case 'select': + if ( $field['multiple'] ?? false ) { + $items = array(); + $count = count( $field['choices'] ); + for ( $i = 0; $i < $count; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + ); + } + + return array( 'type' => 'string' ); + case 'checkbox': + $items = array(); + $count = count( $field['choices'] ); + for ( $i = 0; $i < $count; $i++ ) { + $items[] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'array', + 'items' => $items, + 'additionalItems' => false, + ); + case 'date-time': + if ( 'date-time' === $field['format'] ) { + return array( + 'type' => 'object', + 'properties' => array( + 'date' => array( 'type' => 'string' ), + 'time' => array( 'type' => 'string' ), + ), + ); + } + + return array( 'type' => 'string' ); + case 'address': + $properties = array( + 'address1' => array( 'type' => 'string' ), + 'city' => array( 'type' => 'string' ), + 'state' => array( 'type' => 'string' ), + ); + + if ( ( $field['address2_hide'] ?? '0' ) !== '1' ) { + $properties['address2'] = array( 'type' => 'string' ); + } + + if ( ( $field['postal_hide'] ?? '0' ) !== '1' ) { + $properties['postal'] = array( 'type' => 'string' ); + } + + if ( + ( $field['country_hide'] ?? '0' ) !== '1' && + 'us' !== $field['scheme'] + ) { + $properties['country'] = array( 'type' => 'string' ); + } + + return array( + 'type' => 'object', + 'properties' => $properties, + ); + case 'file-upload': + return; + case 'repeater': + $properties = array_reduce( + $children, + function ( $props, $field ) { + $props[ $field['label'] ] = $field['schema']; + return $props; + }, + array() + ); + + return array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => $properties, + ), + 'additionalItems' => true, + ); + default: + return array( 'type' => 'string' ); + } + } + + /** + * Serializes the form's submission data. + * + * @param array $submission Submission data. + * @param array $form_data Form data. + * + * @return array + */ + public function serialize_submission( $submission, $form_data ) { + $data = array(); + + $repeaters = array(); + foreach ( $form_data['fields'] as $field ) { + if ( 'repeater' === $field['basetype'] ) { + $repeaters[] = $field; + } + } + + $fields_in_repeaters = array(); + foreach ( $repeaters as $repeater ) { + foreach ( $repeater['children'] as $child ) { + $fields_in_repeaters[ $child['id'] ] = array(); + } + } + + foreach ( $submission['fields'] as $field ) { + if ( 'file-upload' === $field['type'] ) { + continue; + } + + $i = array_search( + trim( $field['name'] ), + array_column( $form_data['fields'], 'name' ), + true + ); + + $field_data = $form_data['fields'][ $i ]; + + $field['id'] = preg_replace( '/_\d+$/', '', $field['id'] ); + if ( isset( $fields_in_repeaters[ $field['id'] ] ) ) { + $fields_in_repeaters[ $field['id'] ][] = $field; + } else { + $value = $this->format_value( $field, $field_data ); + + $data[ $field_data['name'] ] = $value; + } + } + + foreach ( $repeaters as $repeater ) { + $repeater_data = array(); + foreach ( $repeater['children'] as $child ) { + foreach ( $fields_in_repeaters as $id => $fields ) { + if ( $id == $child['id'] ) { + break; + } + } + + $l = count( $fields ); + for ( $i = 0; $i < $l; $i++ ) { + $field = $fields[ $i ]; + $value = $this->format_value( $field, $child ); + $datum = + count( $repeater_data ) > $i ? $repeater_data[ $i ] : array(); + $datum[ $child['name'] ] = $value; + $repeater_data[ $i ] = $datum; + } + } + + $data[ $repeater['name'] ] = $repeater_data; + } + + return $data; + } + + private function format_value( $field, $field_data ) { + if ( strstr( $field_data['basetype'], 'payment' ) ) { + $field['value'] = html_entity_decode( $field['value'] ); + } + + if ( 'hidden' === $field_data['basetype'] ) { + $number_val = (float) $field['value']; + if ( strval( $number_val ) === $field['value'] ) { + return $number_val; + } + } + + if ( + 'number' === $field_data['basetype'] || + 'number-slider' === $field_data['basetype'] + ) { + if ( isset( $field['amount'] ) ) { + $value = (float) $field['amount']; + if ( isset( $field['currency'] ) ) { + $value .= ' ' . $field['currency']; + } + + return $value; + } else { + return (float) preg_replace( '/[^0-9\.,]/', '', $field['value'] ); + } + } + + if ( + 'select' === $field_data['basetype'] || + 'checkbox' === $field_data['basetype'] + ) { + if ( $field_data['is_multi'] ) { + return array_map( + function ( $value ) { + return trim( $value ); + }, + explode( "\n", $field['value'] ) + ); + } + } + + if ( 'address' === $field_data['basetype'] ) { + $post_values = $_POST['wpforms']['fields'][ $field['id'] ] ?? array(); + $field_values = array(); + foreach ( array_keys( $field_data['schema']['properties'] ) as $prop ) { + $field_values[ $prop ] = $post_values[ $prop ] ?? ''; + } + + return $field_values; + } + + if ( 'date-time' === $field_data['basetype'] ) { + if ( 'object' === $field_data['schema']['type'] ) { + $post_values = $_POST['wpforms']['fields'][ $field['id'] ]; + return array( + 'date' => $post_values['date'], + 'time' => $post_values['time'], + ); + } + } + + return (string) $field['value']; + } + + /** + * Gets submission uploaded files. + * + * @param object $submission Submission data. + * @param array $form_data Form data. + * + * @return array Uploaded files data. + */ + protected function submission_uploads( $submission, $form_data ) { + $form_fields = wpforms_get_form_fields( + (int) $form_data['id'], + array( + 'file-upload', + ) + ); + + if ( empty( $form_fields ) ) { + return array(); + } + + $fields = array(); + foreach ( $form_fields as $form_field ) { + foreach ( $submission['fields'] as $submission_field ) { + if ( $submission_field['id'] == $form_field['id'] ) { + $fields[] = $submission_field; + } + } + } + + $uploads = array(); + foreach ( $fields as $field ) { + if ( empty( $field['value_raw'] ) ) { + continue; + } + + $is_multi = count( $field['value_raw'] ?: array() ) > 1; + $paths = WPForms_Field_File_Upload::get_entry_field_file_paths( + $form_data['id'], + $field + ); + + $uploads[ $field['name'] ] = array( + 'path' => $is_multi ? $paths : $paths[0], + 'is_multi' => $is_multi, + ); + } + + return $uploads; + } + + /** + * Formats the bridge's form data to be used as the post_content of a wpform post + * type and encode it as json. + * + * @param array $data Bridge's template form data. + * + * @return string Encoded and decorated form data. + */ + private function encode_form_data( $data ) { + $wp_fields = array(); + + $l = count( $data['fields'] ); + for ( $i = 0; $i < $l; $i++ ) { + $id = $i + 1; + $field = $data['fields'][ $i ]; + + $args = array( $id, $field['name'], $field['required'] ?? false ); + switch ( $field['type'] ) { + case 'textarea': + $wp_fields[ strval( $id ) ] = $this->textarea_field( ...$args ); + break; + case 'hidden': + if ( isset( $field['value'] ) ) { + if ( is_bool( $field['value'] ) ) { + $field['value'] = $field['value'] ? '1' : '0'; + } + + $args[] = (string) $field['value']; + $wp_fields[ strval( $id ) ] = $this->hidden_field( ...$args ); + } + + break; + case 'select': + $args[] = $field['options'] ?? array(); + $args[] = $field['is_multi'] ?? false; + $wp_fields[ strval( $id ) ] = $this->select_field( ...$args ); + break; + case 'checkbox': + $wp_fields[ strval( $id ) ] = $this->checkbox_field( ...$args ); + break; + case 'file': + $args[] = $field['filetypes'] ?? ''; + $wp_fields[ strval( $id ) ] = $this->file_field( ...$args ); + break; + case 'date': + $wp_fields[ strval( $id ) ] = $this->date_field( ...$args ); + break; + case 'text': + $wp_fields[ strval( $id ) ] = $this->text_field( ...$args ); + break; + case 'number': + $constraints = array( + 'default_value' => floatval( $field['default'] ?? 0 ), + 'min' => $field['min'] ?? '', + 'max' => $field['max'] ?? '', + 'step' => $field['step'] ?? '1', + ); + + $args[] = $constraints; + $wp_fields[ strval( $id ) ] = $this->number_field( ...$args ); + break; + case 'tel': + $wp_fields[ strval( $id ) ] = $this->field_template( + 'phone', + ...$args + ); + break; + case 'url': + case 'email': + default: + $wp_fields[ strval( $id ) ] = $this->field_template( + $field['type'], + ...$args + ); + } + } + + return wpforms_encode( + array( + 'fields' => $wp_fields, + 'field_id' => $id + 1, + 'settings' => array( + 'form_desc' => '', + 'submit_text' => esc_html__( 'Submit', 'forms-bridge' ), + 'submit_text_processing' => esc_html__( + 'Sending...', + 'forms-bridge' + ), + 'antispam_v3' => '1', + 'notification_enable' => '1', + 'notifications' => array( + '1' => array( + 'email' => '{admin_email}', + 'replyto' => '', + 'message' => '{all_fields}', + ), + ), + 'confirmations' => array( + '1' => array( + 'type' => 'message', + 'message' => esc_html__( + 'Thanks for contacting us! We will be in touch with you shortly.', + 'forms-bridge' + ), + 'message_scroll' => '1', + ), + ), + 'ajax_submit' => '1', + ), + 'meta' => array( 'template' => 'forms-bridge' ), + ) + ); + } + + /** + * Returns a default field array data. Used as template for the field creation methods. + * + * @param string $type Field type. + * @param int $id Field id. + * @param string $label Field label. + * @param boolean $required Is field required. + * + * @return array + */ + private function field_template( $type, $id, $label, $required ) { + return array( + 'id' => (string) $id, + 'type' => $type, + 'label' => esc_html( $label ), + 'required' => $required ? '1' : '0', + 'size' => 'medium', + 'description' => '', + 'placeholder' => '', + 'css' => '', + ); + } + + /** + * Returns a valid text field data. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * + * @return array + */ + private function text_field( $id, $name, $required ) { + return array_merge( + $this->field_template( 'text', $id, $name, $required ), + array( + 'limit_count' => '1', + 'limit_mode' => 'characters', + ) + ); + } + + /** + * Returns a valid single checkbox field data. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * + * @return array + */ + private function checkbox_field( $id, $name, $required ) { + return array_merge( + $this->field_template( 'checkbox', $id, $name, $required ), + array( + 'choices' => array( + array( + 'label' => $name, + 'value' => __( 'Checked', 'forms-bridge' ), + 'image' => '', + 'icon' => '', + 'icon_style' => 'regular', + ), + ), + 'choices_limit' => 1, + ) + ); + } + + /** + * Returns a valid number field data. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * @param array $constraints Field constraints. + * + * @return array + */ + private function number_field( $id, $name, $required, $constraints ) { + return array_merge( + $this->field_template( 'number-slider', $id, $name, $required ), + $constraints + ); + } + + /** + * Returns a valid text field data. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * + * @return array + */ + private function date_field( $id, $name, $required ) { + return array_merge( + $this->field_template( 'date-time', $id, $name, $required ), + array( + 'format' => 'date', + 'date_type' => 'datepicker', + 'date_format' => 'd/m/Y', + ) + ); + } + + /** + * Returns a valid textarea field data. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * + * @return array + */ + private function textarea_field( $id, $name, $required ) { + return array_merge( + $this->field_template( 'textarea', $id, $name, $required ), + array( + 'limit_count' => '1', + 'limit_mode' => 'characters', + ) + ); + } + + /** + * Returns a valid multi select field data, as a select field if is single, as + * a checkbox field if is multiple. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * @param array $options Options data. + * @param boolean $is_multi Is field multi value. + * + * @return array + */ + private function select_field( $id, $name, $required, $options, $is_multi ) { + $choices = array_map( + function ( $opt ) { + return array( + 'label' => esc_html( $opt['label'] ), + 'value' => sanitize_text_field( $opt['value'] ?: $opt['label'] ), + 'image' => '', + 'icon' => '', + 'icon_style' => 'regular', + ); + }, + $options + ); + + if ( $is_multi ) { + return array_merge( + $this->field_template( 'checkbox', $id, $name, $required ), + array( + 'choices' => $choices, + 'choices_images_style' => 'modern', + 'choices_icon_color' => '#066aab', + 'choices_icon_size' => 'large', + 'choices_icon_style' => 'default', + 'choices_limit' => '', + 'dynamic_choices' => '', + ) + ); + } else { + return array_merge( + $this->field_template( 'select', $id, $name, $required ), + array( + 'choices' => $choices, + 'dynamic_choices' => '', + 'style' => 'classic', + ) + ); + } + } + + /** + * Returns a valid hidden field data. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * @param string $value Field's default value. + * + * @return array + */ + private function hidden_field( $id, $name, $required, $value ) { + $field = array_merge( + $this->field_template( 'hidden', $id, $name, $required ), + array( + 'label_hide' => '1', + 'label_disable' => '1', + 'default_value' => $value, + ) + ); + + unset( $field['description'] ); + unset( $field['required'] ); + unset( $field['placeholder'] ); + + return $field; + } + + /** + * Returns a valid file-upload field data. + * + * @param int $id Field id. + * @param string $name Field name (label). + * @param boolean $required Is field required. + * @param string $filetypes String with allowed file extensions separated by commas. + * + * @return array + */ + private function file_field( $id, $name, $required, $filetypes ) { + return array_merge( + $this->field_template( 'file-upload', $id, $name, $required ), + array( + 'max_size' => '', + 'max_file_number' => '1', + 'style' => 'modern', + 'extensions' => $filetypes, + ) + ); + } +} + +WPForms_Integration::setup(); diff --git a/languages/forms-bridge.pot b/forms-bridge/languages/forms-bridge.pot similarity index 100% rename from languages/forms-bridge.pot rename to forms-bridge/languages/forms-bridge.pot diff --git a/forms-bridge/license.txt b/forms-bridge/license.txt new file mode 100644 index 00000000..eb934c44 --- /dev/null +++ b/forms-bridge/license.txt @@ -0,0 +1,359 @@ +Forms Bridge WordPress Plugin, 2024 Còdec Solucions Digitals, SCCL. +Forms Bridge is distributed under the terms of the GNU GPL + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/forms-bridge/migrations/3.0.0.php b/forms-bridge/migrations/3.0.0.php new file mode 100644 index 00000000..bde40baa --- /dev/null +++ b/forms-bridge/migrations/3.0.0.php @@ -0,0 +1,30 @@ +bridge['workflow'] ?? array(); + } + } + } + + $bridge_data['workflow'] = $bridge_data['workflow'] ?? array(); + + if ( ! isset( $bridge_data['mutations'] ) ) { + $mappers = $bridge_data['mappers'] ?? array(); + $mutations = array( $mappers ); + } else { + $mutations = $bridge_data['mutations']; + } + + $count = count( $bridge_data['workflow'] ); + for ( $i = count( $mutations ); $i <= $count; $i++ ) { + $mutations[] = array(); + } + + $bridge_data['mutations'] = $mutations; + unset( $bridge_data['mappers'] ); + } + + update_option( $option, $data ); + } +} + +forms_bridge_migration_310(); diff --git a/forms-bridge/migrations/4.0.0.php b/forms-bridge/migrations/4.0.0.php new file mode 100644 index 00000000..b6544ce5 --- /dev/null +++ b/forms-bridge/migrations/4.0.0.php @@ -0,0 +1,261 @@ + $credential ) { + $backend = FBAPI::get_backend( $name ); + if ( ! $backend ) { + continue; + } + + $headers = $backend->headers; + if ( isset( $headers['api_user'], $headers['token'] ) ) { + $backend_data = array( + 'name' => $backend->name, + 'base_url' => $backend->base_url, + 'headers' => array(), + ); + + $credential_data = array( + 'name' => $credential ?: $backend->name, + 'schema' => 'Token', + 'client_id' => $headers['api_user'], + 'client_secret' => $headers['token'], + ); + + unset( $headers['api_user'] ); + unset( $headers['token'] ); + + foreach ( $headers as $name => $value ) { + $backend_data['headers'][] = array( + 'name' => $name, + 'value' => $value, + ); + } + + $backend_data['credential'] = $credential_data['name']; + FBAPI::save_backend( $backend_data ); + $credentials[] = $credential_data; + } + } + } elseif ( 'forms-bridge_mailchimp' === $option ) { + foreach ( $backends as $name => $credential ) { + $backend = FBAPI::get_backend( $name ); + if ( ! $backend ) { + continue; + } + + $headers = $backend->headers; + if ( isset( $headers['api-key'] ) ) { + $backend_data = array( + 'name' => $backend->name, + 'base_url' => $backend->base_url, + 'headers' => array(), + ); + + $credential_data = array( + 'name' => $credential ?: $backend->name, + 'schema' => 'Basic', + 'client_id' => 'forms-bridge', + 'client_secret' => $headers['api-key'], + ); + + unset( $headers['api-key'] ); + + foreach ( $headers as $name => $value ) { + $backend_data['headers'][] = array( + 'name' => $name, + 'value' => $value, + ); + } + + $backend_data['credential'] = $credential_data['name']; + FBAPI::save_backend( $backend_data ); + $credentials[] = $credential_data; + } + } + } elseif ( 'forms-bridge_financoop' === $option ) { + foreach ( $backends as $name => $credential ) { + $backend = FBAPI::get_backend( $name ); + if ( ! $backend ) { + continue; + } + + $headers = $backend->headers; + if ( + isset( + $headers['X-Odoo-Db'], + $headers['X-Odoo-Username'], + $headers['X-Odoo-Api-Key'] + ) + ) { + $backend_data = array( + 'name' => $backend->name, + 'base_url' => $backend->base_url, + 'headers' => array(), + ); + + $credential_data = array( + 'name' => $credential ?: $backend->name, + 'schema' => 'RPC', + 'client_id' => $headers['X-Odoo-Username'], + 'client_secret' => $headers['X-Odoo-Api-Key'], + 'database' => $headers['X-Odoo-Db'], + ); + + unset( $headers['X-Odoo-Db'] ); + unset( $headers['X-Odoo-Username'] ); + unset( $headers['X-Odoo-Api-Key'] ); + + foreach ( $headers as $name => $value ) { + $backend_data['headers'][] = array( + 'name' => $name, + 'value' => $value, + ); + } + + $backend_data['credential'] = $credential_data['name']; + FBAPI::save_backend( $backend_data ); + $credentials[] = $credential_data; + } + } + } elseif ( 'forms-bridge_odoo' === $option ) { + foreach ( $data['credentials'] ?? array() as $credential ) { + $credentials[] = array( + 'name' => $credential['name'], + 'schema' => 'RPC', + 'client_id' => $credential['user'], + 'client_secret' => $credential['password'], + 'database' => $credential['database'], + ); + } + + unset( $data['credentials'] ); + + foreach ( $backends as $name => $credential ) { + $backend = FBAPI::get_backend( $name ); + if ( ! $backend ) { + continue; + } + + $backend_data = $backend->data(); + $backend_data['credential'] = $credential; + FBAPI::save_backend( $backend_data ); + } + } elseif ( 'forms-bridge_zoho' === $option ) { + unset( $data['credentials'] ); + } elseif ( 'forms-bridge_bigin' === $option ) { + unset( $data['credentials'] ); + } elseif ( 'forms-bridge_gsheets' === $option ) { + unset( $data['credentials'] ); + } + + update_option( $option, $data ); + } + + $rest = get_option( 'forms-bridge_rest-api' ); + if ( $rest && is_array( $rest ) ) { + add_option( 'forms-bridge_rest', $rest ); + delete_option( 'forms-bridge_rest-api' ); + } + + $registry = get_option( 'forms_bridge_addons' ); + if ( $registry && is_array( $registry ) ) { + $registry['rest'] = $registry['rest-api']; + unset( $registry['rest-api'] ); + update_option( 'forms_bridge_addons', $registry ); + } + + $http = get_option( 'http-bridge_general' ); + if ( $http && is_array( $http ) ) { + $http['credentials'] = $credentials; + update_option( 'http-bridge_general', $http ); + } +} + +forms_bridge_migration_400(); diff --git a/forms-bridge/migrations/4.0.7.php b/forms-bridge/migrations/4.0.7.php new file mode 100644 index 00000000..28ebf886 --- /dev/null +++ b/forms-bridge/migrations/4.0.7.php @@ -0,0 +1,14 @@ + array(), + 'credentials' => array(), + ); + + update_option( 'forms-bridge_http', $http ); +} diff --git a/forms-bridge/post_types/bridge-template.php b/forms-bridge/post_types/bridge-template.php new file mode 100644 index 00000000..57006602 --- /dev/null +++ b/forms-bridge/post_types/bridge-template.php @@ -0,0 +1,31 @@ + array( + 'name' => __( 'Bridge templates', 'forms-bridge' ), + 'singular_name' => __( 'Bridge template', 'forms-bridge' ), + ), + 'public' => false, + 'supports' => array( 'title', 'excerpt', 'custom-fields' ), + 'capability_type' => 'post', + 'can_export' => false, + ) + ); + } +); diff --git a/forms-bridge/post_types/job.php b/forms-bridge/post_types/job.php new file mode 100644 index 00000000..f10979e2 --- /dev/null +++ b/forms-bridge/post_types/job.php @@ -0,0 +1,31 @@ + array( + 'name' => __( 'Jobs', 'forms-bridge' ), + 'singular_name' => __( 'Job', 'forms-bridge' ), + ), + 'public' => false, + 'supports' => array( 'title', 'excerpt', 'custom-fields' ), + 'capability_type' => 'post', + 'can_export' => false, + ) + ); + } +); diff --git a/forms-bridge/readme.txt b/forms-bridge/readme.txt new file mode 100644 index 00000000..390265d0 --- /dev/null +++ b/forms-bridge/readme.txt @@ -0,0 +1,220 @@ +=== Forms Bridge - Infinite integrations === +Contributors: codeccoop +Tags: odoo, dolibarr, listmonk, forms, woocommerce +Donate link: https://buymeacoffee.com/codeccoop +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html +Stable Tag: 4.0.6 +Tested up to: 6.8 + +Bridge your WordPress forms without code, add custom fields, use field mappers, set up a workflow and make your data flow seamlessly to your backend. + +== Bridges == + +Think of a bridge as a pipeline through which your form submissions data flows to your backend or service. In the middle, you can add custom fields to the form submissions, use field mappers to rename and mutate your form responses, or use workflow jobs to process the data before it is sent over the wire. With bridges you can connect your WordPress forms to any kind of backend, it doesn't matter if it is a CRM, an ERP, a booking system or an email marketing platform, the only requirement is an HTTP API. If it has an API it can be bridged! + +== Form builders == + +Form builders are well known plugins that add forms to WordPress. We do bridges, let them do the forms and then work together to make your business work with ease. + +Forms Bridge supports the following form builders: + +* [Contact Form 7](https://wordpress.org/plugins/contact-form-7/) +* [GravityForms](https://www.gravityforms.com) +* [WP Forms (PRO)](https://wpforms.com/) +* [Ninja Forms](https://wordpress.org/plugins/ninja-forms/) +* [WooCommerce](https://wordpress.org/plugins/woocommerce) + +== Addons == + +Forms Bridge comes with free addons. Each addon adds to the plugin new bridges to work with specific APIs, new workflow jobs and bridge templates. + +Forms Bridge has the following addons: + +* [REST API](https://en.wikipedia.org/wiki/REST) +* [Bigin](https://www.bigin.com/developer/docs/apis/v2/?source=developer) +* [Brevo](https://developers.brevo.com/) +* [Dolibarr](https://wiki.dolibarr.org/index.php/Module_Web_Services_API_REST_(developer)) +* [FinanCoop](https://somit.coop/financoop/) +* [Google Sheets](https://workspace.google.com/products/sheets/) +* [Holded](https://developers.holded.com/reference/api-key) +* [Listmonk](https://listmonk.app/docs/apis/apis/) +* [Nextcloud](https://docs.nextcloud.com/server/20/user_manual/en/files/access_webdav.html) +* [Mailchimp](https://mailchimp.com/developer/) +* [Odoo](https://www.odoo.com/) +* [Zoho CRM](https://www.zoho.com/developer/rest-api.html) + +== Backends == + +In Forms Bridge, a backend is a set of configurations that handles the information required to get your form submissions bridged over HTTP requests to remote systems. + +To register a new backend you only have to set 3 fields: + +1. A unique name for the new connection +2. The URL of your backend +3. An array of HTTP headers with connection metadata and credentials +4. Optional, an HTTP authentication credential (Basic, Bearer, etc) + +Once registered, you can reuse your backend connection on your form bridges. + +== Custom fields == + +Custom fields are data that will be added the bridge payload. Use them to store private data you don’t want to place on your public forms, like user emails, or config values, like product IDs or lead tags. + +== Field Mappers == + +Field mappers are mutations with which you can rename your form submission fields and transform its values. Use them to make your form submissions to fit your backend API endpoint interface. + +== Workflows == + +Make your form submissions flow through a chain of jobs that pre-process the data before it was sent over the wire. Think of workflow as a system to set up automations to run on each form submission. + +== Templates == + +To streamline the bridge setup process, Forms Bridge comes packed with templates. Templates are blueprints of bridges you can use to set up your form integrations in a matter of minutes. + +== Docs == + +Browse the plugin's documentation on [formsbridge.codeccoop.org](https://formsbridge.codeccoop.org/documentation/) + +== Links == + +* [Official website](https://formsbridge.codeccoop.org/) +* [GitHub](https://github.com/codeccoop/forms-bridge/) +* [Còdec](https://www.codeccoop.org) +* [Other plugins](https://profiles.wordpress.org/codeccoop/#content-plugins) + +== Screenshots == + +1. Settings page +2. Backends +3. Bridges +4. Custom fields +5. Field mappers +6. Workflows +7. Job editor +8. Template wizard +9. Debug console + +== Changelog == + += 4.0.6 = +* fix: field serialization based on field type +* feat: add accpet-language http header on odoo's rpc api calls +* feat: new odoo job position and helpdesk ticket templates +* feat: new odoo workflow jobs +* feat: include textarea to the standard field types += 4.0.5 = +* fix: prepare mappers loop introduced in 4.0.4 += 4.0.4 = +* feat: add remuneration_type form field on financoop subscription request template +* fix: minnor frontend fixes and improvements +* fix: financoop shortcode, templates and jobs +* fix: template config loading race conditions + += 4.0.3 = +* fix: load text domain warnings + += 4.0.2 = +* feat: mailchimp api url selector + += 4.0.1 = +* feat: rename form integrations to form builders +* feat: oauth grant as GET requests +* feat: dolibarr, odoo and holded sync products templates +* feat: brevo woo orders template +* fix: migrations enqueuement loop + += 4.0.0 = +* feat: Workflow jobs editor +* feat: Nextcloud addon +* fix: Edge case of mutations and fingers +* feat: HTTP authentication +* feat: Admin UI refactor +* feat: Wipe config button +* feat: Settings API refactor +* feat: Zoho and Google Oauth web based credentials +* feat: drop support for Google service credentials +* feat: drop support for Zoho Self Client credentials + += 3.5.4 = +* fix: use conditional mappers on stringify attachments +* fix: cast value type for join mutations + += 3.5.3 = +* feat: nename gsheet default backend +* fix: bridge request filter callback removal + += 3.5.2 = +* feat: new google sheets woocomerce orders template +* feat: disable default payload prune for gsheet bridges +* feat: update gsheet composer dependencies +* fix: remove php warnings on zoho and listmonk addons + += 3.5.1 = +* feat: improve dolibarr next code and product search api calls +* feat: add is_bridged woocommerce order meta data +* feat: new validate order job and template for the dolibarr addon +* feat: new delivered order template for the odoo addon +* feat: changes on the holded woocommerce template + += 3.5.0 = +* fix: woocommerce payload schema +* feat: woocommerce bridge templates support +* feat: woocommerce templates for odoo, dolibarr, holded, bigin, brevo, mailchimp and zoho +* feat: jon finger expansions +* feat: conditional json finger pointers +* feat: improvements on the workflows panel UI +* feat: backend and bridges json exports + += 3.4.3 = +* feat: bridge template descriptions +* feat: listmonk skip subscription job + += 3.4.2 = +* fix: holded appointments template jobs +* fix: typos from odoo workflow job descriptions +* feat: add new chapters to the plugin's readme +* feat: settings sanitization with defaults recovery + += 3.4.1 = +* feat: holded quotation templates +* feat: holded API introspection based on swagger data +* fix: bridge api schema invalidation +* feat: api fields button with disabled state + += 3.4.0 = +* feat: odoo quotation templates +* feat: dolibarr quotation templates +* feat: country id odoo workflow job +* feat: gmt date tags +* feat: addons data autoload +* feat: odoo state id job +* feat: skip email list subscription jobs +* fix: firefox backend state updates on firefox + += 3.3.5 = +* feat: support for ninja file fields and conditionals + += 3.3.4 = +* fix: does not skip empty array submissions on submission filter +* feat: remove gf private uploads module +* fix: scroll to bottom on mutations/custom fields tables + += 3.3.3 = +* feat: remove minLength constraint from bridge schema +* feat: set null value on mappers with nowhere jsonfinger pointers + += 3.3.2 = +* feat: update plugin urls and readme +* feat: remote assets from gitlab +* fix: mailchimp template wizard +* feat: update credits, donation link and screenshots + += 3.3.1 = +* fix: odoo api function bridge patches +* feat: plugin screenshots +* feat: update readme and plugin official url + += 3.3.0 = +* feat: introspection api diff --git a/includes/class-addon.php b/includes/class-addon.php deleted file mode 100644 index e1efdf48..00000000 --- a/includes/class-addon.php +++ /dev/null @@ -1,844 +0,0 @@ - - */ - private static $addons = []; - - /** - * Handles addon's registry option name. - * - * @var string - */ - private const registry = 'forms_bridge_addons'; - - /** - * Handles addon public name. - * - * @var string - */ - public const title = ''; - - /** - * Handles addon's API name. - * - * @var string - */ - public const name = ''; - - /** - * Handles addon's custom bridge class name. - * - * @var string - */ - public const bridge_class = '\FORMS_BRIDGE\Form_Bridge'; - - /** - * Addon's default config getter. - * - * @return array - */ - public static function schema() - { - $bridge_schema = FBAPI::get_bridge_schema(static::name); - - return [ - 'type' => 'object', - 'properties' => [ - 'title' => ['type' => 'string'], - 'description' => [ - 'type' => 'string', - 'default' => '', - ], - 'bridges' => [ - 'type' => 'array', - 'items' => $bridge_schema, - 'default' => [], - ], - ], - 'required' => ['title', 'bridges'], - ]; - } - - /** - * Addon's default data getter. - * - * @return array - */ - protected static function defaults() - { - return [ - 'title' => static::title, - 'bridges' => [], - ]; - } - - /** - * Public singleton initializer. - */ - final public static function setup(...$args) - { - return static::get_instance(...$args); - } - - /** - * Public addons registry getter. - * - * @return array Addons registry state. - */ - private static function registry() - { - $state = get_option(self::registry, ['rest' => true]) ?: []; - $addons_dir = FORMS_BRIDGE_ADDONS_DIR; - $addons = array_diff(scandir($addons_dir), ['.', '..']); - - $registry = []; - foreach ($addons as $addon) { - $addon_dir = "{$addons_dir}/{$addon}"; - if (!is_dir($addon_dir)) { - continue; - } - - $index = "{$addon_dir}/class-{$addon}-addon.php"; - if (is_file($index) && is_readable($index)) { - $registry[$addon] = boolval($state[$addon] ?? false); - } - } - - return $registry; - } - - /** - * Updates the addons' registry state. - * - * @param array $value Plugin's general setting data. - */ - private static function update_registry($addons = []) - { - $registry = self::registry(); - foreach ($addons as $addon => $enabled) { - if (!isset($registry[$addon])) { - continue; - } - - $registry[$addon] = (bool) $enabled; - } - - update_option(self::registry, $registry); - } - - final public static function addons() - { - $addons = []; - foreach (self::$addons as $addon) { - if ($addon->enabled) { - $addons[] = $addon; - } - } - - return $addons; - } - - /** - * Addon instances getter. - * - * @var string $name Addon name. - * - * @return Addon|null - */ - final public static function addon($name) - { - return self::$addons[$name] ?? null; - } - - /** - * Public addons loader. - */ - final public static function load_addons() - { - $addons_dir = FORMS_BRIDGE_ADDONS_DIR; - $registry = self::registry(); - foreach ($registry as $addon => $enabled) { - require_once "{$addons_dir}/{$addon}/class-{$addon}-addon.php"; - - if ($enabled) { - self::$addons[$addon]->load(); - } - } - - Settings_Store::ready(function ($store) { - $store::use_getter('general', function ($data) { - $registry = self::registry(); - $addons = []; - foreach (self::$addons as $name => $addon) { - $logo_path = - FORMS_BRIDGE_ADDONS_DIR . - '/' . - $addon::name . - '/assets/logo.png'; - - if (is_file($logo_path) && is_readable($logo_path)) { - $logo = plugin_dir_url($logo_path) . 'logo.png'; - } else { - $logo = ''; - } - - $addons[$name] = [ - 'name' => $name, - 'title' => $addon::title, - 'enabled' => $registry[$name] ?? false, - 'logo' => $logo, - ]; - } - - ksort($addons); - $addons = array_values($addons); - - $addons = apply_filters('forms_bridge_addons', $addons); - return array_merge($data, ['addons' => $addons]); - }); - - $store::use_setter( - 'general', - function ($data) { - if (!isset($data['addons']) || !is_array($data['addons'])) { - return $data; - } - - $registry = []; - foreach ($data['addons'] as $addon) { - $registry[$addon['name']] = (bool) $addon['enabled']; - } - - self::update_registry($registry); - - unset($data['addons']); - return $data; - }, - 9 - ); - }); - } - - /** - * Middelware to the addon settings validation method to filter out of domain - * setting updates. - * - * @param array $data Setting data. - * - * @return array Validated setting data. - */ - private static function sanitize_setting($data) - { - if (!isset($data['bridges'])) { - return $data; - } - - $data['bridges'] = static::sanitize_bridges($data['bridges']); - - return $data; - } - - /** - * Apply bridges setting data sanitization and validation. - * - * @param array $bridges Collection of bridges data. - * - * @return array - */ - private static function sanitize_bridges($bridges) - { - $uniques = []; - $sanitized = []; - - $schema = FBAPI::get_bridge_schema(static::name); - foreach ($bridges as $bridge) { - $bridge['name'] = trim($bridge['name']); - if (in_array($bridge['name'], $uniques, true)) { - continue; - } - - $bridge = static::sanitize_bridge($bridge, $schema); - if ($bridge) { - $sanitized[] = $bridge; - $uniques[] = $bridge['name']; - } - } - - return $sanitized; - } - - /** - * Common bridge sanitization method. - * - * @param array $bridge Bridge data. - * @param array $schema Bridge schema. - * - * @return array - */ - protected static function sanitize_bridge($bridge, $schema) - { - $backends = Http_Store::setting('general')->backends ?: []; - - foreach ($backends as $candidate) { - if ($candidate['name'] === $bridge['backend']) { - $backend = $candidate; - break; - } - } - - if (!isset($backend)) { - $bridge['backend'] = ''; - } - - static $forms; - if ($forms === null) { - $forms = FBAPI::get_forms(); - } - - foreach ($forms as $candidate) { - if ($candidate['_id'] === $bridge['form_id']) { - $form = $candidate; - break; - } - } - - if (!isset($form)) { - $bridge['form_id'] = ''; - } - - $bridge['mutations'] = array_slice( - $bridge['mutations'], - 0, - count($bridge['workflow']) + 1 - ); - - for ($i = 0; $i <= count($bridge['workflow']); $i++) { - $bridge['mutations'][$i] = $bridge['mutations'][$i] ?? []; - } - - $bridge['is_valid'] = - $bridge['form_id'] && - $bridge['backend'] && - $bridge['method'] && - $bridge['endpoint']; - - $bridge['enabled'] = boolval($bridge['enabled'] ?? true); - return $bridge; - } - - public $enabled = false; - - /** - * Private class constructor. Add addons scripts as dependency to the - * plugin's scripts and setup settings hooks. - */ - protected function construct(...$args) - { - if (empty(static::name) || empty(static::title)) { - Logger::log('Skip invalid addon registration', Logger::DEBUG); - Logger::log( - 'Addon name and title const are required', - Logger::ERROR - ); - return; - } - - self::$addons[static::name] = $this; - } - - public function load() - { - add_action( - 'init', - static function () { - self::load_data(); - }, - 5, - 0 - ); - - add_filter( - 'forms_bridge_templates', - static function ($templates, $addon = null) { - if (!wp_is_numeric_array($templates)) { - $templates = []; - } - - if ($addon && $addon !== static::name) { - return $templates; - } - - foreach (static::load_templates() as $template) { - $templates[] = $template; - } - - return $templates; - }, - 10, - 2 - ); - - add_filter( - 'forms_bridge_jobs', - static function ($jobs, $addon = null) { - if (!wp_is_numeric_array($jobs)) { - $jobs = []; - } - - if ($addon && $addon !== static::name) { - return $jobs; - } - - foreach (static::load_jobs() as $job) { - $jobs[] = $job; - } - - return $jobs; - }, - 10, - 2 - ); - - add_filter( - 'forms_bridge_bridges', - static function ($bridges, $addon = null) { - if (!wp_is_numeric_array($bridges)) { - $bridges = []; - } - - if ($addon && $addon !== static::name) { - return $bridges; - } - - $setting = static::setting(); - if (!$setting) { - return $bridges; - } - - foreach ($setting->bridges ?: [] as $bridge_data) { - $bridge_class = static::bridge_class; - $bridges[] = new $bridge_class($bridge_data, static::name); - } - - return $bridges; - }, - 10, - 2 - ); - - Settings_Store::register_setting(static function ($settings) { - $schema = static::schema(); - $schema['name'] = static::name; - $schema['default'] = static::defaults(); - - $settings[] = $schema; - return $settings; - }); - - Settings_Store::ready(static function ($store) { - $store::use_getter(static::name, static function ($data) { - $templates = FBAPI::get_addon_templates(static::name); - $jobs = FBAPI::get_addon_jobs(static::name); - - return array_merge($data, [ - 'templates' => array_map(static function ($template) { - return [ - 'title' => $template->title, - 'name' => $template->name, - 'integrations' => $template->integrations, - ]; - }, $templates), - 'jobs' => array_map(static function ($job) { - return [ - 'title' => $job->title, - 'name' => $job->name, - ]; - }, $jobs), - ]); - }); - - $store::use_setter( - static::name, - static function ($data) { - if (!is_array($data)) { - return $data; - } - - unset($data['templates']); - unset($data['jobs']); - - return static::sanitize_setting($data); - }, - 9 - ); - }); - - $this->enabled = true; - } - - /** - * Addon's setting name getter. - * - * @return string - */ - final protected static function setting_name() - { - return 'forms-bridge_' . static::name; - } - - /** - * Addon's setting instance getter. - * - * @return Setting|null Setting instance. - */ - final protected static function setting() - { - return Forms_Bridge::setting(static::name); - } - - /** - * Performs a request against the backend to check the connexion status. - * - * @param string $backend Target backend name. - * - * @return boolean|WP_Error - */ - public function ping($backend) - { - return true; - } - - /** - * Performs a GET request against the backend endpoint and retrive the response data. - * - * @param string $endpoint Target endpoint name. - * @param string $backend Target backend name. - * - * @return array|WP_Error - */ - public function fetch($endpoint, $backend) - { - return [ - 'headers' => [], - 'cookies' => [], - 'filename' => null, - 'body' => '', - 'response' => [ - 'status' => 202, - 'message' => 'Accepted', - ], - 'http_response' => [ - 'data' => null, - 'headers' => null, - 'status' => null, - ], - 'data' => [], - ]; - } - - /** - * Performs an introspection of the backend endpoint and returns API fields - * and accepted content type. - * - * @param string $endpoint Target endpoint name. - * @param string $backend Target backend name. - * - * @return array|WP_Error - */ - public function get_endpoint_schema($endpoint, $backend) - { - return []; - } - - private static function autoload_posts($post_type, $addon) - { - if (!in_array($post_type, ['fb-bridge-template', 'fb-job'])) { - return []; - } - - return get_posts([ - 'post_type' => $post_type, - 'posts_per_page' => -1, - 'meta_key' => '_fb-addon', - 'meta_value' => $addon, - ]); - } - - /** - * Autoload config files from a given addon's directory. Used to load - * template and job config files. - * - * @param string $dir Path of the target directory. - * - * @return array Array with data from files. - */ - private static function autoload_dir($dir, $extensions = ['php', 'json']) - { - if (!is_readable($dir) || !is_dir($dir)) { - return []; - } - - static $load_cache; - - $files = []; - foreach (array_diff(scandir($dir), ['.', '..']) as $file) { - $file_path = $dir . '/' . $file; - - if (is_file($file_path) && is_readable($file_path)) { - $files[] = $file_path; - } - } - - $loaded = []; - foreach ($files as $file_path) { - $file = basename($file_path); - $name = pathinfo($file)['filename']; - $ext = pathinfo($file)['extension'] ?? null; - - if (!in_array($ext, $extensions)) { - continue; - } - - if (isset($load_cache[$file_path])) { - $loaded[] = $load_cache[$file_path]; - continue; - } - - $data = null; - if ($ext === 'php') { - $data = include_once $file_path; - } elseif ($ext === 'json') { - try { - $content = file_get_contents($file_path); - $data = json_decode($content, true, JSON_THROW_ON_ERROR); - } catch (TypeError) { - // pass - } catch (Error) { - // pass - } - } - - if (is_array($data)) { - $data['name'] = $name; - $loaded[] = $data; - $load_cache[$file_path] = $data; - } - } - - return $loaded; - } - - /** - * Loads addon's bridge data. - */ - private static function load_data() - { - $dir = FORMS_BRIDGE_ADDONS_DIR . '/' . static::name . '/data'; - self::autoload_dir($dir); - } - - /** - * Loads addon's bridge templates. - * - * @return Form_Bridge_Template[]. - */ - private static function load_templates() - { - $dir = FORMS_BRIDGE_ADDONS_DIR . '/' . static::name . '/templates'; - - $directories = apply_filters( - 'forms_bridge_template_directories', - [ - $dir, - Forms_Bridge::path() . 'includes/templates', - get_stylesheet_directory() . - '/forms-bridge/templates/' . - static::name, - ], - static::name - ); - - $templates = []; - foreach ($directories as $dir) { - if (!is_dir($dir)) { - continue; - } - - foreach (self::autoload_dir($dir) as $template) { - $template['name'] = sanitize_title($template['name']); - $templates[$template['name']] = $template; - } - } - - foreach ( - self::autoload_posts('fb-bridge-template', static::name) - as $template_post - ) { - $template[$template->post_name] = $template_post; - } - - $templates = array_values($templates); - - $templates = apply_filters( - 'forms_bridge_load_templates', - $templates, - static::name - ); - - $loaded = []; - foreach ($templates as $template) { - if ( - is_array($template) && - isset($template['data'], $template['name']) - ) { - $template = array_merge($template['data'], [ - 'name' => $template['name'], - ]); - } - - $template = new Form_Bridge_Template($template, static::name); - - if ($template->is_valid) { - $loaded[] = $template; - } - } - - return $loaded; - } - - /** - * Addon's jobs loader. - * - * @return Job[] - */ - private static function load_jobs() - { - $dir = FORMS_BRIDGE_ADDONS_DIR . '/' . static::name . '/jobs'; - - $directories = apply_filters( - 'forms_bridge_job_directories', - [ - $dir, - Forms_Bridge::path() . 'includes/jobs', - get_stylesheet_directory() . - '/forms-bridge/jobs/' . - static::name, - ], - static::name - ); - - $jobs = []; - foreach ($directories as $dir) { - if (!is_dir($dir)) { - continue; - } - - foreach (self::autoload_dir($dir) as $job) { - $job['name'] = sanitize_title($job['name']); - $jobs[$job['name']] = $job; - } - } - - foreach (self::autoload_posts('fb-job', static::name) as $job_post) { - $jobs[$job_post->post_name] = $job_post; - } - - $jobs = array_values($jobs); - - $jobs = apply_filters('forms_bridge_load_jobs', $jobs, static::name); - - $loaded = []; - foreach ($jobs as $job) { - if (is_array($job) && isset($job['data'], $job['name'])) { - $job = array_merge($job['data'], ['name' => $job['name']]); - } - - $job = new Job($job, static::name); - - if ($job->is_valid) { - $loaded[] = $job; - } - } - - return $loaded; - } - - // public static function get_api() - // { - // $__FILE__ = (new ReflectionClass(static::class))->getFileName(); - // $file = dirname($__FILE__) . '/api.php'; - - // if (!is_file($file) || !is_readable($file)) { - // return []; - // } - - // $source = file_get_contents($file); - // $tokens = token_get_all($source); - - // $functions = []; - // $nextStringIsFunc = false; - // $inClass = false; - // $bracesCount = 0; - - // foreach ($tokens as $token) { - // switch ($token[0]) { - // case T_CLASS: - // $inClass = true; - // break; - // case T_FUNCTION: - // if (!$inClass) { - // $nextStringIsFunc = true; - // } - // break; - - // case T_STRING: - // if ($nextStringIsFunc) { - // $nextStringIsFunc = false; - // $functions[] = $token[1]; - // } - // break; - // case '(': - // case ';': - // $nextStringIsFunc = false; - // break; - // case '{': - // if ($inClass) { - // $bracesCount++; - // } - // break; - - // case '}': - // if ($inClass) { - // $bracesCount--; - // if ($bracesCount === 0) { - // $inClass = false; - // } - // } - // break; - // } - // } - - // return $functions; - // } -} diff --git a/includes/class-api.php b/includes/class-api.php deleted file mode 100644 index 8c7aa8af..00000000 --- a/includes/class-api.php +++ /dev/null @@ -1,587 +0,0 @@ -name === $name) { - return $bridge; - } - } - } - - /** - * Gets the current bridge from the bridges loop. - * - * @return Form_Bridge|null - */ - public static function get_current_bridge() - { - return Forms_Bridge::current_bridge(); - } - - /** - * Gets the collection of available bridges. - * - * @return Form_Bridge[] - */ - public static function get_bridges() - { - return apply_filters('forms_bridge_bridges', []); - } - - /** - * Gets the collection of available bridges filtered by form ID. - * - * @param string $form_id Form ID. - * - * @return Form_Bridge[] - */ - public static function get_form_bridges($form_id, $integration = null) - { - $bridges = apply_filters('forms_bridge_bridges', []); - - if (preg_match('/^(\w+):(\d+)$/', $form_id, $matches)) { - [, $integration, $form_id] = $matches; - $form_id = (int) $form_id; - } elseif (empty($integration)) { - return []; - } - - $form_id = "{$integration}:{$form_id}"; - - $form_bridges = []; - foreach ($bridges as $bridge) { - if ($bridge->form_id === $form_id) { - $form_bridges[] = $bridge; - } - } - - return $form_bridges; - } - - /** - * Gets the collection of available bridges filtered by addon name. - * - * @param string $addon Addon name. - * - * @return Form_Bridge[] - */ - public static function get_addon_bridges($addon) - { - return apply_filters('forms_bridge_bridges', [], $addon); - } - - /** - * Inserts or updates the bridge data to the database. - * - * @param array $data Bridge data. - * @param string $addon Addon name. - * - * @return boolean - */ - public static function save_bridge($data, $addon) - { - $addon = self::get_addon($addon); - if (!$addon) { - return false; - } - - $bridge_class = $addon::bridge_class; - $bridge = new $bridge_class($data); - - if (!$bridge->is_valid) { - return false; - } - - return $bridge->save(); - } - - /** - * Deletes the bridge data from the database. - * - * @param string $name Bridge name. - * @param string $addon Addon name. - * - * @return boolean - */ - public static function delete_bridge($name, $addon) - { - $bridge = self::get_bridge($name, $addon); - - if (!$bridge) { - return false; - } - - return $bridge->delete(); - } - - /** - * Gets the bridge schema for a given addon. - * - * @param string $name Addon name. - * - * @return array|null - */ - public static function get_bridge_schema($addon) - { - $addon = Addon::addon($addon); - - if (!$addon) { - return; - } - - return Form_Bridge::schema($addon::name); - } - - /** - * Gets the collection of available templates. - * - * @return Form_Bridge_Template[] - */ - public static function get_templates() - { - return apply_filters('forms_bridge_templates', []); - } - - /** - * Gets the collection of available templates filtered by addon name. - * - * @param string $addon Addon name. - * - * @return Form_Bridge_Template[] - */ - public static function get_addon_templates($addon) - { - return apply_filters('forms_bridge_templates', [], $addon); - } - - /** - * Gets a template instance by name and addon. - * - * @param string $name Template name. - * @param string $addon Addon name. - * - * @return Form_Bridge_Template|null - */ - public static function get_template($name, $addon) - { - $templates = self::get_addon_templates($addon); - - foreach ($templates as $template) { - if ($template->name === $name) { - return $template; - } - } - } - - /** - * Inserts or updates the template data on the database. - * - * @param array $data Template data. - * @param string $addon Addon name. - * - * @return integer|boolean Post ID or false. - */ - public static function save_template($data, $addon) - { - $template = new Form_Bridge_Template($data, $addon); - - if (!$template->is_valid) { - return false; - } - - return $template->save(); - } - - /** - * Delete the template data from the database. - * - * @param string $name Template name. - * @param string $addon Addon name. - * - * @return boolean - */ - public static function reset_template($name, $addon) - { - $template = self::get_template($name, $addon); - - if (!$template) { - return false; - } - - return $template->reset(); - } - - /** - * Gets the template schema for a given addon. - * - * @param string $addon Addon name. - * - * @return array|null - */ - public static function get_template_schema($addon) - { - return Form_Bridge_Template::schema($addon); - } - - /** - * Gets the collection of available jobs. - * - * @return Job[] - */ - public static function get_jobs() - { - return apply_filters('forms_bridge_jobs', []); - } - - /** - * Gets the collection of available jobs filtered by addon name. - * - * @param string $addon Addon name. - * - * @return Job[] - */ - public static function get_addon_jobs($addon) - { - return apply_filters('forms_bridge_jobs', [], $addon); - } - - /** - * Gets a job instance by name and addon. - * - * @param string $name Job name. - * @param string $addon Addon name. - * - * @return Job|null - */ - public static function get_job($name, $addon) - { - $jobs = self::get_addon_jobs($addon); - - foreach ($jobs as $job) { - if ($job->name === $name) { - return $job; - } - } - } - - /** - * Inserts or updates the job data on the database. - * - * @param array $data Job data. - * @param string $addon Addon name. - * - * @return integer|boolean Post ID or false. - */ - public static function save_job($data, $addon) - { - $job = new Job($data, $addon); - - if (!$job->is_valid) { - return false; - } - - return $job->save(); - } - - /** - * Deletes the job data from the database. - * - * @param string $name Job name. - * @param string $addon Addon name. - * - * @return boolean - */ - public static function reset_job($name, $addon) - { - $job = self::get_job($name, $addon); - - if (!$job) { - return false; - } - - return $job->reset(); - } - - /** - * Gets the job schema. - * - * @return array - */ - public static function get_job_schema() - { - return Job::schema(); - } - - /** - * Gets the collection of available backends. - * - * @return Backend[] - */ - public static function get_backends() - { - return apply_filters('http_bridge_backends', []); - } - - /** - * Gets a backend instance by name. - * - * @param string $name Backend name. - * - * @return Backend|null - */ - public static function get_backend($name) - { - $backends = self::get_backends(); - - foreach ($backends as $backend) { - if ($backend->name === $name) { - return $backend; - } - } - } - - /** - * Inserts or updates the backend data on the database. - * - * @param array Backend data. - * - * @return boolean - */ - public static function save_backend($data) - { - $backend = new Backend($data); - - if (!$backend->is_valid) { - return false; - } - - return $backend->save(); - } - - /** - * Delete the backend data from the database. - * - * @param string $name Backend name. - * - * @return boolean - */ - public static function delete_backend($name) - { - $backend = self::get_backend($name); - - if (!$backend) { - return false; - } - - return $backend->remove(); - } - - /** - * Gets the backend schema. - * - * @return array - */ - public static function get_backend_schema() - { - return Backend::schema(); - } - - /** - * Gets the collection of available credentials. - * - * @return Credential[] - */ - public static function get_credentials() - { - return apply_filters('http_bridge_credentials', []); - } - - /** - * Gets a credential instance by name and addon. - * - * @param string $name Credential name. - * - * @return Credential|null - */ - public static function get_credential($name) - { - $credentials = self::get_credentials(); - - foreach ($credentials as $credential) { - if ($credential->name === $name) { - return $credential; - } - } - } - - /** - * Inserts or updates the credential data to the database. - * - * @param array $data Credential data. - * - * @return boolean - */ - public static function save_credential($data) - { - $credential = new Credential($data); - - if (!$credential->is_valid) { - return false; - } - - return $credential->save(); - } - - /** - * Deletes the credential data from the database. - * - * @param string $name Credential name. - * - * @return boolean - */ - public static function delete_credential($name) - { - $credential = self::get_credential($name); - - if (!$credential) { - return false; - } - - return $credential->delete(); - } - - /** - * Gets the credential schema for a given addon. - * - * @return array - */ - public static function get_credential_schema() - { - return Credential::schema(); - } -} diff --git a/includes/class-form-bridge-template.php b/includes/class-form-bridge-template.php deleted file mode 100644 index c65adf71..00000000 --- a/includes/class-form-bridge-template.php +++ /dev/null @@ -1,1251 +0,0 @@ - 'http://json-schema.org/draft-04/schema#', - 'title' => 'form-bridge-template', - 'type' => 'object', - 'properties' => [ - 'name' => [ - 'title' => _x( - 'Name', - 'Bridge template schema', - 'forms-bridge' - ), - 'description' => __( - 'Internal and unique name of the template', - 'forms-bridge' - ), - 'type' => 'string', - 'minLength' => 1, - ], - 'title' => [ - 'title' => _x( - 'Title', - 'Bridge template schema', - 'forms-bridge' - ), - 'description' => __( - 'Public title of the template', - 'forms-bridge' - ), - 'type' => 'string', - 'minLength' => 1, - ], - 'description' => [ - 'title' => _x( - 'Description', - 'Bridge template schema', - 'forms-bridge' - ), - 'description' => __( - 'Short description of the template purpose', - 'forms-bridge' - ), - 'type' => 'string', - 'default' => '', - ], - 'integrations' => [ - 'title' => _x( - 'Integrations', - 'Bridge template schema', - 'forms-bridge' - ), - 'description' => __( - 'Template\'s supported integrations', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => ['type' => 'string'], - 'uniqueItems' => true, - 'minItems' => 1, - ], - 'fields' => [ - 'title' => _x( - 'Fields', - 'Bridge template schema', - 'forms-bridge' - ), - 'description' => __( - 'Template fields to be filled by the user', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'ref' => [ - 'type' => 'string', - 'pattern' => '^#.+', - ], - 'name' => [ - 'type' => 'string', - 'minLength' => 1, - ], - 'label' => [ - 'type' => 'string', - 'minLength' => 1, - ], - 'description' => ['type' => 'string'], - 'type' => [ - 'type' => 'string', - 'enum' => [ - 'text', - 'number', - 'select', - 'boolean', - 'email', - 'url', - ], - ], - 'required' => ['type' => 'boolean'], - 'value' => [ - 'type' => [ - 'integer', - 'number', - 'string', - 'array', - 'boolean', - ], - ], - 'default' => [ - 'type' => [ - 'integer', - 'number', - 'string', - 'array', - 'boolean', - ], - ], - 'options' => [ - 'anyOf' => [ - [ - 'description' => __( - 'List of field options', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'label' => [ - 'type' => 'string', - ], - 'value' => [ - 'type' => 'string', - ], - ], - 'required' => ['value', 'label'], - ], - 'uniqueItems' => true, - ], - [ - 'description' => __( - 'How to get options from the addon API', - 'forms-bridge' - ), - 'type' => 'object', - 'properties' => [ - 'endpoint' => [ - 'description' => __( - 'Endpoint to get values from', - 'forms-bridge' - ), - 'type' => 'string', - ], - 'finger' => [ - 'description' => __( - 'Fingers to get values from the endpoint response', - 'forms-bridge' - ), - 'oneOf' => [ - [ - 'type' => 'object', - 'properties' => [ - 'value' => [ - 'type' => - 'string', - ], - 'label' => [ - 'type' => - 'string', - ], - ], - 'required' => ['value'], - ], - [ - 'type' => 'string', - ], - ], - ], - ], - 'required' => ['endpoint', 'finger'], - ], - ], - ], - 'enum' => [ - 'type' => 'array', - 'items' => [ - 'type' => ['integer', 'number', 'string'], - ], - 'uniqueItems' => true, - ], - 'min' => ['type' => 'integer'], - 'max' => ['type' => 'integer'], - 'multiple' => ['type' => 'boolean'], - ], - 'required' => ['ref', 'name', 'type'], - 'additionalProperties' => true, - ], - ], - 'form' => [ - 'title' => _x( - 'Form', - 'Bridge template schema', - 'forms-bridge' - ), - 'description' => __( - 'Form title and fields settings', - 'forms-bridge' - ), - 'type' => 'object', - 'properties' => [ - 'title' => [ - 'type' => 'string', - 'default' => '', - ], - 'fields' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => [ - 'type' => 'string', - 'minLength' => 1, - ], - 'label' => ['type' => 'string'], - 'type' => [ - 'type' => 'string', - 'enum' => [ - 'text', - 'textarea', - 'number', - 'url', - 'email', - 'select', - 'date', - 'hidden', - 'file', - ], - ], - 'required' => ['type' => 'boolean'], - 'options' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'value' => [ - 'type' => [ - 'integer', - 'number', - 'string', - 'boolean', - ], - ], - 'label' => [ - 'type' => 'string', - ], - ], - 'required' => ['value', 'label'], - ], - ], - 'value' => [ - 'type' => [ - 'integer', - 'number', - 'string', - // 'boolean', - // 'object', - 'array', - // 'null', - ], - ], - 'is_file' => ['type' => 'boolean'], - 'is_multi' => ['type' => 'boolean'], - 'filetypes' => ['type' => 'string'], - 'min' => ['type' => 'number'], - 'max' => ['type' => 'number'], - 'step' => ['type' => 'number'], - 'format' => ['type' => 'string'], - ], - 'required' => ['name', 'type'], - ], - ], - ], - 'required' => ['title', 'fields'], - 'additionalProperties' => false, - ], - 'bridge' => self::child_schema_to_template( - $bridge_schema, - _x('Bridge', 'Bridge template schema', 'forms-bridge') - ), - 'backend' => self::child_schema_to_template( - $backend_schema, - _x('Backend', 'Bridge template schema', 'forms-bridge') - ), - 'credential' => self::child_schema_to_template( - $credential_schema, - _x('Credential', 'Bridge template schema', 'forms-bridge') - ), - ], - 'additionalProperties' => false, - 'required' => [ - 'name', - 'title', - 'integrations', - 'fields', - 'form', - 'backend', - 'bridge', - ], - ]; - - if (!$addon) { - return $schema; - } - - return apply_filters('forms_bridge_template_schema', $schema, $addon); - } - - private static function child_schema_to_template($schema, $title) - { - if (isset($schema['oneOf'])) { - $schema['oneOf'] = array_map(static function ($schema) use ( - $title - ) { - $title = $schema['title'] ?? $title; - return self::child_schema_to_template($schema, $title); - }, $schema['oneOf']); - return $schema; - } elseif (isset($schema['anyOf'])) { - $schema['anyOf'] = array_map(static function ($schema) use ( - $title - ) { - $title = $schema['title'] ?? $title; - return self::child_schema_to_template($schema, $title); - }, $schema['anyOf']); - return $schema; - } - - foreach ($schema['properties'] as &$prop_schema) { - if ($prop_schema['type'] === 'string') { - $prop_schema['default'] = ''; - unset($prop_schema['minLength']); - unset($prop_schema['pattern']); - unset($prop_schema['format']); - } elseif ($prop_schema['type'] === 'array') { - $prop_schema['default'] = []; - unset($prop_schema['minItems']); - } - } - - if (!isset($schema['default'])) { - $schema['default'] = []; - } - - $schema['title'] = $title; - return $schema; - } - - /** - * Template default data getter. - * - * @param string $addon Template addon namespace. - * @param array $schema Template schema. - * - * @return array - */ - protected static function defaults($addon = null, $schema = null) - { - if (!is_array($schema)) { - $schema = static::schema($addon); - } - - return apply_filters( - 'forms_bridge_template_defaults', - [ - 'integrations' => [], - 'fields' => [ - [ - 'ref' => '#form', - 'name' => 'id', - 'label' => __('Form ID', 'forms-bridge'), - 'type' => 'text', - ], - [ - 'ref' => '#form', - 'name' => 'title', - 'label' => __('Form title', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#backend', - 'name' => 'name', - 'label' => __('Name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - ], - [ - 'ref' => '#backend', - 'name' => 'base_url', - 'label' => __('Base URL', 'forms-bridge'), - 'type' => 'url', - 'required' => true, - 'default' => 'https://', - 'format' => 'uri', - ], - [ - 'ref' => '#bridge', - 'name' => 'name', - 'label' => __('Bridge name', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'minLength' => 1, - ], - [ - 'ref' => '#bridge', - 'name' => 'endpoint', - 'label' => __('Endpoint', 'forms-bridge'), - 'type' => 'text', - 'required' => true, - 'default' => '', - ], - [ - 'ref' => '#bridge', - 'name' => 'method', - 'label' => __('Method', 'forms-bridge'), - 'type' => 'options', - 'options' => [ - [ - 'label' => 'GET', - 'value' => 'GET', - ], - [ - 'label' => 'POST', - 'value' => 'POST', - ], - [ - 'label' => 'PUT', - 'value' => 'PUT', - ], - [ - 'label' => 'PATCH', - 'value' => 'PATCH', - ], - [ - 'label' => 'DELETE', - 'value' => 'DELETE', - ], - ], - 'required' => true, - 'default' => 'POST', - ], - ], - 'bridge' => [ - 'endpoint' => '', - 'method' => 'POST', - ], - 'backend' => [ - 'headers' => [ - [ - 'name' => 'Content-Type', - 'value' => 'application/json', - ], - ], - ], - 'form' => [ - 'title' => '', - 'fields' => [], - ], - ], - $addon, - $schema - ); - } - - private static function data_from_post($post) - { - return [ - 'name' => $post->post_name, - 'title' => $post->post_title, - 'description' => $post->post_excerpt, - 'fields' => - (array) (get_post_meta($post->ID, '_template-fields', true) ?: - []), - 'form' => - (array) (get_post_meta($post->ID, '_template-form', true) ?: - []), - 'bridge' => - (array) (get_post_meta($post->ID, '_template-bridge', true) ?: - []), - 'backend' => - (array) (get_post_meta($post->ID, '_template-backend', true) ?: - []), - 'credential' => - (array) (get_post_meta( - $post->ID, - '_template-credential', - true - ) ?: - []), - ]; - } - - /** - * Store template attribute values, validates data and binds the - * instance to custom forms bridge template hooks. - * - * @param string $name Template name. - * @param array $data Template data. - */ - public function __construct($data, $addon) - { - if ($data instanceof WP_Post) { - $data = self::data_from_post($data); - } - - $this->addon = $addon; - $this->data = $this->validate($data); - - if ($this->is_valid) { - $this->id = $this->addon . '-' . $data['name']; - } - } - - /** - * Magic method to proxy private template attributes and data. - * - * @param string $name Attribute name. - * - * @return mixed Attribute value or null. - */ - public function __get($name) - { - switch ($name) { - case 'id': - return $this->id; - case 'addon': - return $this->addon; - case 'data': - return $this->data; - case 'is_valid': - return !is_wp_error($this->data) && - Addon::addon($this->addon) !== null; - default: - if (!$this->is_valid) { - return; - } - - return $this->data[$name] ?? null; - } - } - - /** - * Validates input data against the template schema. - * - * @param array $data Input data. - * - * @return array|WP_Error Validated data. - */ - private function validate($data) - { - $schema = static::schema($this->addon); - $defaults = static::defaults($this->addon, $schema); - - if (empty($data['integrations'])) { - foreach (Integration::integrations() as $integration) { - if ($integration::name !== 'woo') { - $data['integrations'][] = $integration::name; - } - } - } - - $data = wpct_plugin_merge_object($data, $defaults, $schema); - return wpct_plugin_sanitize_with_schema($data, $schema); - } - - /** - * Decorates the template data for REST responses. - * - * @return array REST data. - */ - public function data() - { - if (!$this->is_valid) { - return; - } - - return array_merge( - [ - 'id' => $this->id, - 'addon' => $this->addon, - ], - $this->data - ); - } - - private function get_post_id() - { - $ids = get_posts([ - 'post_type' => self::post_type, - 'name' => $this->name, - 'meta_key' => '_fb-addon', - 'meta_value' => $this->addon, - 'fields' => 'ids', - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - 'update_menu_item_cache' => false, - ]); - - if (count($ids)) { - return $ids[0]; - } - } - - public function save() - { - if (!$this->is_valid) { - return $this->data; - } - - $post_arr = [ - 'post_type' => self::post_type, - 'post_name' => $this->name, - 'post_title' => $this->title, - 'post_excerpt' => $this->description, - ]; - - $post_id = $this->get_post_id(); - if ($post_id) { - $post_arr['ID'] = $post_id; - $post_id = wp_update_post($post_arr, true); - } else { - $post_id = wp_insert_post($post_arr, true); - } - - if (!is_wp_error($post_id)) { - update_post_meta($post_id, '_template-fields', $this->fields); - update_post_meta($post_id, '_template-form', $this->form); - update_post_meta($post_id, '_template-bridge', $this->bridge); - update_post_meta($post_id, '_template-backend', $this->backend); - update_post_meta( - $post_id, - '_template-credential', - $this->credential - ); - } - - return $post_id; - } - - public function reset() - { - $post_id = $this->get_post_id(); - - if (!$post_id) { - return false; - } - - return wp_delete_post($post_id, true) instanceof WP_Post; - } - - /** - * Applies the input fields with the template's data to - * create a form and bind it with a bridge. - * - * @param array $fields User input fields data. - * @param string $integration Target integration. - */ - public function use($fields, $integration) - { - if (!$this->is_valid) { - return new WP_Error( - 'invalid_template', - __('The target template is invalid', 'forms-bridge') - ); - } - - $template = $this->data; - $schema = static::schema($this->addon); - - // Add constants to the user fields - foreach ($template['fields'] as $field) { - if (!empty($field['value'])) { - $fields[] = $field; - } - } - - $all_fields = wpct_plugin_merge_collection( - $fields, - $template['fields'], - $schema['properties']['fields']['items'] - ); - - $requireds = array_filter($all_fields, static function ($field) { - return ($field['required'] ?? false) && !isset($field['value']); - }); - - if (count($requireds) || count($fields) > count($all_fields)) { - return new WP_Error( - 'invalid_fields', - __('Invalid template fields', 'forms-bridge') - ); - } - - $data = $template; - foreach ($fields as $field) { - $is_required = $field['required'] ?? false; - - $field_schema = $schema['properties']['fields']['items']; - $field_schema['required'] = ['ref', 'name']; - - if ($is_required) { - $field_schema['required'][] = 'value'; - } - - $field = wpct_plugin_sanitize_with_schema($field, $field_schema); - - if (is_wp_error($field)) { - return new WP_Error( - 'invalid_field', - sprintf( - __( - /* translators: %s: Field name */ - 'Field `%s` does not match the schema', - 'forms-bridge' - ), - $field['name'] - ) - ); - } - - if (!isset($field['value'])) { - continue; - } - - if (is_array($field['value']) && empty($field['type'])) { - continue; - } - - if ($field['value'] === '') { - continue; - } - - if ($field['type'] === 'boolean') { - if (!isset($field['value'][0])) { - continue; - } else { - $field['value'] = '1'; - } - } - - // Inherit form field structure if field ref points to form fields - if ($field['ref'] === '#form/fields[]') { - $index = array_search( - $field['name'], - array_column($template['form']['fields'], 'name') - ); - - $form_field = $template['form']['fields'][$index]; - $field['index'] = $index; - $field['value'] = array_merge($form_field, [ - 'value' => $field['value'], - ]); - } elseif ( - $field['ref'] === '#backend/headers[]' || - $field['ref'] === '#bridge/custom_fields[]' - ) { - $field['value'] = [ - 'name' => $field['name'], - 'value' => $field['value'] ?? null, - ]; - } - - $keys = explode('/', substr($field['ref'], 1)); - $leaf = &$data; - foreach ($keys as $key) { - $clean_key = str_replace('[]', '', $key); - if (!isset($leaf[$clean_key])) { - return new WP_Error( - 'invalid_ref', - sprintf( - __( - /* translators: %s: ref value */ - 'Invalid template field ref `%s`', - 'forms-bridge' - ), - $field['ref'] - ) - ); - } - - $leaf = &$leaf[$clean_key]; - } - - if (substr($key, -2) === '[]') { - if (isset($field['index'])) { - $leaf[$field['index']] = $field['value']; - } else { - $leaf[] = $field['value']; - } - } elseif (isset($field['value'])) { - $leaf[$field['name']] = $field['value']; - } - } - - $data = apply_filters( - 'forms_bridge_template_data', - $data, - $this->id, - $this - ); - - if (is_wp_error($data)) { - return $data; - } elseif (empty($data)) { - return new WP_Error( - 'template_creation_error', - __('There is a problem with the template data', 'forms-bridge') - ); - } - - if ($integration === 'woo') { - $data['form']['id'] = 1; - } elseif ($integration === 'wpforms') { - $mappers = []; - foreach ($data['form']['fields'] as &$field) { - if ($field['type'] !== 'file') { - $mappers[] = [ - 'from' => JSON_Finger::sanitize_key($field['label']), - 'to' => $field['name'], - 'cast' => 'inherit', - ]; - } - - $field['name'] = $field['label']; - } - - $data['bridge']['mutations'][0] = array_merge( - $mappers, - $data['bridge']['mutations'][0] ?? [] - ); - } - - $integration_instance = Integration::integration($integration); - - try { - $create_form = !$this->form_exists( - $data['form']['id'] ?? null, - $integration - ); - - if ($create_form) { - do_action_ref_array('forms_bridge_before_template_form', [ - $data['form'], - $this->name, - $this, - ]); - - $form_id = $integration_instance->create_form($data['form']); - - if (!$form_id) { - return new WP_Error( - 'form_creation_error', - __( - 'Forms bridge can\'t create the form', - 'forms-bridge' - ), - ['status' => 400, 'data' => $data['form']] - ); - } - - $data['form']['id'] = $form_id; - - do_action( - 'forms_bridge_template_form', - $data['form'], - $this->id, - $this - ); - } - - $data['bridge']['form_id'] = - $integration . ':' . $data['form']['id']; - - $create_credential = false; - if (!empty($data['credential']['name'])) { - $create_credential = !$this->credential_exists( - $data['credential']['name'] - ); - - if ($create_credential) { - $result = $this->create_credential($data['credential']); - - if (!$result) { - if ($create_form) { - $integration_instance->remove_form( - $data['form']['id'] - ); - } - - return new WP_Error( - 'credential_creation_error', - __( - 'Forms bridge can\'t create the credential', - 'forms-bridge', - ['status' => 400, 'data' => $data['credential']] - ) - ); - } - } - - $data['backend']['credential'] = $data['credential']['name']; - } - - $create_backend = !$this->backend_exists($data['backend']['name']); - if ($create_backend) { - $result = $this->create_backend($data['backend']); - - if (!$result) { - if ($create_form) { - $integration_instance->remove_form($data['form']['id']); - } - - if ($create_credential) { - $this->remove_credential($data['credential']['name']); - } - - return new WP_Error( - 'backend_creation_error', - __( - 'Forms bridge can\'t create the backend', - 'forms-bridge', - ['status' => 400, 'data' => $data['backend']] - ) - ); - } - } - - $data['bridge']['backend'] = $data['backend']['name']; - - $bridge_created = $this->create_bridge($data['bridge']); - - if (!$bridge_created) { - if ($create_form) { - $integration_instance->remove_form( - $data['bridge']['form_id'] - ); - } - - if ($create_credential) { - $this->remove_credential($data['credential']['name']); - } - - if ($create_backend) { - $this->remove_backend($data['backend']['name']); - } - - return new WP_Error( - 'bridge_creation_error', - __( - 'Forms bridge can\'t create the form bridge', - 'forms-bridge', - ['status' => 400, 'data' => $data['bridge']] - ) - ); - } - } catch (Error | Exception $e) { - if (isset($create_form) && $create_form) { - $integration_instance->remove_form($data['form']['id']); - } - - if (isset($create_credential) && $create_credential) { - $this->remove_credential($data['credential']['name']); - } - - if (isset($create_backend) && $create_backend) { - $this->remove_backend($data['backend']['name']); - } - - if (isset($bridge_created) && $bridge_created) { - $this->remove_bridge($data['bridge']['name']); - } - - return new WP_Error('internal_server_error', $e->getMessage(), [ - 'status' => 500, - ]); - } - - return true; - } - - /** - * Checks if a form with the given id exists on the settings store. - * - * @param string $form_id Internal ID of the form. - * @param string $integration Slug of the target integration. - * - * @return boolean - */ - private function form_exists($form_id, $integration) - { - $form = FBAPI::get_form_by_id($form_id, $integration); - return !empty($form['id']); - } - - /** - * Checks if a backend with the given name exists on the settings store. - * - * @param string $name Backend name. - * - * @return boolean - */ - final protected function backend_exists($name) - { - $backends = Http_Store::setting('general')->backends ?: []; - return array_search($name, array_column($backends, 'name')) !== false; - } - - /** - * Stores the backend data on the settings store. - * - * @param array $data Backend data. - * - * @return boolean Creation result. - */ - private function create_backend($data) - { - $setting = Http_Store::setting('general'); - $backends = $setting->backends ?: []; - - do_action_ref_array('forms_bridge_before_template_backend', [ - $data, - $this->name, - $this, - ]); - - $setting->backends = array_merge($backends, [$data]); - $setting->flush(); - - $is_valid = $this->backend_exists($data['name']); - if (!$is_valid) { - return; - } - - do_action('forms_bridge_template_backend', $data, $this->id, $this); - return true; - } - - /** - * Removes backend from the settings store by name. - * - * @param string $name Backend name. - */ - private function remove_backend($name) - { - $setting = Http_Store::setting('general'); - $backends = $setting->backends ?: []; - - $setting->backends = array_filter($backends, static function ( - $backend - ) use ($name) { - return $backend['name'] !== $name; - }); - } - - /** - * Checks if a bridge with the given name exists on the settings store. - * - * @param string $form_id Internal ID of the form. - * @param string $integration Slug of the target integration. - * - * @return boolean - */ - private function bridge_exists($name) - { - $bridges = Settings_Store::setting($this->addon)->bridges ?: []; - return array_search($name, array_column($bridges, 'name')) !== false; - } - - /** - * Stores the form bridge data on the settings store. - * - * @param array $data Form bridge data. - * - * @return boolean Creation result. - */ - private function create_bridge($data) - { - $name_conflict = $this->bridge_exists($data['name']); - if ($name_conflict) { - return; - } - - $setting = Settings_Store::setting($this->addon); - $bridges = $setting->bridges ?: []; - - do_action_ref_array('forms_bridge_before_template_bridge', [ - $data, - $this->name, - $this, - ]); - - $setting->bridges = array_merge($bridges, [$data]); - $setting->flush(); - - $is_valid = $this->bridge_exists($data['name']); - if (!$is_valid) { - return; - } - - do_action('forms_bridge_template_bridge', $data, $this->id, $this); - return true; - } - - /** - * Removes a bridge from the settings store by name. - * - * @param string $name Bridge name. - */ - private function remove_bridge($name) - { - $setting = Settings_Store::setting($this->addon); - $bridges = $setting->bridges ?: []; - - $setting->bridges = array_filter($bridges, static function ( - $bridge - ) use ($name) { - return $bridge['name'] !== $name; - }); - } - - /** - * Checks if a credential with the given name exists on the settings store. - * - * @param string $name Credential name. - * - * @return boolean - */ - private function credential_exists($name) - { - $credentials = Http_Store::setting('general')->credentials ?: []; - return array_search($name, array_column($credentials, 'name')) !== - false; - } - - /** - * Stores the bridge credential data on the settings store. - * - * @param array $data Credential data. - * - * @return boolean Creation result. - */ - private function create_credential($data) - { - $setting = Http_Store::setting('general'); - $credentials = $setting->credentials ?: []; - - if (!is_array($credentials)) { - return; - } - - do_action_ref_array('forms_bridge_before_template_credential', [ - $data, - $this->name, - $this, - ]); - - $setting->credentials = array_merge($credentials, [$data]); - $setting->flush(); - - $is_valid = $this->credential_exists($data['name']); - if (!$is_valid) { - return; - } - - do_action('forms_bridge_template_credential', $data, $this->id, $this); - return true; - } - - /** - * Removes a credential from the settings store by name. - * - * @param string $name Credential name. - */ - private function remove_credential($name) - { - $setting = Http_Store::setting('general'); - $credentials = $setting->credentials ?: []; - - $setting->credentials = array_filter($credentials, static function ( - $credential - ) use ($name) { - return $credential['name'] !== $name; - }); - } -} diff --git a/includes/class-form-bridge.php b/includes/class-form-bridge.php deleted file mode 100644 index 97280d12..00000000 --- a/includes/class-form-bridge.php +++ /dev/null @@ -1,919 +0,0 @@ - 'http://json-schema.org/draft-04/schema#', - 'title' => 'form-bridge', - 'type' => 'object', - 'properties' => [ - 'name' => [ - 'title' => _x('Name', 'Bridge schema', 'forms-bridge'), - 'description' => __( - 'Unique name of the bridge', - 'forms-bridge' - ), - 'type' => 'string', - 'minLength' => 1, - ], - 'form_id' => [ - 'title' => _x('Form', 'Bridge schema', 'forms-bridge'), - 'description' => __( - 'Internal form id with integration prefix', - 'forms-bridge' - ), - 'type' => 'string', - 'pattern' => '^\w+:\d+$', - 'default' => '', - ], - 'backend' => [ - 'title' => _x('Backend', 'Bridge schema', 'forms-bridge'), - 'description' => __('Backend name', 'forms-bridge'), - 'type' => 'string', - // 'default' => '', - ], - 'endpoint' => [ - 'title' => _x('Endpoint', 'Bridge schema', 'forms-bridge'), - 'description' => __('HTTP API endpoint', 'forms-bridge'), - 'type' => 'string', - 'default' => '/', - ], - 'method' => [ - 'title' => _x('Method', 'Bridge schema', 'forms-bridge'), - 'description' => __('HTTP method', 'forms-bridge'), - 'type' => 'string', - 'enum' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], - 'default' => 'POST', - ], - 'custom_fields' => [ - 'description' => __( - 'Array of bridge\'s custom fields', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => [ - 'type' => 'string', - 'minLength' => 1, - 'validate_callback' => - '\FORMS_BRIDGE\JSON_Finger::validate', - ], - 'value' => [ - 'type' => ['string', 'integer', 'number'], - 'minLength' => 1, - ], - ], - 'additionalProperties' => false, - 'required' => ['name', 'value'], - ], - 'default' => [], - ], - 'mutations' => [ - 'description' => __( - 'Stack of bridge mutations', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'additionalProperties' => false, - 'properties' => [ - 'from' => [ - 'type' => 'string', - 'minLength' => 1, - 'validate_callback' => - '\FORMS_BRIDGE\JSON_Finger::validate', - ], - 'to' => [ - 'type' => 'string', - 'minLength' => 1, - 'validate_callback' => - '\FORMS_BRIDGE\JSON_Finger::validate', - ], - 'cast' => [ - 'type' => 'string', - 'enum' => [ - 'boolean', - 'string', - 'integer', - 'number', - 'not', - 'and', - 'or', - 'xor', - 'json', - 'csv', - 'concat', - 'join', - 'sum', - 'count', - 'inherit', - 'copy', - 'null', - ], - ], - ], - 'additionalProperties' => false, - 'required' => ['from', 'to', 'cast'], - ], - ], - 'default' => [], - ], - 'workflow' => [ - 'description' => __( - 'Chain of workflow job names', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => [ - 'type' => 'string', - 'minLength' => 1, - ], - 'default' => [], - ], - 'is_valid' => [ - 'description' => __( - 'Validation result of the bridge setting', - 'forms-bridge' - ), - 'type' => 'boolean', - 'default' => true, - ], - 'enabled' => [ - 'description' => __( - 'Boolean flag to enable/disable a bridge', - 'forms-bridge' - ), - 'type' => 'boolean', - 'default' => true, - ], - ], - 'required' => [ - 'name', - 'form_id', - 'backend', - 'method', - 'endpoint', - 'custom_fields', - 'mutations', - 'workflow', - 'is_valid', - 'enabled', - ], - 'additionalProperties' => false, - ]; - - if (!$addon) { - return $schema; - } - - return apply_filters('forms_bridge_bridge_schema', $schema, $addon); - } - - /** - * Handles the form bridge's settings data. - * - * @var array - */ - protected $data; - - protected $id; - - /** - * Handles form bridge's addon slug. - * - * @var string - */ - protected $addon; - - /** - * Stores the form bridge's data as a private attribute. - */ - public function __construct($data, $addon = 'rest') - { - $this->data = wpct_plugin_sanitize_with_schema( - $data, - static::schema($addon) - ); - $this->addon = $addon; - - if ($this->is_valid) { - $this->id = $addon . '-' . $data['name']; - } - } - - public function data() - { - if (!$this->is_valid) { - return; - } - - return array_merge($this->data, [ - 'id' => $this->id, - 'name' => $this->name, - 'addon' => $this->addon, - ]); - } - - /** - * Magic method to proxy public attributes to method getters. - * - * @param string $name Attribute name. - * - * @return mixed Attribute value or null. - */ - public function __get($name) - { - switch ($name) { - case 'id': - return $this->id; - case 'addon': - return $this->addon; - case 'form': - return $this->form(); - case 'integration': - return $this->integration(); - case 'backend': - return $this->backend(); - case 'content_type': - return $this->content_type(); - case 'workflow': - return $this->workflow(); - case 'is_valid': - return !is_wp_error($this->data) && - $this->data['is_valid'] && - Addon::addon($this->addon) !== null; - default: - if (!$this->is_valid) { - return; - } - - return $this->data[$name] ?? null; - } - } - - /** - * Retrives the bridge's backend instance. - * - * @return Backend|null - */ - protected function backend() - { - if (!$this->is_valid) { - return; - } - - return FBAPI::get_backend($this->data['backend']); - } - - /** - * Retrives the bridge's form data. - * - * @return array|null - */ - protected function form() - { - $form_id = $this->form_id; - if (!$form_id) { - return; - } - - if (!preg_match('/^\w+:\d+$/', $form_id)) { - return; - } - - [$integration, $form_id] = explode(':', $form_id); - return FBAPI::get_form_by_id($form_id, $integration); - } - - /** - * Retrives the bridge's integration name. - * - * @return string - */ - protected function integration() - { - $form_id = $this->form_id; - if (!$form_id) { - return; - } - - if (!preg_match('/^\w+:\d+$/', $form_id)) { - return; - } - - [$integration] = explode(':', $form_id); - return $integration; - } - - /** - * Gets bridge's default body encoding schema. - * - * @return string|null - */ - protected function content_type() - { - if (!$this->is_valid) { - return; - } - - $backend = FBAPI::get_backend($this->data['backend']); - if (!$backend) { - return; - } - - return $backend->content_type; - } - - /** - * Gets bridge's workflow instance. - * - * @return Workflow_Job|null; - */ - protected function workflow() - { - if (!$this->is_valid) { - return; - } - - return Job::from_workflow($this->data['workflow'], $this->addon); - } - - /** - * Submits payload and attachments to the bridge's backend. - * - * @param array $payload Payload data. - * @param array $attachments Submission's attached files. - * - * @return array|WP_Error Http request response. - */ - public function submit($payload = [], $attachments = []) - { - if (!$this->is_valid) { - return new WP_Error('invalid_bridge'); - } - - $schema = $this->schema(); - - if ( - !in_array( - $this->method, - $schema['properties']['method']['enum'], - true - ) - ) { - return new WP_Error( - 'method_not_allowed', - sprintf( - /* translators: %s: method name */ - __('HTTP method %s is not allowed', 'forms-bridge'), - sanitize_text_field($this->method) - ), - ['method' => $this->method] - ); - } - - $backend = $this->backend(); - if (!$backend) { - return new WP_Error('invalid_bridge'); - } - - $method = $this->method; - - return $backend->$method($this->endpoint, $payload, [], $attachments); - } - - /** - * Apply cast mappers to data. - * - * @param array $data Array of data. - * - * @return array Data modified by the bridge's mappers. - */ - final public function apply_mutation($data, $mutation = null) - { - if (!is_array($data)) { - return $data; - } - - $finger = new JSON_Finger($data); - - if ($mutation === null) { - $mutation = $this->mutations[0] ?? []; - } - - foreach ($mutation as $mapper) { - $is_valid = - JSON_Finger::validate($mapper['from']) && - JSON_Finger::validate($mapper['to']); - - if (!$is_valid) { - continue; - } - - $isset = $finger->isset($mapper['from'], $is_conditional); - if (!$isset) { - if ($is_conditional) { - continue; - } - - $value = null; - } else { - $value = $finger->get($mapper['from']); - } - - $unset = $mapper['cast'] === 'null'; - - if ($mapper['cast'] !== 'copy') { - $unset = - $unset || - preg_replace('/^\?/', '', $mapper['from']) !== - $mapper['to']; - } - - if ($unset) { - $finger->unset($mapper['from']); - } - - if ($mapper['cast'] !== 'null') { - $finger->set($mapper['to'], $this->cast($value, $mapper)); - } - } - - return $finger->data(); - } - - /** - * Casts value to the given type. - * - * @param mixed $value Original value. - * @param string $type Target type to cast value. - * - * @return mixed - */ - private function cast($value, $mapper) - { - if (strpos($mapper['from'], '[]') !== false) { - return $this->cast_expanded($value, $mapper); - } - - switch ($mapper['cast']) { - case 'string': - return (string) $value; - case 'integer': - return (int) $value; - case 'number': - return (float) $value; - case 'boolean': - return (bool) $value; - case 'not': - return !$value; - case 'and': - return array_reduce( - (array) $value, - fn($bool, $val) => $bool && $val, - !empty($val) - ); - case 'or': - return array_reduce( - (array) $value, - fn($bool, $val) => $bool || $val, - false - ); - case 'xor': - return array_reduce( - (array) $value, - fn($bool, $val) => $bool xor $val, - false - ); - case 'json': - if (!is_array($value)) { - return ''; - } - - return wp_json_encode($value, JSON_UNESCAPED_UNICODE); - case 'csv': - if (!wp_is_numeric_array($value)) { - return ''; - } - - return implode(',', $value); - case 'concat': - if (!wp_is_numeric_array($value)) { - return ''; - } - - return implode(' ', $value); - case 'join': - if (!wp_is_numeric_array($value)) { - return ''; - } - - return implode('', $value); - case 'sum': - if (!wp_is_numeric_array($value)) { - return 0; - } - - return array_reduce( - (array) $value, - static function ($total, $val) { - return $total + $val; - }, - 0 - ); - case 'count': - if (!is_array($value)) { - return 0; - } - - return count((array) $value); - case 'inherit': - return $value; - case 'copy': - return $value; - case 'null': - return; - default: - return (string) $value; - } - } - - private function cast_expanded($values, $mapper) - { - if (!wp_is_numeric_array($values)) { - return []; - } - - $is_expanded = - strpos(preg_replace('/\[\]$/', '', $mapper['from']), '[]') !== - false; - - if (!$is_expanded) { - return array_map(function ($value) use ($mapper) { - return $this->cast($value, [ - 'from' => '', - 'to' => '', - 'cast' => $mapper['cast'], - ]); - }, $values); - } - - preg_match_all( - '/\[\](?=[^\[])/', - preg_replace('/\[\]$/', '', $mapper['to']), - $to_expansions - ); - preg_match_all( - '/\[\](?=[^\[])/', - preg_replace('/\[\]$/', '', $mapper['from']), - $from_expansions - ); - - if (empty($from_expansions) && count($to_expansions) > 1) { - return []; - } elseif ( - !empty($from_expansions) && - count($to_expansions[0]) > count($from_expansions[0]) - ) { - return []; - } - - $parts = array_filter(explode('[]', $mapper['from'])); - $before = $parts[0]; - $after = implode('[]', array_slice($parts, 1)); - - for ($i = 0; $i < count($values); $i++) { - $pointer = "{$before}[{$i}]{$after}"; - $values[$i] = $this->cast($values[$i], [ - 'from' => $pointer, - 'to' => '', - 'cast' => $mapper['cast'], - ]); - } - - return $values; - } - - final public function prepare_mappers($form) - { - foreach ($form['fields'] as $field) { - $is_file = $field['is_file'] ?? false; - $is_conditional = $field['conditional'] ?? false; - $is_multi = $field['is_multi'] ?? false; - - $schema = $field['schema'] ?? ['type' => 'file']; - - if ( - $schema['type'] === 'array' && - ($schema['additionalItems'] ?? true) === false - ) { - $min_items = $field['schema']['minItems'] ?? 0; - $max_items = $field['schema']['maxItems'] ?? 0; - - $is_conditional = $is_conditional || $min_items < $max_items; - } - - if ($is_conditional) { - $name = $field['name']; - - for ($i = 0; $i < count($this->data['mutations']); $i++) { - $mutation = $this->data['mutations'][$i]; - - for ($j = 0; $j < count($mutation); $j++) { - $mapper = $this->data['mutations'][$i][$j]; - - $from = preg_replace('/\[\d*\]/', '', $mapper['from']); - if ( - $from === $name || - ($is_file && $from === $name . '_filename') - ) { - $this->data['mutations'][$i][$j]['from'] = - '?' . $mapper['from']; - - $name = preg_replace( - '/\[\d*\]/', - '', - $mapper['to'] - ); - } - } - } - } - - if ($is_file && $is_multi) { - $name = $field['name']; - - $len = count($this->data['mutations'][0] ?? []); - for ($i = 0; $i < $len; $i++) { - $mapper = $this->data['mutations'][0][$i]; - - $from = preg_replace('/\[\d*\]/', '', $mapper['from']); - $from = preg_replace('/^\?/', '', $mapper['from']); - - if ($from !== $name && $from !== $name . '_filename') { - continue; - } - - $this->data['mutations'][0][$i]['from'] = - $mapper['from'] . '_1'; - $this->data['mutations'][0][$i]['to'] = - $mapper['to'] . '_1'; - - for ($j = 2; $j < 10; $j++) { - $from = - strstr($mapper['from'], '?') ?: - '?' . $mapper['from']; - - $this->data['mutations'][0][] = [ - 'from' => $from . '_' . $j, - 'to' => $mapper['to'] . '_' . $j, - 'cast' => $mapper['cast'], - ]; - } - } - } - } - } - - private static function get_tags() - { - return [ - 'site_title' => static function () { - return get_bloginfo('name'); - }, - 'site_description' => static function () { - return get_bloginfo('description'); - }, - 'blog_url' => static function () { - return get_bloginfo('wpurl'); - }, - 'site_url' => static function () { - return get_bloginfo('url'); - }, - 'admin_email' => static function () { - return get_bloginfo('admin_email'); - }, - 'wp_version' => static function () { - return get_bloginfo('version'); - }, - 'ip_address' => static function () { - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { - return sanitize_text_field( - $_SERVER['HTTP_X_FORWARDED_FOR'] - ); - } elseif (isset($_SERVER['REMOTE_ADDR'])) { - return sanitize_text_field($_SERVER['REMOTE_ADDR']); - } - }, - 'referer' => static function () { - if (isset($_SERVER['HTTP_REFERER'])) { - return sanitize_text_field($_SERVER['HTTP_REFERER']); - } - }, - 'user_agent' => static function () { - if (isset($_SERVER['HTTP_USER_AGENT'])) { - return sanitize_text_field($_SERVER['HTTP_USER_AGENT']); - } - }, - 'browser_locale' => static function () { - if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - return sanitize_text_field( - $_SERVER['HTTP_ACCEPT_LANGUAGE'] - ); - } - }, - 'locale' => static function () { - return apply_filters( - 'wpct_i18n_current_language', - get_locale(), - 'locale' - ); - }, - 'language' => static function () { - include_once ABSPATH . - 'wp-admin/includes/translation-install.php'; - $translations = wp_get_available_translations(); - $locale = apply_filters( - 'wpct_i18n_current_language', - get_locale(), - 'locale' - ); - return $translations[$locale]['native_name'] ?? $locale; - }, - 'datetime' => static function () { - return date('Y-m-d H:i:s', time()); - }, - 'gmt_datetime' => static function () { - return gmdate('Y-m-d H:i:s', time()); - }, - 'timestamp' => static function () { - return time(); - }, - 'iso_date' => static function () { - return date('c', time()); - }, - 'gmt_iso_date' => static function () { - return gmdate('c', time()); - }, - 'utc_date' => static function () { - $date = gmdate('c', time()); - return preg_replace('/\+\d+\:\d+$/', 'Z', $date); - }, - 'user_id' => static function () { - $user = wp_get_current_user(); - return $user->ID; - }, - 'user_login' => static function () { - $user = wp_get_current_user(); - return $user->user_login; - }, - 'user_name' => static function () { - $user = wp_get_current_user(); - return $user->display_name; - }, - 'user_email' => static function () { - $user = wp_get_current_user(); - return $user->user_email; - }, - 'submission_id' => static function () { - return FBAPI::get_submission_id(); - }, - 'form_title' => static function () { - $form = FBAPI::get_current_form(); - return $form['title'] ?? null; - }, - 'form_id' => static function () { - $form = FBAPI::get_current_form(); - return $form['id'] ?? null; - }, - ]; - } - - final public function add_custom_fields($payload = []) - { - if (!is_array($payload)) { - return $payload; - } - - $finger = new JSON_Finger($payload); - - $custom_fields = $this->custom_fields ?: []; - - foreach ($custom_fields as $custom_field) { - $is_value = JSON_Finger::validate($custom_field['name']); - if (!$is_value) { - continue; - } - - $value = $this->replace_field_tags($custom_field['value']); - $finger->set($custom_field['name'], $value); - } - - return $finger->data(); - } - - private function replace_field_tags($value) - { - $tags = self::get_tags(); - foreach ($tags as $tag => $getter) { - if (strstr($value, '$' . $tag) !== false) { - $value = str_replace('$' . $tag, $getter(), $value); - } - } - - return $value; - } - - /** - * Returns a clone of the bridge instance with its data patched by - * the partial array. - * - * @param array $partial Bridge data. - * - * @return Form_Bridge - */ - public function patch($partial = []) - { - if (!$this->is_valid) { - return $this; - } - - $data = array_merge($this->data, $partial); - return new static($data, $this->addon); - } - - public function save() - { - if (!$this->is_valid) { - return false; - } - - $setting = Settings_Store::setting($this->addon); - if (!$setting) { - return false; - } - - $bridges = $setting->bridges ?: []; - - $index = array_search($this->name, array_column($bridges, 'name')); - - if ($index === false) { - $bridges[] = $this->data; - } else { - $bridges[$index] = $this->data; - } - - $setting->bridges = $bridges; - - return true; - } - - public function delete() - { - if (!$this->is_valid) { - return false; - } - - $setting = Settings_Store::setting($this->addon); - if (!$setting) { - return false; - } - - $bridges = $setting->bridges ?: []; - - $index = array_search($this->name, array_column($bridges, 'name')); - - if ($index === false) { - return false; - } - - array_splice($bridges, $index, 1); - $setting->bridges = $bridges; - - return true; - } -} diff --git a/includes/class-integration.php b/includes/class-integration.php deleted file mode 100644 index c442f274..00000000 --- a/includes/class-integration.php +++ /dev/null @@ -1,441 +0,0 @@ - $integrations. - */ - private static $integrations = []; - - private static function check_dependencies($integration) - { - switch ($integration) { - case 'wpcf7': - $dep = 'contact-form-7/wp-contact-form-7.php'; - break; - case 'gf': - $dep = 'gravityforms/gravityforms.php'; - break; - case 'wpforms': - $dep = 'wpforms/wpforms.php'; - break; - case 'ninja': - $dep = 'ninja-forms/ninja-forms.php'; - break; - case 'woo': - $dep = 'woocommerce/woocommerce.php'; - break; - // case 'formidable': - // $plugin = 'formidable/formidable.php'; - // break; - // case 'forminator': - // $plugin = 'forminator/forminator.php'; - // break; - default: - return false; - } - - return Forms_Bridge::is_plugin_active($dep); - } - - /** - * Public integrations registry getter. - * - * @return array Integration registry state. - */ - private static function registry() - { - $state = get_option(self::registry, []) ?: []; - $integrations_dir = FORMS_BRIDGE_INTEGRATIONS_DIR; - $integrations = array_diff(scandir($integrations_dir), ['.', '..']); - - $with_deps = []; - $registry = []; - foreach ($integrations as $integration) { - $integration_dir = "{$integrations_dir}/{$integration}"; - if (!is_dir($integration_dir)) { - continue; - } - - $has_deps = self::check_dependencies($integration); - if ($has_deps) { - $with_deps[] = $integration; - } - - $index = "{$integration_dir}/class-{$integration}-integration.php"; - - if (is_file($index) && is_readable($index)) { - $registry[$integration] = - boolval($state[$integration] ?? false) && $has_deps; - } - } - - if (count($with_deps) === 1) { - $registry[$with_deps[0]] = true; - } - - return $registry; - } - - /** - * Updates the integration's registry state. - * - * @param array $integrations New integrations' registry state. - */ - private static function update_registry($integrations = []) - { - $registry = self::registry(); - foreach ($integrations as $name => $enabled) { - if (!isset($registry[$name])) { - continue; - } - - $registry[$name] = (bool) $enabled; - } - - update_option(self::registry, $registry); - } - - /** - * Public active integration instances getter. - * - * @return array List with integration instances. - */ - final public static function integrations() - { - $integrations = []; - foreach (self::$integrations as $instance) { - if ($instance->enabled) { - $integrations[] = $instance; - } - } - - return $integrations; - } - - final public static function integration($name) - { - return self::$integrations[$name] ?? null; - } - - /** - * Public integrations loader. - */ - public static function load_integrations() - { - $integrations_dir = FORMS_BRIDGE_INTEGRATIONS_DIR; - $registry = self::registry(); - - foreach ($registry as $integration => $enabled) { - $has_dependencies = self::check_dependencies($integration); - - if ($has_dependencies) { - require_once "{$integrations_dir}/{$integration}/class-{$integration}-integration.php"; - - if ($enabled) { - self::$integrations[$integration]->load(); - } - } - } - - Settings_Store::ready(function ($store) { - $store::use_getter('general', function ($data) { - $registry = self::registry(); - $integrations = []; - foreach (self::$integrations as $name => $integration) { - $integrations[$name] = [ - 'name' => $name, - 'title' => $integration::title, - 'enabled' => $registry[$name] ?? false, - ]; - } - - ksort($integrations); - $integrations = array_values($integrations); - - if (count($integrations) === 1) { - $integrations[0]['enabled'] = true; - } - - $integrations = apply_filters( - 'forms_bridge_integrations', - $integrations - ); - return array_merge($data, ['integrations' => $integrations]); - }); - - $store::use_setter( - 'general', - function ($data) { - if ( - !isset($data['integrations']) || - !is_array($data['integrations']) - ) { - return $data; - } - - $registry = []; - foreach ($data['integrations'] as $integration) { - $registry[$integration['name']] = - (bool) $integration['enabled']; - } - - self::update_registry($registry); - - unset($data['integrations']); - return $data; - }, - 9 - ); - }); - - add_filter( - 'forms_bridge_load_templates', - static function ($templates) use ($registry) { - $integrations = []; - foreach ($registry as $integration => $enabled) { - if ($enabled) { - $integrations[] = $integration; - } - } - - $woomode = - count($integrations) === 1 && $integrations[0] === 'woo'; - - $filtered_templates = []; - foreach ($templates as $template) { - if (!isset($template['integrations'])) { - if ($woomode) { - continue; - } - - $filtered_templates[] = $template; - } elseif ( - count( - array_intersect( - $integrations, - $template['integrations'] - ) - ) - ) { - $filtered_templates[] = $template; - } - } - - return $filtered_templates; - }, - 5, - 1 - ); - } - - public static function setup(...$args) - { - return static::get_instance(...$args); - } - - public $enabled = false; - - protected function construct(...$args) - { - self::$integrations[static::name] = $this; - } - - public function load() - { - add_action('init', function () { - $this->init(); - }); - - // Gets available forms' data. - add_filter( - 'forms_bridge_forms', - function ($forms, $integration = null) { - if (!wp_is_numeric_array($forms)) { - $forms = []; - } - - if ($integration && $integration !== static::name) { - return $forms; - } - - $forms = array_merge($forms, $this->forms()); - return $forms; - }, - 9, - 2 - ); - - // Gets form data by context or by ID - add_filter( - 'forms_bridge_form', - function ($form, $form_id = null, $integration = null) { - if (is_array($form) && isset($form['id']) && $form['id']) { - return $form; - } - - if ($form_id) { - if (preg_match('/^(\w+):(\d+)$/', $form_id, $matches)) { - [, $integration, $form_id] = $matches; - $form_id = (int) $form_id; - } elseif (empty($integration)) { - return $form; - } - } - - if ($integration && $integration !== static::name) { - return $form; - } - - if ($form_id) { - return $this->get_form_by_id($form_id); - } - - return $this->form(); - }, - 9, - 3 - ); - - // Gets current submission data - add_filter( - 'forms_bridge_submission', - function ($submission, $raw = false) { - return $this->submission($raw) ?: $submission; - }, - 9, - 2 - ); - - add_filter( - 'forms_bridge_submission_id', - function ($submission_id) { - return $this->submission_id() ?: $submission_id; - }, - 9, - 1 - ); - - // Gets curent submission uploads - add_filter( - 'forms_bridge_uploads', - function ($uploads) { - return $this->uploads() ?: $uploads; - }, - 9, - 1 - ); - - $this->enabled = true; - } - - /** - * Integration initializer to be fired on wp init. - */ - protected function init() {} - - /** - * Retrives the current form. - * - * @return array Form data. - */ - public function form() - { - return; - } - - /** - * Retrives form by ID. - * - * @return array Form data. - */ - public function get_form_by_id($form_id) - { - return; - } - - /** - * Retrives available forms. - * - * @return array Collection of form data. - */ - public function forms() - { - return []; - } - - /** - * Creates a form from a given template fields. - * - * @param array $data Form template data. - * - * @return int|null ID of the new form. - */ - public function create_form($data) - { - return; - } - - /** - * Removes a form by ID. - * - * @param integer $form_id Form ID. - * - * @return boolean Removal result. - */ - public function remove_form($form_id) - { - return false; - } - - public function submission_id() - { - return; - } - - /** - * Retrives the current form submission. - * - * @param boolean $raw Control if the submission is serialized before exit. - * - * @return array|null Submission data. - */ - public function submission($raw) - { - return; - } - - /** - * Retrives the current submission uploaded files. - * - * @return array|null Collection of uploaded files. - */ - public function uploads() - { - return; - } -} diff --git a/includes/class-job.php b/includes/class-job.php deleted file mode 100644 index 25bf9db5..00000000 --- a/includes/class-job.php +++ /dev/null @@ -1,679 +0,0 @@ - 'http://json-schema.org/draft-04/schema#', - 'title' => 'job', - 'type' => 'object', - 'properties' => [ - 'name' => [ - 'title' => _x('Name', 'Job schema', 'forms-bridge'), - 'description' => __( - 'Internal name of the job', - 'forms-bridge' - ), - 'type' => 'string', - 'minLength' => 1, - ], - 'title' => [ - 'title' => _x('Title', 'Job schema', 'forms-bridge'), - 'description' => __( - 'Public title of the job', - 'forms-bridge' - ), - 'type' => 'string', - 'minLength' => 1, - ], - 'description' => [ - 'title' => _x('Description', 'Job schema', 'forms-bridge'), - 'description' => __( - 'Short description of the job effects', - 'forms-birdge' - ), - 'type' => 'string', - 'default' => '', - ], - 'method' => [ - 'title' => _x('Method', 'Job schema', 'forms-bridge'), - 'description' => __( - 'Name of the function with the job subroutine', - 'forms-bridge' - ), - 'type' => 'string', - 'minLength' => 1, - ], - 'input' => [ - 'title' => _x('Input', 'Job schema', 'forms-bridge'), - 'description' => __( - 'Input fields interface schema of the job', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => [ - 'type' => 'string', - 'minLength' => 1, - ], - 'required' => ['type' => 'boolean'], - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'type' => [ - 'type' => 'string', - 'enum' => [ - 'string', - 'integer', - 'number', - 'array', - 'object', - 'boolean', - 'null', - ], - ], - 'items' => [ - 'type' => ['array', 'object'], - 'additionalProperties' => true, - 'additionalItems' => true, - ], - 'properties' => [ - 'type' => 'object', - 'additionalProperties' => true, - ], - 'maxItems' => ['type' => 'integer'], - 'minItems' => ['type' => 'integer'], - 'additionalProperties' => [ - 'type' => 'boolean', - ], - 'additionalItems' => ['type' => 'boolean'], - 'required' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - 'additionalItems' => true, - ], - ], - 'required' => ['type'], - 'additionalProperties' => false, - 'default' => ['type' => 'string'], - ], - ], - 'required' => ['name', 'schema'], - 'additionalProperties' => false, - ], - 'default' => [], - ], - 'output' => [ - 'title' => _x('Output', 'Job schema', 'forms-bridge'), - 'description' => __( - 'Output fields interface schema of the job', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'name' => [ - 'type' => 'string', - 'minLength' => 1, - ], - 'requires' => [ - 'type' => 'array', - 'items' => ['type' => 'string'], - ], - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'type' => [ - 'type' => 'string', - 'enum' => [ - 'string', - 'integer', - 'number', - 'array', - 'object', - 'boolean', - 'null', - ], - ], - 'items' => [ - 'type' => ['array', 'object'], - 'additionalProperties' => true, - 'additionalItems' => true, - ], - 'properties' => [ - 'type' => 'object', - 'additionalProperties' => true, - ], - 'maxItems' => ['type' => 'integer'], - 'minItems' => ['type' => 'integer'], - 'additionalProperties' => [ - 'type' => 'boolean', - ], - 'additionalItems' => ['type' => 'boolean'], - ], - 'required' => ['type'], - 'additionalProperties' => false, - 'default' => ['type' => 'string'], - ], - ], - 'required' => ['name', 'schema'], - 'additionalProperties' => false, - ], - 'default' => [], - ], - 'snippet' => [ - 'title' => _x('Snippet', 'Job schema', 'forms-bridge'), - 'description' => __( - 'PHP code representation of the job subroutine', - 'forms-bridge' - ), - 'type' => 'string', - ], - 'post_id' => ['type' => 'integer'], - ], - 'additionalProperties' => false, - 'required' => [ - 'name', - 'title', - 'description', - 'input', - 'output', - 'method', - 'snippet', - ], - ]; - } - - /** - * Enqueue the job instance as the last element of the workflow chain. - * - * @param string[] $workflow Array with job names. - * @param string $addon Job addon namespace. - * - * @return Job|null - */ - public static function from_workflow($workflow, $addon) - { - $workflow_jobs = []; - $jobs = FBAPI::get_addon_jobs($addon); - - $i = count($workflow) - 1; - while ($job_name = $workflow[$i] ?? null) { - foreach ($jobs as $job) { - if ($job->name === $job_name) { - $workflow_jobs[] = $job; - break; - } - } - - $i--; - } - - $next = null; - foreach ($workflow_jobs as $job) { - $job = clone $job; - $job->chain($next); - $next = $job; - } - - return $next; - } - - private static function reflect_method($method) - { - if (!function_exists($method)) { - return ''; - } - - $reflection = new ReflectionFunction($method); - - $file = $reflection->getFileName(); - $from_line = $reflection->getStartLine(); - $to_line = $reflection->getEndLine(); - - $snippet = implode( - '', - array_slice(file($file), $from_line - 1, $to_line - $from_line + 1) - ); - - if ($_snippet = strstr($snippet, '{')) { - $snippet = substr($_snippet, 1); - } - - $i = strlen($snippet); - while (true) { - $i--; - - if ($snippet[$i] === '}' || $i <= 0) { - break; - } - } - - $snippet = substr($snippet, 0, $i); - - $indentation = ''; - if (preg_match('/^\s+/', $snippet, $matches)) { - $indentation = preg_replace('/(\n|\t)+/', '', $matches[0]); - } - - $snippet = trim($snippet); - $snippet = preg_replace('/return \$payload;$/', '', $snippet); - - return $indentation . trim($snippet); - } - - private static function load_snippet($snippet, $name, $addon) - { - $id = $addon . '_' . $name; - - try { - $method_name = str_replace('-', '_', "forms_bridge_job_{$id}"); - - $method = - 'if (!function_exists(\'' . $method_name . '\')) {' . "\n"; - $method .= - 'function ' . $method_name . '($payload, $bridge) {' . "\n"; - $method .= $snippet . "\n"; - $method .= 'return $payload;' . "\n"; - $method .= "}\n}\n"; - - eval($method); - return $method_name; - } catch (ParseError $e) { - Logger::log("Syntax error on {$id} job snippet", Logger::ERROR); - Logger::log($e, Logger::ERROR); - } catch (Error $e) { - Logger::log("Error while loading {$id} job snippet", Logger::ERROR); - Logger::log($e, Logger::ERROR); - } - } - - private static function data_from_post($post) - { - return [ - 'name' => $post->post_name, - 'title' => $post->post_title, - 'description' => $post->post_excerpt, - 'input' => - (array) (get_post_meta($post->ID, '_job-input', true) ?: []), - 'output' => - (array) (get_post_meta($post->ID, '_job-output', true) ?: []), - 'snippet' => $post->post_content, - 'post_id' => $post->ID, - ]; - } - - /** - * Sets the job addon and name attributes, validates the data and enqueue themself to the job public - * filter getters. - * - * @param string $name Job name. - * @param array $data Job data. - * @param string $addon Addon name. - */ - public function __construct($data, $addon) - { - if ($data instanceof WP_Post) { - $data = self::data_from_post($data); - } - - $this->addon = $addon; - $this->data = $this->validate($data); - - if ($this->is_valid) { - $this->id = $addon . '-' . $data['name']; - } - } - - /** - * Magic method to proxy private attributes. - * - * @param string $name Attribute name. - * - * @return mixed Attribute value or null. - */ - public function __get($name) - { - switch ($name) { - case 'id': - return $this->id; - case 'addon': - return $this->addon; - case 'next': - return $this->next; - case 'data': - return $this->data; - case 'is_valid': - return !is_wp_error($this->data) && - Addon::addon($this->addon) !== null; - default: - if (!$this->is_valid) { - return; - } - - return $this->data[$name] ?? null; - } - } - - /** - * Sets the next job on the chain. - * - * @param Job $job Job instance to be queued as the next item of a workflow chain. - */ - public function chain($job) - { - $this->next = $job; - } - - /** - * Gets the payload from the previous workflow stage and runs the job against it. - * - * @param array $payload Payload data. - * @param Form_Bridge Workflow's bridge owner instance. - * @param array $mutations Bridge's mutations. - * - * @return array|null Payload after job. - */ - public function run($payload, $bridge, $mutations = null) - { - $original = $payload; - - if ($mutations === null) { - $mutations = array_slice($bridge->mutations, 1); - } - - if ($this->missing_requireds($payload)) { - if ($next_job = $this->next) { - $mutations = array_slice($mutations, 1); - $payload = $next_job->run($payload, $bridge, $mutations); - } - - return $payload; - } - - $method = $this->method; - $payload = $method($payload, $bridge, $this); - - if (empty($payload)) { - return; - } elseif (is_wp_error($payload)) { - $error = $payload; - do_action('forms_bridge_on_failure', $bridge, $error, $original); - return; - } - - $payload = $this->output_payload($payload); - - $mutation = array_shift($mutations) ?: []; - $payload = $bridge->apply_mutation($payload, $mutation); - - if ($next_job = $this->next) { - $payload = $next_job->run($payload, $bridge, $mutations); - } - - return $payload; - } - - /** - * Job data serializer to be used on REST API response. - * - * @return array - */ - public function data() - { - if (!$this->is_valid) { - return; - } - - return array_merge( - [ - 'id' => $this->id, - 'addon' => $this->addon, - ], - $this->data - ); - } - - private function get_post_id() - { - $ids = get_posts([ - 'post_type' => self::post_type, - 'name' => $this->name, - 'meta_key' => '_fb-addon', - 'meta_value' => $this->addon, - 'fields' => 'ids', - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - 'update_menu_item_cache' => false, - ]); - - if (count($ids)) { - return $ids[0]; - } - } - - public function save() - { - if (!$this->is_valid) { - return $this->data; - } - - $post_arr = [ - 'post_type' => self::post_type, - 'post_name' => $this->name, - 'post_title' => $this->title, - 'post_excerpt' => $this->description, - 'post_content' => $this->snippet, - 'post_status' => 'publish', - ]; - - $post_id = $this->get_post_id(); - if ($post_id) { - $post_arr['ID'] = $post_id; - $post_id = wp_update_post($post_arr, true); - } else { - $post_id = wp_insert_post($post_arr, true); - } - - if (!is_wp_error($post_id)) { - update_post_meta($post_id, '_fb-addon', $this->addon); - update_post_meta($post_id, '_job-input', $this->input); - update_post_meta($post_id, '_job-output', $this->output); - } - - return $post_id; - } - - public function reset() - { - $post_id = $this->get_post_id(); - - if (!$post_id) { - return false; - } - - return wp_delete_post($post_id, true) instanceof WP_Post; - } - - /** - * Vaildates the data against the job schema. - * - * @param array $data Job data. - * - * @return array|WP_Error Validation result. - */ - private function validate($data) - { - $schema = self::schema(); - - if ( - isset($data['name'], $data['snippet']) && - is_string($data['snippet']) - ) { - $data['method'] = self::load_snippet( - $data['snippet'], - $data['name'], - $this->addon - ); - } else { - if (isset($data['method']) && function_exists($data['method'])) { - $data['snippet'] = self::reflect_method($data['method']); - } else { - $data['method'] = '\FORMS_BRIDGE\forms_bridge_job_noop_method'; - $data['snippet'] = ''; - } - } - - $data = wpct_plugin_sanitize_with_schema($data, $schema); - if (is_wp_error($data)) { - return $data; - } - - if (!function_exists($data['method'])) { - return new WP_Error( - 'method_is_not_function', - __('Job method is not a function', 'forms-bridge'), - $data - ); - } - - return $data; - } - - /** - * Checks if payload compains with the required fields of the job. - * - * @param array $payload Input payload of the job. - * - * @return boolean - */ - private function missing_requireds($payload) - { - $requireds = array_filter($this->input, function ($input_field) { - return $input_field['required'] ?? false; - }); - - foreach ($requireds as $required) { - if (!isset($payload[$required['name']])) { - return true; - } - } - - return false; - } - - /** - * Removes attributes from the payload that are not present on the job output config. - * - * @param array $payload Job result payload. - * - * @return array Filtered payload. - */ - private function output_payload($payload) - { - $input_fields = array_column($this->input, 'name'); - - foreach ($input_fields as $input_field) { - foreach ($this->output as $output_field) { - if ($input_field === $output_field['name']) { - if (is_array($output_field['requires'] ?? null)) { - $requires = array_filter( - $output_field['requires'], - function ($name) use ($input_fields) { - return array_search($name, $input_fields) === - false; - } - ); - - if (count($requires)) { - break; - } - } - - $persist = true; - break; - } - } - - if (!isset($persist)) { - unset($payload[$input_field]); - } - } - - return $payload; - } -} diff --git a/includes/class-json-finger.php b/includes/class-json-finger.php deleted file mode 100644 index 37bec6d1..00000000 --- a/includes/class-json-finger.php +++ /dev/null @@ -1,559 +0,0 @@ - $i) { - if ($pointer[$i + 1] !== '.' && $pointer[$i + 1] !== '[') { - self::$cache[$pointer] = []; - return []; - } - } - } else { - $key .= $char; - } - } - - if ($key) { - $keys[] = $key; - } - - self::$cache[$pointer] = $keys; - return $keys; - } - - /** - * Sanitize a key to be a valid finger key. - * - * @param string|int Finger key value. - * - * @return string Sanitized key value. - */ - public static function sanitize_key($key) - { - if ($key === INF) { - $key = '[]'; - } elseif (intval($key) == $key) { - $key = "[{$key}]"; - } else { - $key = trim($key); - - if ( - preg_match('/( |\.|")/', $key) && - !preg_match('/^\["[^"]+"\]$/', $key) - ) { - $key = "[\"{$key}\"]"; - } - } - - return $key; - } - - /** - * Validates the finger pointer. - * - * @param string $pointer Finger pointer. - * - * @return boolean Validation result. - */ - public static function validate($pointer) - { - $pointer = (string) $pointer; - - if (!strlen($pointer)) { - return false; - } - - return count(self::parse($pointer)) > 0; - } - - /** - * Returns a finger pointer from an array of keys after keys validation and sanitization. - * - * @param array $keys Array with finger keys. - * - * @return string Finger pointer result. - */ - public static function pointer($keys, $is_conditional = false) - { - if (!is_array($keys)) { - return ''; - } - - $pointer = array_reduce( - $keys, - static function ($pointer, $key) { - if ($key === INF) { - $key = '[]'; - } elseif (intval($key) == $key) { - $key = "[{$key}]"; - } else { - $key = self::sanitize_key($key); - - if ($key[0] !== '[' && strlen($pointer) > 0) { - $key = '.' . $key; - } - } - - return $pointer . $key; - }, - '' - ); - - if ($is_conditional) { - $pointer = '?' . $pointer; - } - - return $pointer; - } - - /** - * Binds data to the handler instance. - * - * @param array $data Target data. - */ - public function __construct($data) - { - if (!is_array($data)) { - throw new TypeError('Input data isn\'t an array'); - } - - $this->data = $data; - } - - /** - * Proxy handler attributes to the data. - * - * @param string $name Attribute name. - * - * @return mixed Attribute value or null. - */ - public function __get($name) - { - if (isset($this->data[$name])) { - return $this->data[$name]; - } - } - - /** - * Proxy handler attribute updates to the data. - * - * @param string $name Attribute name. - * @param mixed $value Attribute value. - */ - public function __set($name, $value) - { - $this->data[$name] = $value; - } - - /** - * Returns de current data. - * - * @return array Current data. - */ - public function data() - { - return $this->data; - } - - /** - * Gets the attribute from the data. - * - * @param string $pointer JSON finger pointer. - * @param array $expansion In case pointer needs expansion, this handles an flat array - * with the expansion values. - * - * @return mixed Attribute value. - */ - public function get($pointer, &$expansion = []) - { - $pointer = (string) $pointer; - - if (!$pointer) { - return $this->data; - } - - if (isset($this->data[$pointer])) { - return $this->data[$pointer]; - } - - if (strstr($pointer, '[]') !== false) { - return $this->get_expanded($pointer, $expansion); - } - - $value = null; - try { - $keys = self::parse($pointer); - - $value = $this->data; - foreach ($keys as $key) { - if (!isset($value[$key])) { - return; - } - - $value = $value[$key]; - } - } catch (Error) { - return; - } - - $expansion[] = $value; - return $value; - } - - /** - * Gets values from an expanded finger pointer. - * - * @param string $pointer Finger pointer. - * @param array $expansion Handle for the expansion's flat array of values. - * - * @return array Hierarchical structure of values result of the expansion. - */ - private function get_expanded($pointer, &$expansion = []) - { - $flat = preg_match('/\[\]$/', $pointer); - - $parts = explode('[]', $pointer); - $before = $parts[0]; - - $after = array_slice($parts, 1); - if (count($after) && !$after[count($after) - 1]) { - array_pop($after); - } - $after = implode('[]', $after); - - if (!$before) { - if (!wp_is_numeric_array($this->data)) { - return []; - } - - $items = $this->data; - } else { - $items = $this->get($before); - } - - if (empty($after) || !wp_is_numeric_array($items)) { - return $items; - } - - for ($i = 0; $i < count($items); $i++) { - $pointer = "{$before}[$i]{$after}"; - $items[$i] = $this->get($pointer, $expansion); - } - - if ($flat) { - return $expansion; - } - - return $items; - } - - /** - * Sets the attribute value on the data. - * - * @param string $pointer JSON finger pointer. - * @param mixed $value Attribute value. - * @param boolean $unset If true, unsets the attribute. - * - * @return array Updated data. - */ - public function set($pointer, $value, $unset = false) - { - if ($this->$pointer) { - $this->$pointer = $value; - return $this->data; - } - - if (strstr($pointer, '[]') !== false) { - return $this->set_expanded($pointer, $value, $unset); - } - - $data = $this->data; - $breadcrumb = []; - - try { - $keys = self::parse($pointer); - if (count($keys) === 1) { - if ($unset) { - unset($data[$keys[0]]); - } else { - $data[$keys[0]] = $value; - } - - $this->data = $data; - return $data; - } - - $partial = &$data; - - for ($i = 0; $i < count($keys) - 1; $i++) { - if (!is_array($partial)) { - return $data; - } - - $key = $keys[$i]; - if (intval($key) == $key) { - if (!wp_is_numeric_array($partial)) { - return $data; - } - - $key = intval($key); - } - - if (!isset($partial[$key])) { - $partial[$key] = []; - } - - $breadcrumb[] = ['partial' => &$partial, 'key' => $key]; - $partial = &$partial[$key]; - } - - $key = array_pop($keys); - if ($unset) { - if (wp_is_numeric_array($partial)) { - array_splice($partial, $key, 1); - } elseif (is_array($partial)) { - unset($partial[$key]); - } - - for ($i = count($breadcrumb) - 1; $i >= 0; $i--) { - $step = &$breadcrumb[$i]; - $partial = &$step['partial']; - $key = $step['key']; - - if (!empty($partial[$key])) { - break; - } - - if (wp_is_numeric_array($partial)) { - array_splice($partial, $key, 1); - } else { - unset($partial[$key]); - } - } - } else { - $partial[$key] = $value; - } - } catch (Error $e) { - error_log($e->getMessage()); - return $this->data; - } - - $this->data = $data; - return $data; - } - - /** - * Sets values based on the expansion of the finger pointer. - * - * @param string $pointer Finger pointer. - * @param array $values Array of values. - * @param boolean $unset If true, unsets the attributes. - * - * @return array - */ - private function set_expanded($pointer, $values, $unset) - { - $parts = array_filter(explode('[]', $pointer)); - $before = $parts[0]; - - $after = array_slice($parts, 1); - if (count($after) && !$after[count($after) - 1]) { - array_pop($after); - } - $after = implode('[]', $after); - - if (!$before) { - if (!is_array($values) || (!$after && $unset)) { - return; - } - } - - if ($unset) { - $values = $this->get($before); - } - - if (!wp_is_numeric_array($values)) { - return; - } - - for ($i = count($values) - 1; $i >= 0; $i--) { - $pointer = "{$before}[{$i}]{$after}"; - - if ($unset) { - $this->unset($pointer); - } else { - $this->set($pointer, $values[$i]); - } - } - - $values = $this->get($before); - - if (wp_is_numeric_array($values)) { - ksort($values); - $this->set($before, $values); - } - - return $this->data; - } - - /** - * Unsets the attribute from the data. - * - * @param string $pointer JSON finger pointer. - */ - public function unset($pointer) - { - if (isset($this->data[$pointer])) { - if (intval($pointer) == $pointer) { - if (wp_is_numeric_array($this->data)) { - array_splice($this->data, $pointer, 1); - } - } else { - unset($this->data[$pointer]); - } - - return $this->data; - } - - return $this->set($pointer, null, true); - } - - /** - * Checks if the json finger is set on the data. - * - * @param string $pointer JSON finger pointer. - * @param boolean &$is_conditional Reference to handle if the pointer is - * conditional. - * - * @return boolean True if attribute is set. - */ - public function isset($pointer, &$is_conditional = false) - { - $keys = self::parse($pointer, $is_conditional); - - switch (count($keys)) { - case 0: - return false; - case 1: - $key = $keys[0]; - return isset($this->data[$key]); - default: - $key = array_pop($keys); - $pointer = self::pointer($keys); - $parent = $this->get($pointer); - - if (strstr($pointer, '[]') === false) { - if ($key === INF && is_array($parent)) { - return true; - } - - return isset($parent[$key]); - } - - if (!wp_is_numeric_array($parent)) { - return false; - } - - if ($key === INF) { - return true; - } - - foreach ($parent as $item) { - if (isset($item[$key])) { - return true; - } - } - - return false; - } - } -} diff --git a/includes/class-logger.php b/includes/class-logger.php deleted file mode 100644 index 2e36efdc..00000000 --- a/includes/class-logger.php +++ /dev/null @@ -1,280 +0,0 @@ - 0 && $lines >= 0) { - $seek = min(ftell($socket), $buffer); - - fseek($socket, -$seek, SEEK_CUR); - - $output = ($chunk = fread($socket, $seek)) . $output; - - fseek($socket, -mb_strlen($chunk, '8bit'), SEEK_CUR); - - $lines -= substr_count($chunk, "\n"); - } - - while ($lines++ < 0) { - $output = substr($output, strpos($output, "\n") + 1); - } - - fclose($socket); - - $output = trim($output); - return (array) preg_split('/(\n|\r)+/', $output); - } - - /** - * Write log lines to the log file if debug mode is active. - * - * @param mixed $data Log line data. - * @param string $level Log level, DEBUG as default. - */ - public static function log($data, $level = 'DEBUG') - { - if (!self::is_active()) { - return; - } - - if (!in_array($level, ['DEBUG', 'ERROR', 'INFO'], true)) { - $level = 'DEBUG'; - } - - if (is_object($data)) { - $data = (array) $data; - } - - if (is_array($data)) { - $data = json_encode( - $data, - JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE - ); - } - - $message = print_r($data, true); - $line = sprintf("[%s] %s\n", $level, $message); - - $socket = fopen(self::log_path(), 'a+'); - fwrite($socket, $line, strlen($line)); - fclose($socket); - } - - /** - * Check if the debug mode is active. - * - * @return boolean - */ - public static function is_active() - { - $log_path = self::log_path(); - return is_file($log_path); - } - - /** - * Debug mode activator. - */ - public static function activate() - { - if (!self::is_active()) { - $log_path = self::log_path(); - if (!is_file($log_path)) { - touch($log_path); - } - } - } - - /** - * Debug mode deactivator. - */ - public static function deactivate() - { - if (self::is_active()) { - $log_path = self::log_path(); - if (is_file($log_path)) { - wp_delete_file($log_path); - } - } - } - - /** - * Logger's setup method. Initializes php log configurations. - * - * @return Logger - */ - public static function setup() - { - if (self::is_active()) { - error_reporting(E_ALL); - ini_set('log_errors', 1); - ini_set('display_errors', 0); - ini_set('error_log', self::log_path()); - } - - return self::get_instance(); - } - - /** - * Logger singleton constructor. Binds the logger to wp and custom hooks - */ - protected function construct(...$args) - { - add_action( - 'rest_api_init', - static function () { - self::register_log_route(); - }, - 10, - 0 - ); - - Settings_Store::ready(function ($store) { - $store::use_getter('general', static function ($data) { - $data['debug'] = self::is_active(); - return $data; - }); - - $store::use_setter( - 'general', - static function ($data) { - if (!isset($data['debug'])) { - return $data; - } - - if ($data['debug'] === true) { - self::activate(); - } else { - self::deactivate(); - } - - unset($data['debug']); - return $data; - }, - 9 - ); - }); - } - - /** - * Registers the logger REST API route. - */ - private static function register_log_route() - { - register_rest_route('forms-bridge/v1', '/logs/', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => static function () { - $lines = isset($_GET['lines']) ? (int) $_GET['lines'] : 500; - $logs = self::logs($lines); - - if (empty($logs)) { - return []; - } - - return $logs; - }, - 'permission_callback' => static function () { - return self::permission_callback(); - }, - ]); - } - - /** - * REST API route's permission callback. - * - * @return boolean|WP_Error - */ - private static function permission_callback() - { - return current_user_can('manage_options') ?: - new WP_Error('rest_unauthorized', 'You can\'t manage wp options', [ - 'status' => 403, - ]); - } -} - -Logger::setup(); diff --git a/includes/class-menu.php b/includes/class-menu.php deleted file mode 100644 index e8be214b..00000000 --- a/includes/class-menu.php +++ /dev/null @@ -1,26 +0,0 @@ -%s', - esc_html__('Loading', 'forms-bridge') - ); - } -} diff --git a/includes/class-rest-settings-controller.php b/includes/class-rest-settings-controller.php deleted file mode 100644 index de25270b..00000000 --- a/includes/class-rest-settings-controller.php +++ /dev/null @@ -1,740 +0,0 @@ - WP_REST_Server::READABLE, - 'callback' => static function () { - return self::forms(); - }, - 'permission_callback' => [self::class, 'permission_callback'], - ]); - } - - private static function register_schema_route() - { - foreach (Addon::addons() as $addon) { - if (!$addon->enabled) { - continue; - } - - $addon = $addon::name; - register_rest_route('forms-bridge/v1', "/{$addon}/schemas", [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => static function () use ($addon) { - return self::addon_schemas($addon); - }, - 'permission_callback' => [self::class, 'permission_callback'], - ]); - } - - register_rest_route('forms-bridge/v1', '/http/schemas', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => static function () { - return self::http_schemas(); - }, - 'permission_callback' => [self::class, 'permission_callback'], - ]); - } - - /** - * Registers templates REST API routes. - */ - private static function register_template_routes() - { - foreach (Addon::addons() as $addon) { - if (!$addon->enabled) { - continue; - } - - $addon = $addon::name; - - $schema = Form_Bridge_Template::schema($addon); - $args = []; - - foreach ($schema['properties'] as $name => $prop_schema) { - $args[$name] = $prop_schema; - $args[$name]['required'] = in_array( - $name, - $schema['required'], - true - ); - } - - register_rest_route( - 'forms-bridge/v1', - "/{$addon}/templates/(?P[a-zA-Z0-9-_]+)", - [ - [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => static function ($request) use ($addon) { - return self::get_template($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'name' => $args['name'], - ], - ], - [ - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => static function ($request) use ($addon) { - return self::save_template($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => $args, - ], - [ - 'methods' => WP_REST_Server::DELETABLE, - 'callback' => static function ($request) use ($addon) { - return self::reset_template($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'name' => $args['name'], - ], - ], - ] - ); - - register_rest_route( - 'forms-bridge/v1', - "/{$addon}/templates/(?P[a-zA-Z0-9-_]+)/use", - [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => static function ($request) use ($addon) { - return self::use_template($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'name' => $args['name'], - 'integration' => [ - 'description' => __( - 'Target integration', - 'forms-bridge' - ), - 'type' => 'string', - 'required' => true, - ], - 'fields' => $args['fields'], - ], - ] - ); - - register_rest_route( - 'forms-bridge/v1', - "/{$addon}/templates/(?P[a-zA-Z0-9-_]+)/options", - [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => static function ($request) use ($addon) { - return self::get_template_options($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'name' => $args['name'], - 'backend' => FBAPI::get_backend_schema(), - 'credential' => FBAPI::get_credential_schema(), - ], - ] - ); - } - } - - /** - * Registers jobs REST API routes. - */ - private static function register_job_routes() - { - foreach (Addon::addons() as $addon) { - if (!$addon->enabled) { - continue; - } - - $addon = $addon::name; - - $schema = Job::schema(); - $args = []; - - foreach ($schema['properties'] as $name => $prop_schema) { - $args[$name] = $prop_schema; - $args[$name]['required'] = in_array( - $name, - $schema['required'], - true - ); - } - - register_rest_route('forms-bridge/v1', "/{$addon}/jobs/workflow", [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => static function ($request) use ($addon) { - return self::get_jobs($addon, $request); - }, - 'permission_callback' => [self::class, 'permission_callback'], - 'args' => [ - 'jobs' => [ - 'description' => __( - 'Array of job names', - 'forms-bridge' - ), - 'type' => 'array', - 'items' => ['type' => 'string'], - 'uniqueItems' => true, - 'minItems' => 1, - 'required' => true, - ], - ], - ]); - - register_rest_route( - 'forms-bridge/v1', - "/{$addon}/jobs/(?P[a-zA-Z0-9-_]+)", - [ - [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => static function ($request) use ($addon) { - return self::get_job($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'name' => $args['name'], - ], - ], - [ - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => static function ($request) use ($addon) { - return self::save_job($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => $args, - ], - [ - 'methods' => WP_REST_Server::DELETABLE, - 'callback' => static function ($request) use ($addon) { - return self::reset_job($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'name' => $args['name'], - ], - ], - ] - ); - } - } - - private static function register_backend_routes() - { - foreach (Addon::addons() as $addon) { - if (!$addon->enabled) { - continue; - } - - $addon = $addon::name; - - // $schema = Form_Bridge_Template::schema($addon); - // $args = []; - - // foreach ($schema['properties'] as $name => $prop_schema) { - // $args[$name] = $prop_schema; - // $args[$name]['required'] = in_array( - // $name, - // $schema['required'], - // true - // ); - // } - - register_rest_route('forms-bridge/v1', "/{$addon}/backend/ping", [ - [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => static function ($request) use ($addon) { - return self::ping_backend($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'backend' => FBAPI::get_backend_schema(), - 'credential' => FBAPI::get_credential_schema(), - ], - ], - ]); - - register_rest_route( - 'forms-bridge/v1', - "/{$addon}/backend/endpoint/schema", - [ - [ - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => static function ($request) use ($addon) { - return self::get_endpoint_schema($addon, $request); - }, - 'permission_callback' => [ - self::class, - 'permission_callback', - ], - 'args' => [ - 'backend' => FBAPI::get_backend_schema(), - 'endpoint' => [ - 'description' => __( - 'Target endpoint name', - 'forms-bridge' - ), - 'type' => 'string', - 'required' => true, - ], - ], - ], - ] - ); - } - } - - /** - * Callback for GET requests to the forms endpoint. - * - * @return array - */ - private static function forms() - { - $forms = FBAPI::get_forms(); - return array_map(static function ($form) { - unset($form['bridges']); - return $form; - }, $forms); - } - - /** - * Callback for GET requests to the job endpoint. - * - * @param string $addon Addon name. - * @param WP_REST_Request $request. - * - * @return array|WP_Error - */ - private static function get_job($addon, $request) - { - $job = FBAPI::get_job($request['name'], $addon); - if (empty($job)) { - return self::not_found(); - } - - return $job->data(); - } - - private static function save_job($addon, $request) - { - $data = $request->get_json_params(); - $data['name'] = $request['name']; - - $post_id = FBAPI::save_job($data, $addon); - if (!$post_id) { - return self::bad_request(); - } - - $post = get_post($post_id); - return (new Job($post, $addon))->data(); - } - - private static function reset_job($addon, $request) - { - $job = FBAPI::get_job($request['name'], $addon); - - if (!$job) { - return self::not_found(); - } - - $reset = $job->reset(); - if (!$reset) { - return $job->data(); - } - - $job = FBAPI::get_job($request['name'], $addon); - if ($job) { - return $job->data(); - } - - return []; - } - - /** - * Callback for POST requests to the jobs endpoint. - * - * @param string $addon Addon name. - * @param WP_REST_Request $request. - * - * @return array|WP_Error Jobs data. - */ - private static function get_jobs($addon, $request) - { - $jobs = []; - foreach (FBAPI::get_addon_jobs($addon) as $job) { - if (in_array($job->name, $request['jobs'], true)) { - $jobs[] = $job->data(); - } - } - - if (count($jobs) !== count($request['jobs'])) { - return self::not_found(); - } - - return $jobs; - } - - /** - * Callback for GET requests to the templates endpoint. - * - * @param string $addon Addon name. - * @param WP_REST_Request $request. - * - * @return array|WP_Error Template data. - */ - private static function get_template($addon, $request) - { - $template = FBAPI::get_template($request['name'], $addon); - if (empty($template)) { - return self::not_found(__('Template is unknown', 'forms-bridge'), [ - 'name' => $request['name'], - 'addon' => $addon, - ]); - } - - return $template->data(); - } - - private static function save_template($addon, $request) - { - $data = $request->get_json_params(); - $data['name'] = $request['name']; - - $result = FBAPI::save_template($data, $addon); - if (is_wp_error($result)) { - return $result; - } - - return ['success' => true]; - } - - private static function reset_template($addon, $name) - { - $template = FBAPI::get_template($name, $addon); - - if (!$template) { - return self::not_found(); - } - - $result = $template->reset(); - - if (!$result) { - return self::internal_server_error(); - } - - $template = FBAPI::get_template($name, $addon); - if ($template) { - return $template->data(); - } - } - - /** - * Callback for POST requests to the templates endpoint. - * - * @param string $addon Name of the owner addon of the template. - * @param REST_Request Request instance. - * - * @return array|WP_Error Template use result. - */ - private static function use_template($addon, $request) - { - $name = $request['name']; - $fields = $request['fields']; - $integration = $request['integration']; - - $template = FBAPI::get_template($name, $addon); - if (empty($template)) { - return self::not_found(); - } - - if (!in_array($integration, $template->integrations)) { - return self::bad_request(); - } - - $result = $template->use($fields, $integration); - - if (is_wp_error($result)) { - return $result; - } - - return ['success' => $result === true]; - } - - private static function get_template_options($addon, $request) - { - $handler = self::prepare_addon_backend_request_handler( - $addon, - $request - ); - - if (is_wp_error($handler)) { - return $handler; - } - - [$addon, $backend] = $handler; - - $template = FBAPI::get_template($request['name'], $addon::name); - if (!$template) { - return self::not_found(); - } - - if (!$template->is_valid) { - return self::bad_request(); - } - - $field_options = []; - $fields = $template->fields; - foreach ($fields as $field) { - if ($endpoint = $field['options']['endpoint'] ?? null) { - if (is_string($field['options']['finger'])) { - $finger = [ - 'value' => $field['options']['finger'], - ]; - } else { - $finger = $field['options']['finger']; - } - - $value_pointer = $finger['value']; - - if (!JSON_Finger::validate($value_pointer)) { - return self::internal_server_error(); - } - - $label_pointer = $finger['label'] ?? $finger['value']; - - if (!JSON_Finger::validate($label_pointer)) { - return self::internal_server_error(); - } - - $response = $addon->fetch($endpoint, $backend); - - if (is_wp_error($response)) { - $error = self::internal_server_error(); - $error->add( - $response->get_error_code(), - $response->get_error_message(), - $response->get_error_data() - ); - - return $error; - } - - $options = []; - $data = $response['data']; - - $json_finger = new JSON_Finger($data); - - $values = $json_finger->get($value_pointer); - - if (!wp_is_numeric_array($values)) { - return self::internal_server_error(); - } - - foreach ($values as $value) { - $options[] = ['value' => $value, 'label' => $value]; - } - - $labels = $json_finger->get($label_pointer); - if ( - wp_is_numeric_array($labels) && - count($labels) === count($values) - ) { - for ($i = 0; $i < count($labels); $i++) { - $options[$i]['label'] = $labels[$i]; - } - } - - $field_options[] = [ - 'ref' => $field['ref'], - 'name' => $field['name'], - 'options' => $options, - ]; - } - } - - return $field_options; - } - - /** - * Performs a request validation and sanitization - * - * @param string $addon Target addon name. - * @param WP_REST_Request $request Request instance. - * - * @return [Addon, string, string|null]|WP_Error - */ - private static function prepare_addon_backend_request_handler( - $addon, - $request - ) { - $backend = wpct_plugin_sanitize_with_schema( - $request['backend'], - FBAPI::get_backend_schema() - ); - - if (is_wp_error($backend)) { - return self::bad_request(); - } - - $credential = $request['credential']; - if (!empty($credential)) { - $credential = wpct_plugin_sanitize_with_schema( - $credential, - FBAPI::get_credential_schema($addon) - ); - - if (is_wp_error($credential)) { - return self::bad_request(); - } - - $backend['credential'] = $credential['name']; - } - - $addon = FBAPI::get_addon($addon); - if (!$addon) { - return self::bad_request(); - } - - Backend::temp_registration($backend); - Credential::temp_registration($credential); - - return [$addon, $backend['name'], $credential['name'] ?? null]; - } - - private static function ping_backend($addon, $request) - { - $handler = self::prepare_addon_backend_request_handler( - $addon, - $request - ); - - if (is_wp_error($handler)) { - return $handler; - } - - [$addon, $backend] = $handler; - - $result = $addon->ping($backend); - - if (is_wp_error($result)) { - $error = self::bad_request(); - $error->add( - $result->get_error_code(), - $result->get_error_message(), - $result->get_error_data() - ); - - return $error; - } - - return ['success' => $result]; - } - - private static function get_endpoint_schema($addon, $request) - { - $handler = self::prepare_addon_backend_request_handler( - $addon, - $request - ); - if (is_wp_error($handler)) { - return $handler; - } - - [$addon, $backend] = $handler; - - $schema = $addon->get_endpoint_schema($request['endpoint'], $backend); - - if (is_wp_error($schema)) { - $error = self::internal_server_error(); - $error->add( - $schema->get_error_code(), - $schema->get_error_message(), - $schema->get_error_data() - ); - - return $error; - } - - return $schema; - } - - private static function addon_schemas($name) - { - $bridge = FBAPI::get_bridge_schema($name); - return ['bridge' => $bridge]; - } - - private static function http_schemas() - { - $backend = FBAPI::get_backend_schema(); - $credential = FBAPI::get_credential_schema(); - return ['backend' => $backend, 'credential' => $credential]; - } -} diff --git a/includes/class-settings-store.php b/includes/class-settings-store.php deleted file mode 100644 index 917e6f55..00000000 --- a/includes/class-settings-store.php +++ /dev/null @@ -1,82 +0,0 @@ - 'general', - 'properties' => [ - 'notification_receiver' => [ - 'type' => 'string', - 'format' => 'email', - 'default' => $admin_email, - ], - ], - 'required' => ['notification_receiver'], - 'default' => [ - 'notification_receiver' => $admin_email, - ], - ]); - - self::register_setting([ - 'name' => 'http', - 'properties' => [], - 'default' => [], - ]); - - self::ready(static function ($store) { - $store::use_getter('http', static function () { - $setting = Http_Store::setting('general'); - return $setting->data(); - }); - - $store::use_setter( - 'http', - static function ($data) { - if ( - isset($data['backends']) && - isset($data['credentials']) - ) { - $setting = Http_Store::setting('general'); - $setting->update($data); - } - - return []; - }, - 9 - ); - - $store::use_cleaner('general', static function () { - $setting = Http_Store::setting('general'); - $setting->update(['backends' => [], 'credentials' => []]); - }); - }); - } -} diff --git a/includes/jobs/date-fields-to-date.php b/includes/jobs/date-fields-to-date.php deleted file mode 100644 index 00e2341b..00000000 --- a/includes/jobs/date-fields-to-date.php +++ /dev/null @@ -1,92 +0,0 @@ - 'date-fields-to-date', - 'title' => __('Format date fields', 'forms-bridge'), - 'description' => __( - 'Gets date, hour and minute fields and merge its values into a date with format Y-m-d H:M:S', - 'forms-bridge' - ), - 'method' => 'forms_bridge_job_format_date_fields', - 'input' => [ - [ - 'name' => 'date', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'hour', - 'schema' => ['type' => 'string'], - ], - [ - 'name' => 'minute', - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'datetime', - 'schema' => ['type' => 'string'], - ], - ], -]; - -function forms_bridge_job_format_date_fields($payload) -{ - $date = $payload['date']; - $hour = $payload['hour'] ?? '00'; - $minute = $payload['minute'] ?? '00'; - - $form_data = FBAPI::get_current_form(); - $date_index = array_search( - 'date', - array_column($form_data['fields'], 'type') - ); - - $date_format = $form_data['fields'][$date_index]['format'] ?? ''; - - if (strstr($date_format, '-')) { - $separator = '-'; - } elseif (strstr($date_format, '.')) { - $separator = '.'; - } elseif (strstr($date_format, '/')) { - $separator = '/'; - } - - switch (substr($date_format, 0, 1)) { - case 'y': - [$year, $month, $day] = explode($separator, $date); - break; - case 'm': - [$month, $day, $year] = explode($separator, $date); - break; - case 'd': - [$day, $month, $year] = explode($separator, $date); - break; - } - - $date = "{$year}-{$month}-{$day}"; - - if (preg_match('/(am|pm)/i', $hour, $matches)) { - $hour = (int) $hour; - if (strtolower($matches[0]) === 'pm') { - $hour += 12; - } - } - - $time = strtotime("{$date} {$hour}:{$minute}"); - - if ($time === false) { - return new WP_Error( - 'invalid-date', - __('Invalid date format', 'forms-bridge') - ); - } - - $payload['datetime'] = date('Y-m-d H:i:s', $time); - return $payload; -} diff --git a/includes/jobs/iso2-country-code.php b/includes/jobs/iso2-country-code.php deleted file mode 100644 index 1cc7e7ef..00000000 --- a/includes/jobs/iso2-country-code.php +++ /dev/null @@ -1,51 +0,0 @@ - 'iso2-country-code', - 'title' => __('ISO2 country code', 'forms-bridge'), - 'description' => __( - 'Gets the ISO2 country code from country names and replace its value', - 'forms-bridge' - ), - 'method' => 'forms_bridge_job_iso2_country_code', - 'input' => [ - [ - 'name' => 'country', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - ], - ], -]; - -function forms_bridge_job_iso2_country_code($payload) -{ - global $forms_bridge_iso2_countries; - $country_code = strtoupper($payload['country']); - - if (!isset($forms_bridge_iso2_countries[$country_code])) { - $countries = []; - foreach ($forms_bridge_iso2_countries as $country_code => $country) { - $countries[$country] = $country_code; - } - - if (isset($countries[$payload['country']])) { - $payload['country'] = $countries[$payload['country']]; - } else { - $payload['country'] = null; - } - } else { - $payload['country'] = $country_code; - } - - return $payload; -} diff --git a/includes/jobs/iso3-country-code.php b/includes/jobs/iso3-country-code.php deleted file mode 100644 index 6b706cf4..00000000 --- a/includes/jobs/iso3-country-code.php +++ /dev/null @@ -1,51 +0,0 @@ - 'iso3-country-code', - 'title' => __('ISO3 country code', 'forms-bridge'), - 'description' => __( - 'Gets the ISO3 country code from country names and replace its value', - 'forms-bridge' - ), - 'method' => 'forms_bridge_job_iso3_country_code', - 'input' => [ - [ - 'name' => 'country', - 'required' => true, - 'schema' => ['type' => 'string'], - ], - ], - 'output' => [ - [ - 'name' => 'country', - 'schema' => ['type' => 'string'], - ], - ], -]; - -function forms_bridge_job_iso3_country_code($payload) -{ - global $forms_bridge_iso3_countries; - $country_code = strtoupper($payload['country']); - - if (!isset($forms_bridge_iso3_countries[$country_code])) { - $countries = []; - foreach ($forms_bridge_iso3_countries as $country_code => $country) { - $countries[$country] = $country_code; - } - - if (isset($countries[$payload['country']])) { - $payload['country'] = $countries[$payload['country']]; - } else { - $payload['country'] = null; - } - } else { - $payload['country'] = $country_code; - } - - return $payload; -} diff --git a/includes/jobs/skip-submission.php b/includes/jobs/skip-submission.php deleted file mode 100644 index 37ebfbe8..00000000 --- a/includes/jobs/skip-submission.php +++ /dev/null @@ -1,31 +0,0 @@ - __('Skip submission', 'forms-bridge'), - 'description' => __( - 'Skip submission if condition is not truthy', - 'forms-bridge' - ), - 'method' => 'forms_bridge_job_skip_if_not_condition', - 'input' => [ - [ - 'name' => 'condition', - 'schema' => ['type' => 'boolean'], - 'required' => true, - ], - ], - 'output' => [], -]; - -function forms_bridge_job_skip_if_not_condition($payload) -{ - if (empty($payload['condition'])) { - return; - } - - return $payload; -} diff --git a/index.php b/index.php deleted file mode 100644 index 4a77e236..00000000 --- a/index.php +++ /dev/null @@ -1,2 +0,0 @@ -serialize_form($form); - } - - /** - * Retrives a form's data by ID. - * - * @param int $form_id Form ID. - * - * @return array. - */ - public function get_form_by_id($form_id) - { - $form = GFAPI::get_form($form_id); - if (!$form) { - return null; - } - - return $this->serialize_form($form); - } - - /** - * Retrives available forms' data. - * - * @return array Collection of form data array representations. - */ - public function forms() - { - $forms = GFAPI::get_forms(); - return array_map( - function ($form) { - return $this->serialize_form($form); - }, - array_filter($forms, function ($form) { - return $form['is_active'] && !$form['is_trash']; - }) - ); - } - - /** - * Creates a form from a given template fields. - * - * @param array $data Form template data. - * - * @return int|null ID of the new form. - */ - public function create_form($data) - { - if (empty($data['title']) || empty($data['fields'])) { - return; - } - - $data = array_merge($data, [ - 'id' => 1, - 'fields' => $this->prepare_fields($data['fields']), - 'labelPlacement' => 'top_label', - 'useCurrentUserAsAuthor' => '1', - 'postAuthor' => '1', - 'postCategory' => '1', - 'postStatus' => 'publish', - 'button' => [ - 'type' => 'text', - 'text' => esc_html__('Submit', 'forms-bridge'), - 'imageUrl' => '', - 'conditionalLogic' => null, - ], - 'version' => '2.7', - ]); - - $form_id = GFAPI::add_form($data); - - if (is_wp_error($form_id)) { - return; - } - - return $form_id; - } - - /** - * Removes a form by ID. - * - * @param integer $form_id Form ID. - * - * @return boolean Removal result. - */ - public function remove_form($form_id) - { - GFFormsModel::delete_form($form_id); - } - - public function submission_id() - { - $submission = $this->submission(true); - if ($submission) { - return (string) $submission['id']; - } - } - - /** - * Retrives the current submission data. - * - * @param boolean $raw Control if the submission is serialized before exit. - * - * @return array - */ - public function submission($raw = false) - { - $form_data = $this->form(); - if (!$form_data) { - return null; - } - - $submission = GFFormsModel::get_current_lead( - GFAPI::get_form($form_data['id']) - ); - - if (!$submission) { - return; - } elseif ($raw) { - return $submission; - } - - $form = $this->form(); - return $this->serialize_submission($submission, $form); - } - - /** - * Retrives the current submission uploaded files. - * - * @return array Collection of uploaded files. - */ - public function uploads() - { - $form_data = $this->form(); - if (!$form_data) { - return null; - } - - $submission = GFFormsModel::get_current_lead( - GFAPI::get_form($form_data['id']) - ); - - if (!$submission) { - return null; - } - - $form = $this->form(); - return $this->submission_uploads($submission, $form); - } - - /** - * Serializes gf form's data. - * - * @param array $form GF form data. - * - * @return array - */ - public function serialize_form($form) - { - $form_id = (int) $form['id']; - $fields = array_reduce( - $form['fields'], - function ($fields, $field) { - $field = $this->serialize_field($field); - if ($field) { - $field = wp_is_numeric_array($field) ? $field : [$field]; - $fields = array_merge($fields, $field); - } - - return $fields; - }, - [] - ); - - return apply_filters( - 'forms_bridge_form_data', - [ - '_id' => 'gf:' . $form_id, - 'id' => $form_id, - 'title' => $form['title'], - 'bridges' => FBAPI::get_form_bridges($form_id, 'gf'), - 'fields' => $fields, - ], - $form, - 'gf' - ); - } - - /** - * Serializes a GFField as array data. - * - * @param GFField $field Field object instance. - * - * @return array - */ - private function serialize_field($field) - { - if ( - in_array($field->type, [ - 'page', - 'section', - 'html', - 'submit', - 'captcha', - ]) - ) { - return; - } - - if (strstr($field->type, 'post_')) { - return; - } - - $label = $field->adminLabel ?: $field->label; - $name = $field->inputName ?: $label; - - $allowsPrepopulate = $field->allowsPrepopulate ?? false; - - $options = array_map( - function ($opt) { - return [ - 'value' => $opt['value'], - 'label' => $opt['text'], - ]; - }, - $field->choices ?: [] - ); - - try { - $inputs = array_map(static function ($input) use ( - $allowsPrepopulate - ) { - $input['name'] = $allowsPrepopulate ? $input['name'] : ''; - return $input; - }, $field->get_entry_inputs()); - } catch (Error) { - $inputs = []; - } - - $inputs = array_values( - array_filter($inputs, static function ($input) { - return !isset($input['isHidden']) || !$input['isHidden']; - }) - ); - - $named_inputs = array_filter($inputs, function ($input) { - return !empty($input['name']); - }); - - $subfields = []; - if (count($named_inputs)) { - for ($i = 1; $i <= count($inputs); $i++) { - $input = $inputs[$i - 1]; - - $input_label = implode(' ', [ - $label, - $input['label'] ? "({$input['label']})" : "($i)", - ]); - - $input_name = $input['name'] ?: $input_label; - - $subfields[] = $this->serialize_field( - (object) array_merge((array) $field, $input, [ - 'id' => $input['id'], - 'inputName' => $input_name, - 'label' => $input_label, - 'adminLabel' => $input_label, - 'type' => 'text', - 'schema' => ['type' => 'string'], - ]) - ); - } - } - - switch ($field->type) { - case 'list': - case 'checkbox': - case 'multiselect': - case 'multi_choice': - case 'image_choice': - case 'option': - case 'select': - case 'radio': - $type = 'select'; - break; - case 'number': - case 'total': - case 'quantity': - $type = 'number'; - break; - case 'consent': - $type = 'boolean'; - break; - case 'fileupload': - $type = 'file'; - break; - case 'email': - $type = 'email'; - break; - case 'website': - $type = 'url'; - break; - case 'date': - $type = 'date'; - break; - case 'textarea': - $type = $field->type; - break; - case 'address': - case 'product': - case 'name': - case 'shipping': - default: - $type = 'text'; - break; - } - - $field = apply_filters( - 'forms_bridge_form_field_data', - [ - 'id' => $field->id, - 'type' => $type, - 'name' => $name, - 'label' => $label, - 'required' => $field->isRequired, - 'options' => $options, - 'inputs' => $inputs, - 'is_file' => $field->type === 'fileupload', - 'is_multi' => $this->is_multi_field($field), - 'conditional' => $field->conditionalLogic['enabled'] ?? false, - 'format' => $field->type === 'date' ? 'yyyy-mm-dd' : '', - 'schema' => $this->field_value_schema($field), - '_type' => $field->type, - ], - $field, - 'gf' - ); - - if ( - !empty($subfields) && - ($allowsPrepopulate || $field['type'] === 'list') - ) { - return array_map(function ($subfield) use ($field) { - return array_merge($subfield, ['parent' => $field]); - }, $subfields); - } - - return $field; - } - - /** - * Checks if a filed is multi value field. - * - * @param GF_Field Target field instance. - * - * @return boolean - */ - private function is_multi_field($field) - { - if ($field->type === 'fileupload') { - return $field->multipleFiles ?? false; - } - - if (isset($field->storageType) && $field->storageType === 'json') { - return true; - } - - // if (isset($field->choiceLimit)) { - // if ($field->choiceLimit === 'unlimited') { - // return true; - // } elseif ($field->choiceLimit === 'exactly' && $field->choiceLimitNumber > 1) { - // return true; - // } - // } - - if (in_array($field->inputType, ['list', 'checkbox'])) { - return true; - } - - return false; - } - - /** - * Gets the field value JSON schema. - * - * @param GF_Field $field Field instance. - * - * @return array JSON schema of the value of the field. - */ - private function field_value_schema($field) - { - switch ($field->type) { - case 'list': - if (!empty($field->choices)) { - return [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => array_reduce( - $field->choices, - static function ($choices, $choice) { - $choices[$choice['value']] = [ - 'type' => 'string', - ]; - return $choices; - }, - [] - ), - ], - 'additionalItems' => true, - ]; - } - - return [ - 'type' => 'array', - 'items' => ['type' => 'string'], - 'additionalItems' => true, - ]; - case 'checkbox': - case 'multiselect': - $items = []; - for ($i = 0; $i < count($field->choices); $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - ]; - case 'multi_choice': - case 'image_choice': - case 'option': - if ($this->is_multi_field($field)) { - if ($field->choiceLimit === 'range') { - $maxItems = $field->choiceLimitMax; - } elseif ($field->choiceLimit === 'exactly') { - $maxItems = $field->choiceLimitNumber; - } else { - $maxItems = count($field->choices); - } - - $items = []; - for ($i = 0; $i < $maxItems; $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - ]; - } - - return ['type' => 'string']; - case 'select': - case 'radio': - case 'address': - case 'website': - case 'product': - case 'email': - case 'textarea': - case 'name': - case 'shipping': - return ['type' => 'string']; - case 'number': - case 'total': - case 'quantity': - return ['type' => 'number']; - case 'fileupload': - return; - case 'consent': - return ['type' => 'boolean']; - default: - return ['type' => 'string']; - } - } - - /** - * Serializes the current form's submission data. - * - * @param array $submission GF form submission. - * @param array $form Form data. - * - * @return array - */ - public function serialize_submission($submission, $form_data) - { - $data = []; - - $has_total = array_search( - 'total', - array_map(static function ($field) { - return $field['_type']; - }, $form_data['fields']) - ); - - $has_quantity = array_search( - 'quantity', - array_map(static function ($field) { - return $field['_type']; - }, $form_data['fields']) - ); - - foreach ($form_data['fields'] as $field) { - if ($field['is_file']) { - continue; - } - - $input_name = $field['name']; - $inputs = $field['inputs']; - - if (!empty($inputs)) { - $values = []; - foreach ($inputs as $input) { - if (!$this->isset($input['id'])) { - continue; - } - - $value = rgar($submission, (string) $input['id']); - if ($input_name && $value) { - $value = $this->format_value( - $value, - $field['_type'], - $input - ); - - if ($value !== null) { - $values[] = $value; - } - } - } - - if ($field['_type'] === 'consent') { - $data[$input_name] = boolval($values[0] ?? false); - } elseif ($field['_type'] === 'name') { - $data[$input_name] = implode(' ', $values); - } elseif ($field['_type'] === 'product') { - if ($has_total) { - $data[$input_name] = $values[0]; - } else { - if ($has_quantity) { - $values = array_slice($values, 0, 2); - } - - $data[$input_name] = implode('|', $values); - } - } elseif ($field['_type'] === 'address') { - $data[$input_name] = implode(', ', $values); - } else { - $data[$input_name] = $values; - } - } else { - // simple fields - $isset = $this->isset($field['id']); - if (!$isset) { - continue; - } - - if ($input_name) { - $raw_value = rgar($submission, (string) $field['id']); - $data[$input_name] = $this->format_value( - $raw_value, - $field['_type'] - ); - } - } - } - - return $data; - } - - /** - * Formats field values with noop fallback. - * - * @param mixed $value Field's value. - * @param string $field_type GF field type. - * @param array $input Field's input data. - * - * @return mixed Formatted value. - */ - private function format_value($value, $field_type, $input = null) - { - try { - switch ($field_type) { - case 'consent': - if (preg_match('/\.1$/', $input['id'])) { - return $value === '1'; - } - - return null; - case 'hidden': - $number_val = (float) $value; - if (strval($number_val) === $value) { - return $number_val; - } - break; - case 'quantity': - case 'number': - return (float) preg_replace('/[^0-9\.,]/', '', $value); - case 'list': - return maybe_unserialize($value); - case 'multiselect': - return json_decode($value); - case 'product': - case 'option': - case 'shipping': - return $value; - // return explode('|', $value)[0]; - } - } catch (TypeError) { - // do nothing - } - - return $value; - } - - /** - * Gets the current submission's uploaded files. - * - * @param array $submission GF submission data. - * @param array $form_data Form data. - * - * @return array Uploaded files data. - */ - protected function submission_uploads($submission, $form_data) - { - return array_reduce( - array_filter($form_data['fields'], function ($field) { - return $field['is_file']; - }), - function ($carry, $field) use ($submission, $form_data) { - $upload_path = GFFormsModel::get_upload_path($form_data['id']); - $upload_url = GFFormsModel::get_upload_url($form_data['id']); - - $urls = $submission[$field['id']] ?? []; - $urls = $field['is_multi'] ? json_decode($urls, true) : [$urls]; - - if (!is_array($urls)) { - return $carry; - } - - $paths = []; - foreach ($urls as $url) { - $path = str_replace($upload_url, $upload_path, $url); - if (is_file($path)) { - $paths[] = $path; - } - } - - if (!empty($paths)) { - $carry[$field['name']] = [ - 'path' => $field['is_multi'] ? $paths : $paths[0], - 'is_multi' => $field['is_multi'], - ]; - } - - return $carry; - }, - [] - ); - } - - /** - * Helper function to check if field is set on $_POST super global. - * - * @param string $field_id ID of the field. - * - * @return boolean - */ - private function isset($field_id) - { - $key = 'input_' . implode('_', explode('.', $field_id)); - return isset($_POST[$key]); - } - - /** - * Decorate bridge's tempalte form fields data to be created as gf fields. - * - * @param array $fields Array with bridge's template form fields data. - * - * @return array Decorated array of fields. - */ - private function prepare_fields($fields) - { - $gf_fields = []; - for ($i = 0; $i < count($fields); $i++) { - $id = $i + 1; - $field = $fields[$i]; - $args = [ - $id, - $field['name'], - $field['label'] ?? '', - $field['required'] ?? false, - ]; - - switch ($field['type']) { - case 'hidden': - if (isset($field['value'])) { - if (is_bool($field['value'])) { - $field['value'] = $field['value'] ? '1' : '0'; - } - - $args[] = (string) $field['value']; - $gf_fields[] = $this->hidden_field(...$args); - } - - break; - case 'number': - $constraints = [ - 'rangeMin' => $field['min'] ?? '', - 'rangeMax' => $filed['max'] ?? '', - 'rangeStep' => $field['step'] ?? '1', - 'defaultValue' => floatval($field['default'] ?? 0), - ]; - - $args[] = $constraints; - $gf_fields[] = $this->number_field(...$args); - break; - case 'email': - $gf_fields[] = $this->email_field(...$args); - break; - case 'select': - $args[] = $field['options'] ?? []; - $args[] = $field['is_multi'] ?? false; - $gf_fields[] = $this->select_field(...$args); - break; - case 'textarea': - $gf_fields[] = $this->textarea_field(...$args); - break; - case 'url': - $gf_fields[] = $this->url_field(...$args); - break; - case 'date': - $gf_fields[] = $this->date_field(...$args); - break; - case 'file': - $args[] = $field['is_multi'] ?? false; - $args[] = $field['filetypes'] ?? ''; - $gf_fields[] = $this->file_field(...$args); - break; - // case 'text': - default: - $gf_fields[] = $this->text_field(...$args); - } - } - - return $gf_fields; - } - - /** - * Returns a default field array data. Used as template for the field creation methods. - * - * @param string $type Field type. - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function field_template($type, $id, $name, $label, $required) - { - return [ - 'type' => $type, - 'id' => (int) $id, - 'isRequired' => (bool) $required, - 'size' => 'large', - 'errorMessage' => __('Please supply a valid value', 'forms-bridge'), - 'label' => $label, - 'formId' => 84, - 'inputType' => '', - 'displayOnly' => '', - 'inputs' => null, - 'choices' => '', - 'conditionalLogic' => '', - 'labelPlacement' => '', - 'descriptionPlacement' => '', - 'subLabelPlacement' => '', - 'placeholder' => '', - 'multipleFiles' => false, - 'maxFiles' => '', - 'calculationFormula' => '', - 'calculationRounding' => '', - 'enableCalculation' => '', - 'disableQuantity' => false, - 'displayAllCategories' => false, - 'inputMask' => false, - 'inputMaskValue' => '', - 'allowsPrepopulate' => true, - 'useRichTextEditor' => false, - 'visibility' => 'visible', - 'fields' => '', - 'inputMaskIsCustom' => false, - 'layoutGroupId' => '17f293c9', - 'autocompleteAttribute' => '', - 'emailConfirmEnabled' => false, - 'adminLabel' => '', - 'description' => '', - 'maxLength' => '', - 'cssClass' => '', - 'inputName' => $name, - 'noDuplicates' => false, - 'defaultValue' => '', - 'enableAutocomplete' => false, - ]; - } - - /** - * Returns a valid email field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function email_field($id, $name, $label, $required) - { - return array_merge( - $this->field_template('email', $id, $name, $label, $required), - [ - 'errorMessage' => __( - 'please supply a valid email address', - 'forms-bridge' - ), - 'enableAutocomplete' => true, - ] - ); - } - - /** - * Returns a valid textarea field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function textarea_field($id, $name, $label, $required) - { - return $this->field_template('textarea', $id, $name, $label, $required); - } - - /** - * Returns a valid multi select field data, as a select field if is single, as - * a checkbox field if is multiple. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * @param array $options Options data. - * @param boolean $is_multi Is field multi value - * - * @return array - */ - private function select_field( - $id, - $name, - $label, - $required, - $options, - $is_multi - ) { - $choices = array_map(function ($opt) { - return [ - 'text' => esc_html($opt['label']), - 'value' => $opt['value'], - 'isSelected' => false, - 'price' => '', - ]; - }, $options); - - if ($is_multi) { - $inputs = []; - for ($i = 0; $i < count($choices); $i++) { - $input_id = $i + 1; - $inputs[] = [ - 'id' => $id . '.' . $input_id, - 'label' => $choices[$i]['label'], - 'name' => '', - ]; - } - - return array_merge( - $this->field_template( - 'checkbox', - $id, - $name, - $label, - $required - ), - [ - 'choices' => $choices, - 'inputs' => $inputs, - 'enableChoiceValue' => true, - ] - ); - } else { - return array_merge( - $this->field_template('select', $id, $name, $label, $required), - [ - 'choices' => $choices, - 'enableChoiceValue' => true, - ] - ); - } - } - - /** - * Returns a valid file-upload field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * @param boolean $is_mulit Is field multi value? - * @param string $filetypes String with allowed file extensions separated by commas. - * - * @return array - */ - private function file_field( - $id, - $name, - $label, - $required, - $is_multi, - $filetypes - ) { - return array_merge( - $this->field_template('fileupload', $id, $name, $label, $required), - [ - 'allowedExtensions' => (string) $filetypes, - 'multipleFiles' => (bool) $is_multi, - ] - ); - } - - /** - * Returns a valid hidden field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label (unused). - * @param boolean $required Is field required (unused). - * @param string $value Field's default value. - * - * @return array - */ - private function hidden_field($id, $name, $label, $required, $value) - { - return array_merge( - $this->field_template('hidden', $id, $name, $name, true), - [ - 'inputType' => 'hidden', - 'defaultValue' => $value, - ] - ); - } - - /** - * Returns a valid hidden field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function url_field($id, $name, $label, $required) - { - return $this->field_template('website', $id, $name, $label, $required); - } - - /** - * Returns a valid hidden field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function text_field($id, $name, $label, $required) - { - return array_merge( - $this->field_template('text', $id, $name, $label, $required), - [ - 'inputType' => 'text', - ] - ); - } - - /** - * Returns a valid date field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function date_field($id, $name, $label, $required) - { - return array_merge( - $this->field_template('date', $id, $name, $label, $required), - [ - 'dateType' => 'datepicker', - 'calendarIconType' => 'none', - 'dateFormatPlacement' => 'below', - 'dateFormat' => 'mdy', - ] - ); - } - - /** - * Returns a valid hidden field data. - * - * @param int $id Field id. - * @param string $name Input name. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function number_field($id, $name, $label, $required, $constraints) - { - return array_merge( - $this->field_template('number', $id, $name, $label, $required), - array_merge($constraints, [ - 'inputType' => 'number', - 'numberFormat' => 'decimal_dot', - ]) - ); - } -} - -add_filter( - 'gform_field_content', - function ($field_content, $field, $value, $entry_id, $form_id) { - if ($field->type !== 'number') { - return $field_content; - } - - if (empty($field->rangeStep)) { - return $field_content; - } - - $step = (int) $field->rangeStep; - return str_replace("step='any'", "step='{$step}'", $field_content); - }, - 10, - 5 -); - -Integration::setup(); diff --git a/integrations/ninja/class-ninja-integration.php b/integrations/ninja/class-ninja-integration.php deleted file mode 100644 index 3ef3d560..00000000 --- a/integrations/ninja/class-ninja-integration.php +++ /dev/null @@ -1,1115 +0,0 @@ - null]; - $form_id = (int) $form_data['id']; - if (empty($form_id)) { - return null; - } - - return $this->get_form_by_id($form_id); - } - - /** - * Retrives a form model's data by ID. - * - * @return array. - */ - public function get_form_by_id($form_id) - { - return $this->serialize_form(Ninja_Forms()->form($form_id)); - } - - /** - * Retrives available form models' data. - * - * @return array Collection of forms data. - */ - public function forms() - { - $forms = Ninja_Forms()->form()->get_forms(); - - return array_map(function ($form) { - $form = Ninja_Forms()->form($form->get_id()); - return $this->serialize_form($form); - }, $forms); - } - - /** - * Creates a form from the given template fields. - * - * @param array $data Form template data. - * - * @return int|null ID of the new form. - * - * @todo Implement this routine. - */ - public function create_form($data) - { - $title = sanitize_text_field($data['title']); - $form_data = $this->form_template($title); - $form_data['fields'] = $this->decorate_form_fields($data['fields']); - $form_data['settings']['formContentData'] = array_map(function ( - $field - ) { - return $field['settings']['key']; - }, $form_data['fields']); - - $form = Ninja_Forms()->form()->get(); - $form->save(); - - $form_data['id'] = $form->get_id(); - - $form->update_settings($form_data['settings'])->save(); - - $db_fields_controller = new NF_Database_FieldsController( - $form_data['id'], - $form_data['fields'] - ); - $db_fields_controller->run(); - $form_data['fields'] = $db_fields_controller->get_updated_fields_data(); - - foreach ($form_data['actions'] as &$action_data) { - $action_data['parent_id'] = $form_data['id']; - $action = Ninja_Forms()->form()->action()->get(); - $action->save(); - $action_data['id'] = $action->get_id(); - $action->update_settings($action_data)->save(); - } - - WPN_Helper::update_nf_cache($form_data['id'], $form_data); - - return $form_data['id']; - } - - /** - * Removes a form by ID. - * - * @param integer $form_id Form ID. - * - * @return boolean Removal result. - */ - public function remove_form($form_id) - { - $form = Ninja_Forms()->form($form_id)->get(); - if ($form) { - return $form->delete(); - } - - return false; - } - - public function submission_id() - { - $submission = $this->submission(true); - if ($submission) { - return (string) $submission['actions']['save']['sub_id']; - } - } - - /** - * Retrives the current form submission data. - * - * @param boolean $raw Control if the submission is serialized before exit. - * - * @return array - */ - public function submission($raw = false) - { - if (empty(self::$submission)) { - return null; - } elseif ($raw) { - return self::$submission; - } - - $form = $this->form(); - return $this->serialize_submission(self::$submission, $form); - } - - /** - * Retrives the current submission uploaded files. - * - * @return array Collection of uploaded files. - * - * @todo Adapt to premium version with available upload field. - */ - public function uploads() - { - $form_data = $this->form(); - if (!$form_data) { - return null; - } - - $submission = self::$submission; - if (empty($submission)) { - return null; - } - - return $this->submission_uploads($submission, $form_data); - } - - /** - * Serializes a ninja form model instance as array data. - * - * @param NF_Abstracts_ModelFactory $form Form factory instance. - * - * @return array - */ - public function serialize_form($form_factory) - { - $form = $form_factory->get(); - $form_id = (int) $form->get_id(); - $fields = array_filter( - array_map(function ($field) use ($form) { - return $this->serialize_field($field, $form->get_settings()); - }, $form_factory->get_fields()) - ); - - return apply_filters( - 'forms_bridge_form_data', - [ - '_id' => 'ninja:' . $form_id, - 'id' => $form_id, - 'title' => $form->get_setting('title'), - 'bridges' => FBAPI::get_form_bridges($form_id, 'ninja'), - 'fields' => array_values($fields), - ], - $form, - 'ninja' - ); - } - - /** - * Serializes a form field model instance as array data. - * - * @param NF_Database_Models_Field $field Form field model instance. - * @param array $form_settings Form settings data. - * - * @return array - */ - private function serialize_field($field, $form_settings) - { - if ( - in_array($field->get_setting('type'), [ - 'html', - 'hr', - 'confirm', - 'recaptcha', - 'spam', - 'submit', - ]) - ) { - return; - } - - return apply_filters( - 'forms_bridge_form_field_data', - $this->_serialize_field( - $field->get_id(), - $field->get_settings(), - $form_settings - ), - $field, - 'ninja' - ); - } - - /** - * Serializes field settings as field data array. - * - * @param int $id Field id. - * @param array $settings Field settings data. - * @param array $form_settings Form settings data. - * - * @return array. - */ - private function _serialize_field($id, $settings, $form_settings) - { - $name = - $settings['key'] ?? - ($settings['admin_label'] ?? $settings['label']); - - $children = isset($settings['fields']) - ? array_map(function ($setting) use ($form_settings) { - return $this->_serialize_field( - $setting['id'], - $setting, - $form_settings - ); - }, $settings['fields']) - : []; - - $is_conditional = false; - $conditions = $form_settings['conditions'] ?? []; - foreach ((array) $conditions as $condition) { - $then = $condition['then'] ?? []; - $else = $condition['else'] ?? []; - foreach (array_merge($then, $else) as $effect) { - if ($effect['type'] !== 'field') { - continue; - } - - $is_conditional = - $effect['key'] === $settings['key'] && - $effect['trigger'] === 'hide_field'; - if ($is_conditional) { - break; - } - } - - if ($is_conditional) { - break; - } - } - - switch ($settings['type']) { - case 'email': - $type = 'email'; - break; - case 'checkbox': - $type = 'boolean'; - break; - case 'date': - $type = 'date'; - break; - case 'select': - case 'radio': - case 'listradio': - case 'listselect': - case 'listcountry': - case 'liststate': - case 'listimage': - case 'listmultiselect': - case 'listcheckbox': - $type = 'select'; - break; - case 'starrating': - case 'number': - $type = 'number'; - break; - case 'repeater': - $type = 'mixed'; - break; - case 'file_upload': - $type = 'file'; - break; - case 'textarea': - $type = 'textarea'; - break; - case 'textbox': - case 'lastname': - case 'firstname': - case 'address': - case 'zip': - case 'city': - case 'spam': - case 'phone': - default: - $type = 'text'; - break; - } - - return [ - 'id' => $id, - 'type' => $type, - 'name' => $name, - 'label' => $settings['label'], - 'required' => isset($settings['required']) - ? $settings['required'] === '1' - : false, - 'options' => isset($settings['options']) - ? $settings['options'] - : [], - 'is_file' => $settings['type'] === 'file_upload', - 'is_multi' => $this->is_multi_field($settings), - 'conditional' => $is_conditional, - 'children' => $children, - 'format' => strtolower($settings['date_format'] ?? ''), - 'schema' => $this->field_value_schema($settings, $children), - '_type' => $settings['type'], - ]; - } - - /** - * Checks if a filed is multi value field. - * - * @param array Field settings data. - * - * @return boolean - */ - private function is_multi_field($settings) - { - if ( - in_array( - $settings['type'], - ['listmultiselect', 'listcheckbox', 'repeater'], - true - ) - ) { - return true; - } - - if ( - $settings['type'] === 'listimage' && - ($settings['allow_multi_select'] ?? false) - ) { - return true; - } - - if ( - $settings['type'] === 'file_upload' && - $settings['upload_multi_count'] > 1 - ) { - return true; - } - - return false; - } - - /** - * Gets the field value JSON schema. - * - * @param array $settings Field settings data. - * @param array $children Children fields. - * - * @return array JSON schema of the value of the field. - */ - private function field_value_schema($settings, $children = []) - { - switch ($settings['type']) { - case 'checkbox': - return ['type' => 'boolean']; - case 'textbox': - case 'lastname': - case 'firstname': - case 'address': - case 'zip': - case 'city': - case 'spam': - case 'phone': - case 'email': - case 'textarea': - case 'select': - case 'radio': - case 'checkbox': - case 'date': - case 'listradio': - case 'listselect': - case 'listcountry': - case 'liststate': - return ['type' => 'string']; - case 'listimage': - if ($settings['allow_multi_select'] ?? false) { - $items = []; - for ($i = 0; $i < count($settings['image_options']); $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - ]; - } - - return ['type' => 'string']; - case 'listmultiselect': - case 'listcheckbox': - $items = []; - for ($i = 0; $i < count($settings['options']); $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - ]; - case 'starrating': - case 'number': - return ['type' => 'number']; - case 'repeater': - $i = 0; - $properties = array_reduce( - $children, - function ($props, $child) use ($settings, &$i) { - $field = $settings['fields'][$i]; - $props[$child['name']] = $this->field_value_schema( - $field - ); - $i++; - return $props; - }, - [] - ); - - return [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => $properties, - ], - 'additionalItems' => true, - ]; - case 'file_upload': - return; - default: - return ['type' => 'string']; - } - } - - /** - * Serialize the form's submission data. - * - * @param array $submission Submission data. - * @param array $form_data Form data. - * - * @return array. - */ - public function serialize_submission($submission, $form_data) - { - $data = []; - - foreach ($form_data['fields'] as $field_data) { - if ($field_data['is_file']) { - continue; - } - - $field = $submission['fields'][(int) $field_data['id']]; - - if ($field_data['_type'] === 'repeater') { - $subfields = $field['fields']; - $values = $field['value']; - $fieldset = []; - - $i = 0; - foreach (array_values($values) as $value) { - $row_index = floor($i / count($subfields)); - $row = - count($fieldset) == $row_index - ? [] - : $fieldset[$row_index]; - - $field_index = $i % count($subfields); - $child_field = $field['fields'][$field_index]; - $child_data = $field_data['children'][$field_index]; - - $row[$child_data['name']] = $this->format_field_value( - $child_field['type'], - $value['value'] - ); - - $fieldset[$row_index] = $row; - ++$i; - } - - $data[$field_data['name']] = $fieldset; - } else { - $data[$field_data['name']] = $this->format_field_value( - $field_data['_type'], - $field['value'] - ); - - if ( - $data[$field_data['name']] === false && - $field_data['schema']['type'] !== 'boolean' && - $field_data['conditional'] - ) { - $data[$field_data['name']] = null; - } - } - } - - return $data; - } - - /** - * Formats field values based on its type. - * - * @param string $type Field type. - * @param mixed $value Raw field value. - * - * @return mixed Formated value. - */ - private function format_field_value($type, $value) - { - if ($type === 'hidden') { - $number_val = (float) $value; - if (strval($number_val) === $value) { - return $number_val; - } - - return $value; - } elseif ($type === 'number' || $type === 'starrating') { - return (float) $value; - } elseif ($type === 'date') { - if (is_string($value)) { - return $value; - } - - $_value = ''; - - if (!empty($value['date'])) { - $_value .= $value['date'] . ' '; - } - - if (isset($value['hour'])) { - $_value .= "{$value['hour']}:{$value['minute']}"; - - if (isset($value['ampm'])) { - $_value .= ' ' . $value['ampm']; - } - } - - return trim($_value); - } else { - return $value; - } - } - - /** - * Gets submission uploaded files. - * - * @param WPCF7_Submission $submission Submission instance. - * @param array $form_data Form data. - * - * @return array Uploaded files data. - * - * @todo Ninja forms uploads addon for premium licenses - */ - protected function submission_uploads($submission, $form_data) - { - $uploads = []; - - foreach ($form_data['fields'] as $field_data) { - if (!$field_data['is_file']) { - continue; - } - - $field = $submission['fields'][(int) $field_data['id']]; - - $uploads_path = - wp_upload_dir()['basedir'] . '/ninja-forms/' . $form_data['id']; - $urls = $field['value']; - $paths = []; - foreach ($urls as $url) { - $basename = basename($url); - $path = $uploads_path . '/' . $basename; - if (is_file($path)) { - $paths[] = $path; - } - } - - if (empty($paths)) { - continue; - } - - if ($field_data['is_multi']) { - $uploads[$field_data['name']] = [ - 'path' => $paths, - 'is_multi' => true, - ]; - } else { - $uploads[$field_data['name']] = [ - 'path' => $paths[0], - 'is_multi' => false, - ]; - } - } - - return $uploads; - } - - private function decorate_form_fields($fields) - { - $nf_fields = []; - for ($i = 0; $i < count($fields); $i++) { - $order = $i + 1; - $field = $fields[$i]; - - $args = [ - $order, - $field['name'], - $field['label'] ?? $field['name'], - $field['required'] ?? false, - ]; - - switch ($field['type']) { - case 'number': - $constraints = []; - if (isset($field['min'])) { - $constraints['num_min'] = $field['min']; - } - - if (isset($field['max'])) { - $constraints['num_max'] = $field['max']; - } - - if (isset($field['step'])) { - $constraints['num_step'] = $field['step']; - } - - $args[] = $constraints; - $nf_fields[] = $this->number_field(...$args); - break; - case 'text': - $nf_fields[] = $this->text_field(...$args); - break; - case 'textarea': - $nf_fields[] = $this->textarea_field(...$args); - break; - case 'email': - $nf_fields[] = $this->email_field(...$args); - break; - case 'date': - $nf_fields[] = $this->date_field(...$args); - break; - case 'file': - $args[] = $field['is_multi'] ?? false; - $args[] = $field['filetypes'] ?? ''; - $nf_fields[] = $this->upload_field(...$args); - break; - case 'hidden': - if (isset($field['value'])) { - if (is_bool($field['value'])) { - $field['value'] = $field['value'] ? '1' : '0'; - } - - $args[] = (string) $field['value']; - $nf_fields[] = $this->hidden_field(...$args); - } - - break; - case 'select': - $args[] = $field['options'] ?? []; - $args[] = $field['is_multi'] ?? false; - $nf_fields[] = $this->select_field(...$args); - break; - default: - $args = array_merge([$field['type']], $args); - $nf_fields[] = $this->field_template(...$args); - } - } - - $nf_fields[] = [ - 'objectType' => 'Field', - 'objectDomain' => 'fields', - 'editActive' => false, - 'order' => count($nf_fields) + 1, - 'type' => 'submit', - 'label' => __('Submit', 'forms-bridge'), - 'processing_label' => __('Processing', 'forms-bridge'), - 'key' => 'submit', - ]; - - return array_map(function ($nf_field) { - return [ - 'id' => 'tmp-' . $nf_field['order'], - 'settings' => $nf_field, - ]; - }, $nf_fields); - } - - private function field_template($type, $order, $name, $label, $required) - { - return [ - 'objectType' => 'Field', - 'objectDomain' => 'fields', - 'editActive' => false, - 'order' => (string) $order, - 'type' => $type, - 'label' => $label, - 'key' => $name, - // 'custom_name_attribute' => $name, - 'admin_label' => '', - 'required' => $required ? '1' : '', - 'default' => '', - 'placeholder' => '', - 'container_class' => '', - 'label_pos' => 'default', - ]; - } - - private function upload_field( - $order, - $name, - $label, - $required, - $is_multi, - $filetypes = '' - ) { - $filetypes = preg_replace('/\.(?=[A-Za-z]{2})/', '', $filetypes); - - return array_merge( - $this->field_template( - 'file_upload', - $order, - $name, - $label, - $required - ), - [ - 'upload_types' => $filetypes, - 'upload_multi_count' => $is_multi ? 2 : 1, - ] - ); - } - - private function hidden_field($order, $name, $label, $required, $value) - { - return array_merge( - $this->field_template('hidden', $order, $name, $label, $required), - [ - 'required' => '1', - 'default' => $value, - 'value' => $value, - ] - ); - } - - private function select_field( - $order, - $name, - $label, - $required, - $options, - $is_multi - ) { - $_options = []; - for ($i = 0; $i < count($options); $i++) { - $_options[] = [ - 'label' => $options[$i]['label'], - 'value' => $options[$i]['value'], - 'order' => (string) $i, - 'calc' => '', - 'selected' => 0, - 'max_options' => 0, - 'errors' => [], - 'settingModel' => [ - 'settings' => false, - 'hide_merge_tags' => false, - 'error' => false, - 'name' => 'options', - 'type' => 'option-repeater', - 'label' => - 'Options Add New Import', - 'width' => 'full', - 'group' => '', - 'value' => [ - [ - 'label' => 'One', - 'value' => 'one', - 'calc' => '', - 'selected' => 0, - 'order' => 0, - ], - [ - 'label' => 'Two', - 'value' => 'two', - 'calc' => '', - 'selected' => 0, - 'order' => 0, - ], - [ - 'label' => 'Three', - 'value' => 'three', - 'calc' => '', - 'selected' => 0, - 'order' => 0, - ], - ], - 'columns' => [ - 'label' => [ - 'header' => __('Label', 'forms-bridge'), - 'default' => '', - ], - 'value' => [ - 'header' => __('Value', 'forms-bridge'), - 'default' => '', - ], - 'calc' => [ - 'header' => __('Calc value', 'forms-bridge'), - 'default' => '', - ], - 'selected' => [ - 'header' => - '', - 'default' => 0, - ], - ], - ], - ]; - } - - $type = $is_multi ? 'listcheckbox' : 'listselect'; - - return array_merge( - $this->field_template($type, $order, $name, $label, $required), - [ - 'options' => $_options, - ] - ); - } - - private function text_field($order, $name, $label, $required) - { - return array_merge( - $this->field_template('textbox', $order, $name, $label, $required), - [ - 'input_limit_type' => 'characters', - 'input_limit_msg' => __('Character(s) left', 'forms-bridge'), - 'drawerDisabled' => false, - 'manual_key' => false, - ] - ); - } - - private function number_field( - $order, - $name, - $label, - $required, - $constraints - ) { - return array_merge( - $this->field_template('number', $order, $name, $label, $required), - $constraints - ); - } - - private function textarea_field($order, $name, $label, $required) - { - return array_merge( - $this->field_template('textarea', $order, $name, $label, $required), - [ - 'input_limit_type' => 'characters', - 'input_limit_msg' => __('Character(s) left', 'forms-bridge'), - 'drawerDisabled' => false, - 'manual_key' => false, - ] - ); - } - - private function email_field($order, $name, $label, $required) - { - return array_merge( - $this->field_template('email', $order, $name, $label, $required), - [] - ); - } - - private function date_field($order, $name, $label, $required) - { - return array_merge( - $this->field_template('date', $order, $name, $label, $required), - [ - 'date_format' => 'DD/MM/YYYY', - 'date_mode' => 'date_only', - 'date_default' => 1, - 'hours_24' => 0, - 'year_range_start' => '', - 'year_range_end' => '', - 'minute_increment' => 5, - ] - ); - } - - private function form_template($title) - { - return [ - 'id' => 'tmp-1', - 'settings' => [ - 'objectType' => 'Form Setting', - 'editActive' => true, - 'key' => '', - 'title' => $title, - 'clear_complete' => '1', - 'hide_complete' => '1', - 'default_label_pos' => 'above', - 'show_title' => '0', - 'wrapper_class' => '', - 'element_class' => '', - 'add_submit' => '1', - 'calculations' => [], - 'formContentData' => [], - 'drawerDisabled' => false, - 'unique_field_error' => __( - 'A form with this value has already been submitted.', - 'forms-bridge' - ), - 'sub_limit_msg' => __( - 'The form has reached its submission limit.', - 'forms-bridge' - ), - 'logged_in' => false, - 'not_logged_in_msg' => '', - ], - 'fields' => [], - 'actions' => [ - [ - 'title' => '', - 'key' => '', - 'type' => 'save', - 'active' => '1', - 'created_at' => date('Y-m-d h:i:s'), - 'label' => __('Record Submission', 'forms-bridge'), - 'objectType' => 'Action', - 'objectDomain' => 'actions', - 'editActive' => '', - 'conditions' => [ - 'collapsed' => '', - 'process' => '1', - 'connector' => 'all', - 'when' => [ - [ - 'connector' => 'AND', - 'key' => '', - 'comparator' => '', - 'value' => '', - 'type' => 'field', - 'modelType' => 'when', - ], - ], - 'then' => [ - [ - 'key' => '', - 'trigger' => '', - 'value' => '', - 'type' => 'field', - 'modelType' => 'then', - ], - ], - 'else' => [], - ], - 'payment_gateways' => '', - 'payment_total' => '', - 'tag' => '', - 'to' => '', - 'email_subject' => '', - 'email_message' => '', - 'from_name' => '', - 'from_address' => '', - 'reply_to' => '', - 'email_format' => 'html', - 'cc' => '', - 'bcc' => '', - 'attach_csv' => '', - 'redirect_url' => '', - 'email_message_plain' => '', - 'fields-save-toggle' => 'save_all', - ], - [ - 'title' => '', - 'key' => '', - 'type' => 'successmessage', - 'active' => '1', - 'created_at' => date('Y-m-d h:i:s'), - 'label' => __('Success message', 'forms-bridge'), - 'message' => __( - 'Thank you for filling out my form!', - 'forms-bridge' - ), - 'objectType' => 'Action', - 'objectDomain' => 'actions', - 'editActive' => '', - 'conditions' => [ - 'collapsed' => '', - 'process' => '1', - 'connector' => 'all', - 'when' => [ - [ - 'connector' => 'AND', - 'key' => '', - 'comparator' => '', - 'value' => '', - 'type' => 'field', - 'modelType' => 'when', - ], - ], - 'then' => [ - [ - 'key' => '', - 'trigger' => '', - 'value' => '', - 'type' => 'field', - 'modelType' => 'then', - ], - ], - 'else' => [], - ], - 'payment_gateways' => '', - 'payment_total' => '', - 'tag' => '', - 'to' => '', - 'email_subject' => '', - 'email_message' => '', - 'from_name' => '', - 'from_address' => '', - 'reply_to' => '', - 'email_format' => 'html', - 'cc' => '', - 'bcc' => '', - 'attach_csv' => '', - 'redirect_url' => '', - 'success_msg' => __( - '

Form submitted successfully.

', - 'forms-bridge' - ), - 'email_message_plain' => '', - ], - ], - ]; - } -} - -Integration::setup(); diff --git a/integrations/woo/class-woo-integration.php b/integrations/woo/class-woo-integration.php deleted file mode 100644 index 5a4bd49c..00000000 --- a/integrations/woo/class-woo-integration.php +++ /dev/null @@ -1,735 +0,0 @@ - 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'status' => ['type' => 'string'], - 'currency' => ['type' => 'string'], - 'version' => ['type' => 'string'], - 'prices_include_tax' => ['type' => 'boolean'], - 'date_created' => ['type' => 'string'], - 'date_modified' => ['type' => 'string'], - 'discount_total' => ['type' => 'number'], - 'discount_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'shipping_total' => ['type' => 'number'], - 'shipping_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'cart_total' => ['type' => 'number'], - 'cart_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'total' => ['type' => 'number'], - 'total_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'customer_id' => ['type' => 'integer'], - 'order_key' => ['type' => 'string'], - 'billing' => [ - 'type' => 'object', - 'properties' => [ - 'first_name' => ['type' => 'string'], - 'last_name' => ['type' => 'string'], - 'company' => ['type' => 'string'], - 'address_1' => ['type' => 'string'], - 'address_2' => ['type' => 'string'], - 'city' => ['type' => 'string'], - 'state' => ['type' => 'string'], - 'postcode' => ['type' => 'string'], - 'country' => ['type' => 'string'], - 'email' => ['type' => 'string'], - 'phone' => ['type' => 'string'], - ], - 'additionalProperties' => true, - ], - 'shipping' => [ - 'type' => 'object', - 'properties' => [ - 'first_name' => ['type' => 'string'], - 'last_name' => ['type' => 'string'], - 'company' => ['type' => 'string'], - 'address_1' => ['type' => 'string'], - 'address_2' => ['type' => 'string'], - 'city' => ['type' => 'string'], - 'state' => ['type' => 'string'], - 'postcode' => ['type' => 'string'], - 'country' => ['type' => 'string'], - 'phone' => ['type' => 'string'], - ], - 'additionalProperties' => true, - ], - 'payment_method' => ['type' => 'string'], - 'payment_method_title' => ['type' => 'string'], - 'transaction_id' => ['type' => 'string'], - 'customer_ip_address' => ['type' => 'string'], - 'customer_user_agent' => ['type' => 'string'], - 'created_via' => ['type' => 'string'], - 'customer_note' => ['type' => 'string'], - 'date_completed' => ['type' => 'string'], - 'date_paid' => ['type' => 'string'], - 'cart_hash' => ['type' => 'string'], - 'order_stock_reduced' => ['type' => 'boolean'], - 'download_permissions_granted' => ['type' => 'boolean'], - 'new_order_email_sent' => ['type' => 'boolean'], - 'recorded_sales' => ['type' => 'boolean'], - 'recorded_coupon_usage_counts' => ['type' => 'boolean'], - 'number' => ['type' => 'integer'], - // 'meta_data' => [ - // 'type' => 'array', - // 'items' => [ - // 'type' => 'object', - // 'properties' => [ - // 'id' => ['type' => 'integer'], - // 'key' => ['type' => 'string'], - // 'value' => ['type' => 'string'], - // ], - // 'required' => ['id', 'key', 'value'], - // 'additionalProperties' => false, - // ], - // 'additionalItems' => true, - // ], - 'line_items' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'order_id' => ['type' => 'integer'], - 'name' => ['type' => 'string'], - 'product_id' => ['type' => 'integer'], - 'variation_id' => ['type' => 'integer'], - 'quantity' => ['type' => 'integer'], - 'tax_class' => ['type' => 'string'], - 'subtotal' => ['type' => 'number'], - 'subtotal_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'total' => ['type' => 'number'], - 'total_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'taxes' => [ - 'type' => 'object', - 'properties' => [ - 'subtotal' => [ - 'type' => 'array', - 'items' => ['type' => 'number'], - 'additionalItems' => true, - ], - 'total' => [ - 'type' => 'array', - 'items' => ['type' => 'number'], - 'additionalItems' => true, - ], - ], - ], - 'product' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'parent_id' => ['type' => 'integer'], - 'sku' => ['type' => 'string'], - 'name' => ['type' => 'string'], - 'slug' => ['type' => 'string'], - 'price' => ['type' => 'number'], - 'sale_price' => ['type' => 'number'], - 'regular_price' => ['type' => 'number'], - 'stock_quantity' => ['type' => 'number'], - 'stock_status' => ['type' => 'string'], - ], - ], - // 'meta_data' => [ - // 'type' => 'array', - // 'items' => [ - // 'type' => 'object', - // 'properties' => [ - // 'id' => ['type' => 'integer'], - // 'key' => ['type' => 'string'], - // 'value' => ['type' => 'string'], - // ], - // ], - // ], - ], - ], - 'additionalItems' => true, - 'minItems' => 1, - ], - 'tax_lines' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'order_id' => ['type' => 'integer'], - 'name' => ['type' => 'string'], - 'rate_code' => ['type' => 'string'], - 'rate_id' => ['type' => 'integer'], - 'label' => ['type' => 'string'], - 'compound' => ['type' => 'boolean'], - 'tax_total' => ['type' => 'number'], - 'shipping_tax_total' => ['type' => 'number'], - 'rate_percent' => ['type' => 'number'], - // 'meta_data' => [ - // 'type' => 'array', - // 'items' => [ - // 'type' => 'object', - // 'properties' => [ - // 'id' => ['type' => 'integer'], - // 'key' => ['type' => 'string'], - // 'value' => ['type' => 'string'], - // ], - // ], - // 'additionalItems' => true, - // ], - ], - ], - 'additionalItems' => true, - ], - 'shipping_lines' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'order_id' => ['type' => 'integer'], - 'name' => ['type' => 'string'], - 'method_id' => ['type' => 'string'], - 'method_title' => ['type' => 'string'], - 'instance_id' => ['type' => 'integer'], - 'total' => ['type' => 'number'], - 'total_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'tax_status' => ['type' => 'string'], - 'taxes' => [ - 'type' => 'object', - 'properties' => [ - 'total' => ['type' => 'number'], - 'subtotal' => ['type' => 'number'], - ], - 'required' => ['total'], - ], - // 'meta_data' => [ - // 'type' => 'array', - // 'items' => [ - // 'type' => 'object', - // 'properties' => [ - // 'id' => ['type' => 'integer'], - // 'key' => ['type' => 'string'], - // 'value' => ['type' => 'string'], - // ], - // ], - // ], - ], - ], - 'additionalItems' => true, - ], - 'fee_lines' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'order_id' => ['type' => 'integer'], - 'name' => ['type' => 'string'], - 'tax_class' => ['type' => 'string'], - 'tax_status' => ['type' => 'string'], - 'amount' => ['type' => 'number'], - 'total' => ['type' => 'number'], - 'total_tax' => [ - 'type' => 'object', - 'properties' => [ - 'amount' => ['type' => 'number'], - 'rate' => ['type' => 'number'], - 'percentage' => ['type' => 'number'], - ], - ], - 'taxes' => [ - 'type' => 'object', - 'properties' => [ - 'total' => [ - 'type' => 'array', - 'items' => ['type' => 'number'], - 'additionalItems' => true, - ], - ], - 'required' => ['total'], - ], - // 'meta_data' => [ - // 'type' => 'array', - // 'items' => [ - // 'type' => 'object', - // 'properties' => [ - // 'id' => ['type' => 'integer'], - // 'key' => ['type' => 'string'], - // 'value' => ['type' => 'string'], - // ], - // ], - // ], - ], - ], - 'additionalItems' => true, - ], - 'coupon_lines' => [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => [ - 'id' => ['type' => 'integer'], - 'order_id' => ['type' => 'integer'], - 'name' => ['type' => 'string'], - 'code' => ['type' => 'string'], - 'discount' => ['type' => 'number'], - 'discount_tax' => ['type' => 'number'], - // 'meta_data' => [ - // 'type' => 'array', - // 'items' => [ - // 'type' => 'object', - // 'properties' => [ - // 'id' => ['type' => 'integer'], - // 'key' => ['type' => 'string'], - // 'value' => ['type' => 'string'], - // ], - // ], - // ], - ], - ], - 'additionalItems' => true, - ], - ], - 'additionalProperties' => false, - ]; - - private static function decorate_tax($tax, $total) - { - try { - $tax = (float) $tax; - $rate = $tax / $total; - $rate = floor($rate * 1000) / 1000; - - return [ - 'amount' => $tax, - 'rate' => $rate, - 'percentage' => $rate * 100, - ]; - } catch (TypeError | DivisionByZeroError) { - return [ - 'amount' => 0, - 'rate' => 0, - 'percentage' => 0, - ]; - } - } - - public function init() - { - add_action( - 'woocommerce_order_status_changed', - static function ($order_id, $old_status, $new_status) { - $is_bridged = - get_post_meta( - $order_id, - self::is_order_bridged_custom_field, - true - ) === '1'; - - $trigger_submission = apply_filters( - 'forms_bridge_woo_trigger_submission', - !$is_bridged && $new_status === 'completed', - $order_id, - $new_status, - $old_status, - $is_bridged - ); - - if ($trigger_submission) { - self::$order_id = $order_id; - - add_action( - 'forms_bridge_after_submission', - function () { - update_post_meta( - self::$order_id, - self::is_order_bridged_custom_field, - '1' - ); - }, - 90 - ); - - Forms_Bridge::do_submission(); - } - }, - 10, - 3 - ); - } - - public function form() - { - if (empty(self::$order_id)) { - return; - } - - return $this->get_form_by_id(1); - } - - public function get_form_by_id($form_id) - { - if ($form_id != 1) { - return; - } - - WC()->session = new WC_Session_Handler(); - WC()->customer = new WC_Customer(); - - return apply_filters( - 'forms_bridge_form_data', - [ - '_id' => 'woo:1', - 'id' => 1, - 'title' => __('Woo Checkout', 'forms-bridge'), - 'bridges' => FBAPI::get_form_bridges(1, 'woo'), - 'fields' => $this->serialize_order_fields(), - ], - WC()->checkout, - 'woo' - ); - } - - public function forms() - { - return [$this->get_form_by_id(1)]; - } - - public function create_form($data) - { - return 1; - } - - public function remove_form($form_id) - { - return; - } - - public function submission_id() - { - if (self::$order_id) { - return (string) self::$order_id; - } - } - - public function submission($raw) - { - if (empty(self::$order_id)) { - return; - } - - return $this->serialize_order(self::$order_id); - } - - public function uploads() - { - return []; - } - - private function serialize_order_fields() - { - $checkout_fields = WC()->checkout->checkout_fields; - - $fields = []; - foreach ( - self::order_data_schema['properties'] - as $name => $field_schema - ) { - $fields[] = self::decorate_order_field($name, $field_schema); - } - - foreach (array_keys($checkout_fields['billing']) as $name) { - $name = str_replace('billing_', '', $name); - if (isset(self::order_data_schema['billing'][$name])) { - continue; - } - - $index = array_search('billing', array_column($fields, 'name')); - - $billing_field = &$fields[$index]; - $billing_field['schema']['properties'][$name] = [ - 'type' => 'text', - ]; - } - - foreach (array_keys($checkout_fields['shipping']) as $name) { - $name = str_replace('shipping_', '', $name); - if (isset(self::order_data_schema['shipping'][$name])) { - continue; - } - - $index = array_search('shipping', array_column($fields, 'name')); - - $shipping_field = &$fields[$index]; - $shipping_field['schema']['properties'][$name] = [ - 'type' => 'text', - ]; - } - - return $fields; - } - - private function decorate_order_field($name, $schema) - { - switch ($schema['type']) { - case 'string': - $field_type = 'text'; - break; - case 'number': - case 'integer': - $field_type = 'number'; - break; - case 'boolean': - $field_type = 'boolean'; - break; - case 'array': - $field_type = 'select'; - break; - default: - $field_type = $schema['type']; - } - - return [ - 'id' => null, - 'name' => $name, - 'label' => $name, - 'type' => $field_type, - 'required' => true, - 'is_file' => false, - 'is_multi' => $schema['type'] === 'array', - 'conditional' => false, - 'schema' => $schema, - ]; - } - - private function serialize_order($order_id) - { - $order = wc_get_order($order_id); - if (empty($order)) { - return; - } - - $checkout = WC()->checkout; - - $data = $order->get_data(); - unset($data['meta_data']); - - $checkout_fields = $checkout->checkout_fields; - foreach (array_keys($checkout_fields['billing']) as $name) { - $unprefixed = str_replace('billing_', '', $name); - if (!isset($data['billing'][$unprefixed])) { - $data['billing'][$unprefixed] = $checkout->get_value($name); - } - } - - foreach (array_keys($checkout_fields['shipping']) as $name) { - $unprefixed = str_replace('shipping_', '', $name); - if (!isset($data['shipping'][$unprefixed])) { - $data['shipping'][$unprefixed] = $checkout->get_value($name); - } - } - - $tax_lines = []; - foreach ($data['tax_lines'] as $tax_line) { - $line_data = $tax_line->get_data(); - unset($line_data['meta_data']); - $tax_lines[] = $line_data; - } - - $data['tax_lines'] = $tax_lines; - - $line_items = []; - foreach ($data['line_items'] as $line_item) { - $item_data = $line_item->get_data(); - unset($item_data['meta_data']); - - $product = $line_item->get_product(); - $item_data['product'] = [ - 'id' => $product->get_id(), - 'parent_id' => $product->get_parent_id(), - 'slug' => $product->get_slug(), - 'sku' => $product->get_sku(), - 'name' => $product->get_name(), - 'price' => $product->get_price(), - 'sale_price' => $product->get_sale_price(), - 'regular_price' => $product->get_regular_price(), - 'stock_quantity' => $product->get_stock_quantity(), - 'stock_status' => $product->get_stock_status(), - ]; - - $item_data['total_tax'] = self::decorate_tax( - $line_item['total_tax'], - $line_item['total'] - ); - - $item_data['subtotal_tax'] = self::decorate_tax( - $line_item['subtotal_tax'], - $line_item['subtotal'] - ); - - $line_items[] = $item_data; - } - - $data['line_items'] = $line_items; - - $shipping_lines = []; - foreach ($data['shipping_lines'] as $shipping_line) { - $line_data = $shipping_line->get_data(); - unset($line_data['meta_data']); - - $line_data['total_tax'] = self::decorate_tax( - $line_data['total_tax'], - $line_data['total'] - ); - - $shipping_lines[] = $line_data; - } - - $data['shipping_lines'] = $shipping_lines; - - $coupon_lines = []; - foreach ($data['coupon_lines'] ?? [] as $coupon_line) { - $line_data = $coupon_line->get_data(); - unset($line_data['meta_data']); - - $line_data['discount_tax'] = self::decorate_tax( - $line_data['discount_tax'], - $line_data['discount'] - ); - - $coupon_lines[] = $line_data; - } - - $data['coupon_lines'] = $coupon_lines; - - $fee_lines = []; - foreach ($data['fee_lines'] ?? [] as $fee_line) { - $line_data = $fee_line->get_data(); - unset($line_data['meta_data']); - - $line_data['total_tax'] = self::decorate_tax( - $line_data['total_tax'], - $line_data['total'] - ); - - $fee_lines[] = $line_data; - } - - $data['fee_lines'] = $fee_lines; - - $data['discount_tax'] = self::decorate_tax( - $data['discount_tax'], - $data['discount_total'] - ); - - $data['shipping_tax'] = self::decorate_tax( - $data['shipping_tax'], - $data['shipping_total'] - ); - - $data['total_tax'] = self::decorate_tax( - $data['total_tax'], - $data['total'] - ); - - $cart_total = 0; - foreach ($data['line_items'] as $line_data) { - $cart_total += $line_data['total']; - } - - foreach ($data['fee_lines'] as $line_data) { - $cart_total += $line_data['total']; - } - - $data['cart_total'] = $cart_total; - $data['cart_tax'] = self::decorate_tax( - $data['cart_tax'], - $data['cart_total'] - ); - - return rest_sanitize_value_from_schema($data, self::order_data_schema); - } -} - -Integration::setup(); diff --git a/integrations/wpcf7/class-wpcf7-integration.php b/integrations/wpcf7/class-wpcf7-integration.php deleted file mode 100644 index 5efa146b..00000000 --- a/integrations/wpcf7/class-wpcf7-integration.php +++ /dev/null @@ -1,547 +0,0 @@ -serialize_form($form); - } - - /** - * Retrives a contact form's data by ID. - * - * @param int $form_id Form ID. - * @return array $form_data Form data. - */ - public function get_form_by_id($form_id) - { - $form = WPCF7_ContactForm::get_instance($form_id); - if (!$form) { - return null; - } - - return $this->serialize_form($form); - } - - /** - * Retrives available constact forms as form data. - * - * @return array $forms Collection of form data. - */ - public function forms() - { - $forms = WPCF7_ContactForm::find(['post_status', 'publish']); - return array_map(function ($form) { - return $this->serialize_form($form); - }, $forms); - } - - /** - * Creates a form from the given template fields. - * - * @param array $data Form template data. - * - * @return int|null ID of the new form. - * - * @todo Fix form email attribute. - */ - public function create_form($data) - { - if (empty($data['title']) || empty($data['fields'])) { - return; - } - - $form = $this->fields_to_form($data['fields']); - $email = $this->form_email($data['title'], $data['fields']); - - $contact_form = wpcf7_save_contact_form([ - 'title' => $data['title'], - 'locale' => apply_filters( - 'wpct_i18n_current_language', - null, - 'locale' - ), - 'form' => $form, - 'mail' => $email, - ]); - - if (!$contact_form) { - return; - } - - return $contact_form->id(); - } - - /** - * Removes a form by ID. - * - * @param integer $form_id Form ID. - * - * @return boolean Removal result. - */ - public function remove_form($form_id) - { - $result = wp_delete_post($form_id); - return !!$result; - } - - public function submission_id() - { - $submission = $this->submission(true); - if ($submission) { - return $submission->get_posted_data_hash(); - } - } - - /** - * Retrives the current submission data. - * - * @param boolean $raw Control if the submission is serialized before exit. - * - * @return WPCF7_Submission|array Submission data. - */ - public function submission($raw = false) - { - $submission = WPCF7_Submission::get_instance(); - - if (!$submission) { - return null; - } elseif ($raw) { - return $submission; - } - - $form = $this->form(); - return $this->serialize_submission($submission, $form); - } - - /** - * Retrives the current submission uploaded files. - * - * @return array Uploaded files data. - */ - public function uploads() - { - $submission = WPCF7_Submission::get_instance(); - if (!$submission) { - return null; - } - - return $this->submission_uploads($submission); - } - - /** - * Serializes a contact form instance as array data. - * - * @param WPCF7_ContactForm $form Form instance. - * - * @return array. - */ - public function serialize_form($form) - { - $form_id = (int) $form->id(); - $fields = array_filter( - array_map(function ($field) { - return $this->serialize_field($field); - }, $form->scan_form_tags()) - ); - - return apply_filters( - 'forms_bridge_form_data', - [ - '_id' => 'wpcf7:' . $form_id, - 'id' => $form_id, - 'title' => $form->title(), - 'bridges' => FBAPI::get_form_bridges($form_id, 'wpcf7'), - 'fields' => array_values($fields), - ], - $form, - 'wpcf7' - ); - } - - /** - * Serializes a form tags as array data. - * - * @param WPCF7_FormTag $field Form tag instance. - * @param array $form_data Form data. - * - * @return array. - */ - private function serialize_field($field) - { - if (in_array($field->basetype, ['response', 'submit', 'quiz'])) { - return; - } - - $type = $field->basetype; - if ($type === 'conditional') { - $type = $field->get_option('type')[0]; - } elseif ($type === 'hidden') { - $type = 'text'; - } - - $options = []; - if (is_array($field->values)) { - $values = $field->pipes->collect_afters(); - for ($i = 0; $i < sizeof($field->raw_values); $i++) { - $options[] = [ - 'value' => $values[$i], - 'label' => $field->labels[$i], - ]; - } - } - - $format = $type === 'date' ? 'yyyy-mm-dd' : ''; - - return apply_filters( - 'forms_bridge_form_field_data', - [ - 'id' => $field->get_id_option(), - 'type' => $type, - 'name' => $field->raw_name, - 'label' => $field->name, - 'required' => $field->is_required(), - 'options' => $options, - 'is_file' => $type === 'file', - 'is_multi' => $this->is_multi_field($field), - 'conditional' => - $field->basetype === 'conditional' || - $field->basetype === 'fileconditional', - 'format' => $format, - 'schema' => $this->field_value_schema($field), - '_type' => $field->basetype, - ], - $field, - 'wpcf7' - ); - } - - /** - * Checks if a filed is multi value field. - * - * @param WPCF7_FormTag Target tag instance. - * - * @return boolean - */ - private function is_multi_field($tag) - { - $type = str_replace('*', '', $tag->type); - - if ($type === 'checkbox') { - return !$tag->has_option('exclusive'); - } - - if ($type === 'select') { - return $tag->has_option('multiple'); - } - - return false; - } - - /** - * Gets the field value JSON schema. - * - * @param WPCF7_FormTag $tag Tag instance. - * - * @return array JSON schema of the value of the field. - */ - private function field_value_schema($tag) - { - $type = str_replace('*', '', $tag->type); - - switch ($type) { - case 'text': - case 'textarea': - case 'date': - case 'email': - case 'url': - case 'quiz': - case 'radio': - case 'iban': - case 'vat': - return ['type' => 'string']; - case 'select': - if ($tag->has_option('multiple')) { - $items = []; - for ($i = 0; $i < count($tag->values); $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - 'minItems' => $tag->is_required() ? 1 : 0, - 'maxItems' => count($tag->values), - ]; - } - - return ['type' => 'string']; - case 'checkbox': - if ($tag->has_option('exclusive')) { - return ['type' => 'string']; - } - - $items = []; - for ($i = 0; $i < count($tag->values); $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - 'minItems' => $tag->is_required() ? 1 : 0, - 'maxItems' => count($tag->values), - ]; - case 'file': - case 'files': - return; - case 'acceptance': - return ['type' => 'boolean']; - case 'number': - return ['type' => 'number']; - default: - return ['type' => 'string']; - } - } - - /** - * Serializes the form's submission data. - * - * @param WPCF7_Submission $submission Submission instance. - * @param array $form Form data. - * - * @return array Submission data. - */ - public function serialize_submission($submission, $form_data) - { - $data = $submission->get_posted_data(); - - foreach ($data as $key => $val) { - $i = array_search($key, array_column($form_data['fields'], 'name')); - $field = $form_data['fields'][$i]; - - if ($field['_type'] === 'hidden') { - $number_val = (float) $val; - if (strval($number_val) === $val) { - $data[$key] = $number_val; - } else { - $data[$key] = $val; - } - } elseif ($field['_type'] === 'number') { - $data[$key] = (float) $val; - } elseif (is_array($val) && !$field['is_multi']) { - $data[$key] = $val[0]; - } elseif ($field['_type'] === 'file') { - unset($data[$key]); - } - } - - return $data; - } - - /** - * Gets submission uploaded files. - * - * @param WPCF7_Submission $submission Submission instance. - * - * @return array Uploaded files data. - */ - protected function submission_uploads($submission) - { - $uploads = []; - $uploads = $submission->uploaded_files(); - foreach ($uploads as $file_name => $paths) { - if (!empty($paths)) { - $is_multi = sizeof($paths) > 1; - $uploads[$file_name] = [ - 'path' => $is_multi ? $paths : $paths[0], - 'is_multi' => $is_multi, - ]; - } - } - - return $uploads; - } - - /** - * Gets form fields from a template and return a contact form content string. - * - * @param array $fields. - * - * @return string Form content. - */ - private function fields_to_form($fields) - { - $form = ''; - foreach ($fields as $field) { - if ($field['type'] == 'hidden') { - if (isset($field['value'])) { - if (is_bool($field['value'])) { - $field['value'] = $field['value'] ? '1' : '0'; - } - - $form .= $this->field_to_tag($field) . "\n\n"; - } - } else { - $form .= "\n\n"; - } - } - - $form .= sprintf('[submit "%s"]', __('Submit', 'forms-bridge')); - return $form; - } - - /** - * Gets a field template data and returns a form tag string. - * - * @param array $field. - * - * @return string. - */ - private function field_to_tag($field) - { - if (isset($field['value'])) { - $type = 'hidden'; - } else { - $type = sanitize_text_field($field['type']); - - if (($field['required'] ?? false) && $type !== 'hidden') { - $type .= '*'; - } - } - - $name = sanitize_text_field($field['name']); - $tag = "[{$type} {$name} "; - - foreach ($field as $key => $val) { - if ( - !in_array($key, ['name', 'type', 'value', 'required', 'label']) - ) { - $key = sanitize_text_field($key); - $val = sanitize_text_field($val); - $tag .= "{$key}:{$val} "; - } - } - - $value = null; - - if ($type === 'select' || $type === 'select*') { - $options = array_map(function ($opt) { - return $opt['label'] . '|' . $opt['value']; - }, $field['options'] ?? []); - - $value = implode('" "', $options); - } elseif (!empty($field['value'])) { - $value = sanitize_text_field((string) $field['value']); - } - - if ($value) { - $tag .= "\"{$value}\""; - } - - return $tag . ']'; - } - - private function form_email($title, $fields) - { - $site_url = get_option('siteurl'); - $host = wp_parse_url($site_url)['host'] ?? 'example.coop'; - - $email_index = array_search('email', array_column($fields, 'type')); - if ($email_index) { - $replay_to = 'Replay-To: [' . $fields[$email_index]['name'] . ']'; - } else { - $replay_to = ''; - } - - $body = __( - "This are the responses to the contact form:\n\n", - 'forms-bridge' - ); - - foreach ($fields as $field) { - $label = $field['label'] ?? $field['name']; - $body .= ' * ' . esc_html($label) . ': [' . $field['name'] . "]\n"; - } - - $notice = sprintf( - /* translators: 1: blog name, 2: blog URL */ - __( - 'This is a notification that a contact form was submitted on your website (%1$s %2$s).', - 'forms-bridge' - ), - '[_site_title]', - '[_site_url]' - ); - - $body .= "\n---\n{$notice}"; - - return [ - 'recipient' => '[_site_admin_email]', - 'sender' => "[_site_title] ", - 'subject' => "[_site_title] \"{$title}\"", - 'additional_headers' => $replay_to, - 'body' => $body, - 'attachments' => '', - ]; - } -} - -Integration::setup(); diff --git a/integrations/wpforms/class-wpforms-integration.php b/integrations/wpforms/class-wpforms-integration.php deleted file mode 100644 index 66e4be3c..00000000 --- a/integrations/wpforms/class-wpforms-integration.php +++ /dev/null @@ -1,1049 +0,0 @@ -obj('form')->get($form_id); - - if (!$form) { - return; - } - - return $this->serialize_form($form); - } - - /** - * Retrives a WPForms_Form_Handler's data by ID. - * - * @param int $form_id ID of the form. - * - * @return array. - */ - public function get_form_by_id($form_id) - { - $form = wpforms()->obj('form')->get($form_id); - - if (!$form) { - return null; - } - - return $this->serialize_form($form); - } - - /** - * Retrives available form instances' data. - * - * @return array Collection of forms data. - */ - public function forms() - { - $forms = array_filter((array) wpforms()->obj('form')->get()); - return array_map(function ($form) { - return $this->serialize_form($form); - }, $forms); - } - - /** - * Creates a form from the given template fields. - * - * @param array $data Form template data. - * - * @return int|null ID of the new form. - */ - public function create_form($data) - { - $form_title = esc_html($data['title']); - $title_query = new WP_Query([ - 'post_type' => 'wpforms', - 'title' => $form_title, - 'posts_per_page' => 1, - 'fields' => 'ids', - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - 'no_found_rows' => true, - ]); - $title_exists = $title_query->post_count > 0; - - add_filter( - 'wpforms_create_form_args', - function ($args, $create_data) use ($data) { - if ($create_data['template'] === 'forms-bridge') { - $args['post_content'] = $this->encode_form_data($data); - } - return $args; - }, - 99, - 2 - ); - - $form_id = wpforms() - ->obj('form') - ->add( - esc_html($data['title']), - [], - [ - 'template' => 'forms-bridge', - 'category' => 'all', - 'subcategory' => 'all', - ] - ); - - if ($title_exists) { - // $form_title = $form_title . ' (ID #' . $form_id . ')'; - remove_action('post_updated', 'wp_save_post_revision'); - wp_update_post([ - 'ID' => $form_id, - 'post_title' => $form_title . ' (ID #' . $form_id . ')', - ]); - add_action('post_updated', 'wp_save_post_revision'); - } - - $form = wpforms()->obj('form')->get($form_id); - $form_data = wpforms_decode($form->post_content); - $form_data['id'] = $form_id; - $form_data['settings']['form_title'] = $form_title; - - wpforms() - ->obj('form') - ->update($form_id, $form_data, ['context' => 'save_form']); - - return $form_id; - } - - /** - * Removes a form by ID. - * - * @param integer $form_id Form ID. - * - * @return boolean Removal result. - */ - public function remove_form($form_id) - { - $post = wp_delete_post($form_id); - return boolval($post->ID ?? false); - } - - public function submission_id() - { - $submission = $this->submission(true); - if ($submission) { - return $submission['entry_id']; - } - } - - /** - * Retrives the current form submission data. - * - * @param boolean $raw Control if the submission is serialized before exit. - * - * @return array Submission data. - */ - public function submission($raw = false) - { - $form = $this->form(); - - if (!$form) { - return; - } - - if (empty(self::$submission)) { - return; - } elseif ($raw) { - return self::$submission; - } - - return $this->serialize_submission(self::$submission, $form); - } - - /** - * Retrives the current submission uploaded files. - * - * @return array Uploaded files data. - */ - public function uploads() - { - $submission = self::$submission; - if (!$submission) { - return null; - } - - return $this->submission_uploads($submission, $this->form()); - } - - /** - * Serializes a wp form post instance as array data. - * - * @param WP_Post $form Form post instance. - * - * @return array - */ - public function serialize_form($form) - { - $data = - $form instanceof WP_Post - ? wpforms_decode($form->post_content) - : $form; - - $form_id = isset($data['id']) ? (int) $data['id'] : $form->ID; - - return apply_filters( - 'forms_bridge_form_data', - [ - '_id' => 'wpforms:' . $form_id, - 'id' => $form_id, - 'title' => $data['settings']['form_title'] ?? '', - 'bridges' => FBAPI::get_form_bridges($form_id, 'wpforms'), - 'fields' => array_reduce( - $data['fields'], - function ($fields, $field) use ($data) { - $field = $this->serialize_field( - $field, - $fields, - $data['fields'] - ); - - if ($field) { - $fields[] = $field; - } - - return $fields; - }, - [] - ), - ], - $data, - 'wpforms' - ); - } - - /** - * Serializes a field as array data. - * - * @param array $field Field data. - * @param array $form_data Form data. - * - * @return array - */ - private function serialize_field($field, $fields = [], $all_fields = []) - { - if ( - in_array($field['type'], [ - 'submit', - 'pagebreak', - 'layout', - 'captcha', - 'content', - 'entry-preview', - 'html', - 'divider', - ]) - ) { - return; - } - - $repeaters = array_filter($fields, static function ($field) { - return $field['type'] === 'repeater'; - }); - - $fields_in_repeater = array_reduce( - $repeaters, - static function ($ids, $repeater) { - foreach ($repeater['children'] as $child) { - $ids[] = $child['id']; - } - - return $ids; - }, - [] - ); - - $children = []; - if ($field['type'] === 'repeater') { - foreach ($field['columns'] as $column) { - foreach ($column['fields'] as $field_id) { - $children[] = $field_id; - } - } - - $children = array_map( - function ($field) { - return $this->serialize_field($field); - }, - array_filter($all_fields, static function ($field) use ( - $children - ) { - return in_array($field['id'], $children); - }) - ); - } elseif (in_array($field['id'], $fields_in_repeater)) { - return; - } - - $format = $field['date_format'] ?? ''; - if ($format) { - $format = - [ - 'd/m/Y' => 'dd/mm/yyyy', - 'm/d/Y' => 'mm/dd/yyyy', - ][$format] ?? ''; - } - - switch ($field['type']) { - case 'url': - $type = 'url'; - break; - case 'email': - $type = 'email'; - break; - case 'radio': - case 'payment-select': - case 'payment-multiple': - case 'payment-checkbox': - case 'select': - case 'checkbox': - $type = 'select'; - break; - case 'number-slider': - case 'number': - $type = 'number'; - break; - case 'file-upload': - $type = 'file'; - break; - case 'repeater': - $type = 'mixed'; - break; - case 'date-time': - $type = 'date'; - break; - case 'textarea': - $type = 'textarea'; - break; - case 'name': - case 'text': - case 'password': - case 'payment-total': - case 'payment-single': - case 'address': - default: - $type = 'text'; - break; - } - - return apply_filters( - 'forms_bridge_form_field_data', - [ - 'id' => (int) ($field['id'] ?? 0), - 'type' => $type, - 'name' => $field['label'] ?? '', - 'label' => $field['label'] ?? '', - 'required' => ($field['required'] ?? '') === '1', - 'options' => isset($field['choices']) ? $field['choices'] : [], - 'is_file' => $field['type'] === 'file-upload', - 'is_multi' => $this->is_multi_field($field), - 'conditional' => false, - 'format' => $format, - 'children' => array_values($children), - 'schema' => $this->field_value_schema($field, $children), - '_type' => $field['type'], - ], - $field, - 'wpforms' - ); - } - - /** - * Checks if a filed is multi value field. - * - * @param array Target field instance. - * - * @return boolean - */ - private function is_multi_field($field) - { - if ($field['type'] === 'checkbox' || $field['type'] === 'repeater') { - return true; - } - - if ($field['type'] === 'select' && ($field['multiple'] ?? false)) { - return true; - } - - if ( - $field['type'] === 'file-upload' && - ($field['max_file_number'] ?? 1) !== '1' - ) { - return true; - } - - return false; - } - - /** - * Gets the field value JSON schema. - * - * @param array $field Field instance. - * @param array $children Children fields. - * - * @return array JSON schema of the value of the field. - */ - private function field_value_schema($field, $children = []) - { - switch ($field['type']) { - case 'name': - case 'email': - case 'text': - case 'textarea': - case 'payment-total': - case 'payment-single': - case 'radio': - case 'password': - case 'url': - return ['type' => 'string']; - case 'number-slider': - case 'number': - return ['type' => 'number']; - case 'payment-select': - case 'payment-multiple': - case 'payment-checkbox': - case 'select': - if ($field['multiple'] ?? false) { - $items = []; - for ($i = 0; $i < count($field['choices']); $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - ]; - } - - return ['type' => 'string']; - case 'checkbox': - $items = []; - for ($i = 0; $i < count($field['choices']); $i++) { - $items[] = ['type' => 'string']; - } - - return [ - 'type' => 'array', - 'items' => $items, - 'additionalItems' => false, - ]; - case 'date-time': - if ($field['format'] === 'date-time') { - return [ - 'type' => 'object', - 'properties' => [ - 'date' => ['type' => 'string'], - 'time' => ['type' => 'string'], - ], - ]; - } - - return ['type' => 'string']; - case 'address': - $properties = [ - 'address1' => ['type' => 'string'], - 'city' => ['type' => 'string'], - 'state' => ['type' => 'string'], - ]; - - if (($field['address2_hide'] ?? '0') !== '1') { - $properties['address2'] = ['type' => 'string']; - } - - if (($field['postal_hide'] ?? '0') !== '1') { - $properties['postal'] = ['type' => 'string']; - } - - if ( - ($field['country_hide'] ?? '0') !== '1' && - $field['scheme'] !== 'us' - ) { - $properties['country'] = ['type' => 'string']; - } - - return [ - 'type' => 'object', - 'properties' => $properties, - ]; - case 'file-upload': - return; - case 'repeater': - $properties = array_reduce( - $children, - function ($props, $field) { - $props[$field['label']] = $field['schema']; - return $props; - }, - [] - ); - - return [ - 'type' => 'array', - 'items' => [ - 'type' => 'object', - 'properties' => $properties, - ], - 'additionalItems' => true, - ]; - default: - return ['type' => 'string']; - } - } - - /** - * Serializes the form's submission data. - * - * @param array $submission Submission data. - * @param array $form Form data. - * - * @return array - */ - public function serialize_submission($submission, $form_data) - { - $data = []; - $repeaters = array_filter($form_data['fields'], static function ( - $field - ) { - return $field['_type'] === 'repeater'; - }); - - $fields_in_repeaters = array_reduce( - $repeaters, - static function ($ids, $repeater) { - foreach ($repeater['children'] as $child) { - $ids[$child['id']] = []; - } - - return $ids; - }, - [] - ); - - foreach ($submission['fields'] as $field) { - if ($field['type'] === 'file-upload') { - continue; - } - - $i = array_search( - $field['name'], - array_column($form_data['fields'], 'name') - ); - $field_data = $form_data['fields'][$i]; - - $field['id'] = preg_replace('/_\d+$/', '', $field['id']); - if (isset($fields_in_repeaters[$field['id']])) { - $fields_in_repeaters[$field['id']][] = $field; - } else { - $value = $this->format_value($field, $field_data); - $data[$field_data['name']] = $value; - } - } - - foreach ($repeaters as $repeater) { - $repeater_data = []; - foreach ($repeater['children'] as $child) { - foreach ($fields_in_repeaters as $id => $fields) { - if ($id == $child['id']) { - break; - } - } - - for ($i = 0; $i < count($fields); $i++) { - $field = $fields[$i]; - $value = $this->format_value($field, $child); - $datum = - count($repeater_data) > $i ? $repeater_data[$i] : []; - $datum[$child['name']] = $value; - $repeater_data[$i] = $datum; - } - } - - $data[$repeater['name']] = $repeater_data; - } - - return $data; - } - - private function format_value($field, $field_data) - { - if (strstr($field['_type'], 'payment')) { - $field['value'] = html_entity_decode($field['value']); - } - - if ($field_data['_type'] === 'hidden') { - $number_val = (float) $field['value']; - if (strval($number_val) === $field['value']) { - return $number_val; - } - } - - if ( - $field_data['_type'] === 'number' || - $field_data['_type'] === 'number-slider' - ) { - if (isset($field['amount'])) { - $value = (float) $field['amount']; - if (isset($field['currency'])) { - $value .= ' ' . $field['currency']; - } - - return $value; - } else { - return (float) preg_replace('/[^0-9\.,]/', '', $field['value']); - } - } - - if ( - $field_data['_type'] === 'select' || - $field_data['_type'] === 'checkbox' - ) { - if ($field_data['is_multi']) { - return array_map(function ($value) { - return trim($value); - }, explode("\n", $field['value'])); - } - } - - if ($field_data['_type'] === 'address') { - $post_values = $_POST['wpforms']['fields'][$field['id']]; - $field_values = []; - foreach (array_keys($field_data['schema']['properties']) as $prop) { - $field_values[$prop] = $post_values[$prop] ?? ''; - } - - return $field_values; - } - - if ($field_data['_type'] === 'date-time') { - if ($field_data['schema']['type'] === 'object') { - $post_values = $_POST['wpforms']['fields'][$field['id']]; - return [ - 'date' => $post_values['date'], - 'time' => $post_values['time'], - ]; - } - } - - return (string) $field['value']; - } - - /** - * Gets submission uploaded files. - * - * @param object $submission Submission data. - * @param array $form_data Form data. - * - * @return array Uploaded files data. - */ - protected function submission_uploads($submission, $form_data) - { - $form_fields = wpforms_get_form_fields((int) $form_data['id'], [ - 'file-upload', - ]); - - if (empty($form_fields)) { - return []; - } - - $fields = []; - foreach ($form_fields as $form_field) { - foreach ($submission['fields'] as $submission_field) { - if ($submission_field['id'] == $form_field['id']) { - $fields[] = $submission_field; - } - } - } - - $uploads = []; - foreach ($fields as $field) { - if (empty($field['value_raw'])) { - continue; - } - - $is_multi = count($field['value_raw'] ?: []) > 1; - $paths = WPForms_Field_File_Upload::get_entry_field_file_paths( - $form_data['id'], - $field - ); - $uploads[$field['name']] = [ - 'path' => $is_multi ? $paths : $paths[0], - 'is_multi' => $is_multi, - ]; - } - - return $uploads; - } - - /** - * Formats the bridge's form data to be used as the post_content of a wpform post - * type and encode it as json. - * - * @param array $data Bridge's template form data. - * - * @return string Encoded and decorated form data. - */ - private function encode_form_data($data) - { - $wp_fields = []; - for ($i = 0; $i < count($data['fields']); $i++) { - $id = $i + 1; - $field = $data['fields'][$i]; - - $args = [$id, $field['name'], $field['required'] ?? false]; - switch ($field['type']) { - case 'textarea': - $wp_fields[strval($id)] = $this->textarea_field(...$args); - break; - case 'hidden': - if (isset($field['value'])) { - if (is_bool($field['value'])) { - $field['value'] = $field['value'] ? '1' : '0'; - } - - $args[] = (string) $field['value']; - $wp_fields[strval($id)] = $this->hidden_field(...$args); - } - - break; - case 'select': - $args[] = $field['options'] ?? []; - $args[] = $field['is_multi'] ?? false; - $wp_fields[strval($id)] = $this->select_field(...$args); - break; - case 'file': - $args[] = $field['filetypes'] ?? ''; - $wp_fields[strval($id)] = $this->file_field(...$args); - break; - case 'date': - $wp_fields[strval($id)] = $this->date_field(...$args); - break; - case 'text': - $wp_fields[strval($id)] = $this->text_field(...$args); - break; - case 'number': - $constraints = [ - 'default_value' => floatval($field['default'] ?? 0), - 'min' => $field['min'] ?? '', - 'max' => $field['max'] ?? '', - 'step' => $field['step'] ?? '1', - ]; - - $args[] = $constraints; - $wp_fields[strval($id)] = $this->number_field(...$args); - break; - // case 'url': - // case 'email': - // case 'number': - default: - $wp_fields[strval($id)] = $this->field_template( - $field['type'], - ...$args - ); - } - } - - return wpforms_encode([ - 'fields' => $wp_fields, - 'field_id' => $id + 1, - 'settings' => [ - 'form_desc' => '', - 'submit_text' => esc_html__('Submit', 'forms-bridge'), - 'submit_text_processing' => esc_html__( - 'Sending...', - 'forms-bridge' - ), - 'antispam_v3' => '1', - 'notification_enable' => '1', - 'notifications' => [ - '1' => [ - 'email' => '{admin_email}', - 'replyto' => '', - 'message' => '{all_fields}', - ], - ], - 'confirmations' => [ - '1' => [ - 'type' => 'message', - 'message' => esc_html__( - 'Thanks for contacting us! We will be in touch with you shortly.', - 'forms-bridge' - ), - 'message_scroll' => '1', - ], - ], - 'ajax_submit' => '1', - ], - 'meta' => ['template' => 'forms-bridge'], - ]); - } - - /** - * Returns a default field array data. Used as template for the field creation methods. - * - * @param string $type Field type. - * @param int $id Field id. - * @param string $label Field label. - * @param boolean $required Is field required. - * - * @return array - */ - private function field_template($type, $id, $label, $required) - { - return [ - 'id' => (string) $id, - 'type' => $type, - 'label' => esc_html($label), - 'required' => $required ? '1' : '0', - 'size' => 'medium', - 'description' => '', - 'placeholder' => '', - 'css' => '', - ]; - } - - /** - * Returns a valid text field data. - * - * @param int $id Field id. - * @param string $name Field name (label). - * @param boolean $required Is field required. - * - * @return array - */ - private function text_field($id, $name, $required) - { - return array_merge( - $this->field_template('text', $id, $name, $required), - [ - 'limit_count' => '1', - 'limit_mode' => 'characters', - ] - ); - } - - /** - * Returns a valid number field data. - * - * @param int $id Field id. - * @param string $name Field name (label). - * @param boolean $required Is field required. - * @param array $constraints Field constraints. - * - * @return array - */ - private function number_field($id, $name, $required, $constraints) - { - return array_merge( - $this->field_template('number-slider', $id, $name, $required), - $constraints - ); - } - - /** - * Returns a valid text field data. - * - * @param int $id Field id. - * @param string $name Field name (label). - * @param boolean $required Is field required. - * - * @return array - */ - private function date_field($id, $name, $required) - { - return array_merge( - $this->field_template('date-time', $id, $name, $required), - [ - 'format' => 'date', - 'date_type' => 'datepicker', - 'date_format' => 'd/m/Y', - ] - ); - } - - /** - * Returns a valid textarea field data. - * - * @param int $id Field id. - * @param string $name Field name (label). - * @param boolean $required Is field required. - * - * @return array - */ - private function textarea_field($id, $name, $required) - { - return array_merge( - $this->field_template('textarea', $id, $name, $required), - [ - 'limit_count' => '1', - 'limit_mode' => 'characters', - ] - ); - } - - /** - * Returns a valid multi select field data, as a select field if is single, as - * a checkbox field if is multiple. - * - * @param int $id Field id. - * @param string $name Field name (label). - * @param boolean $required Is field required. - * @param array Options data. - * @param boolean Is field multi value. - * - * @return array - */ - private function select_field($id, $name, $required, $options, $is_multi) - { - $choices = array_map(function ($opt) { - return [ - 'label' => esc_html($opt['label']), - 'value' => sanitize_text_field($opt['value']), - 'image' => '', - 'icon' => '', - 'icon_style' => 'regular', - ]; - }, $options); - - if ($is_multi) { - return array_merge( - $this->field_template('checkbox', $id, $name, $required), - [ - 'choices' => $choices, - 'choices_images_style' => 'modern', - 'choices_icon_color' => '#066aab', - 'choices_icon_size' => 'large', - 'choices_icon_style' => 'default', - 'choices_limit' => '', - 'dynamic_choices' => '', - ] - ); - } else { - return array_merge( - $this->field_template('select', $id, $name, $required), - [ - 'choices' => $choices, - 'dynamic_choices' => '', - 'style' => 'classic', - ] - ); - } - } - - /** - * Returns a valid hidden field data. - * - * @param int $id Field id. - * @param string $name Field name (label). - * @param boolean $required Is field required. - * @param string $value Field's default value. - * - * @return array - */ - private function hidden_field($id, $name, $required, $value) - { - $field = array_merge( - $this->field_template('hidden', $id, $name, $required), - [ - 'label_hide' => '1', - 'label_disable' => '1', - 'default_value' => $value, - ] - ); - - unset($field['description']); - unset($field['required']); - unset($field['placeholder']); - - return $field; - } - - /** - * Returns a valid file-upload field data. - * - * @param int $id Field id. - * @param string $name Field name (label). - * @param boolean $required Is field required. - * @param string $filetypes String with allowed file extensions separated by commas. - * - * @return array - */ - private function file_field($id, $name, $required, $filetypes) - { - return array_merge( - $this->field_template('file-upload', $id, $name, $required), - [ - 'max_size' => '', - 'max_file_number' => '1', - 'style' => 'modern', - 'extensions' => $filetypes, - ] - ); - } -} - -Integration::setup(); diff --git a/migrations/3.0.0.php b/migrations/3.0.0.php deleted file mode 100644 index 2e557826..00000000 --- a/migrations/3.0.0.php +++ /dev/null @@ -1,26 +0,0 @@ -bridge['workflow'] ?? []; - } - } - } - - $bridge_data['workflow'] = $bridge_data['workflow'] ?? []; - - if (!isset($bridge_data['mutations'])) { - $mappers = $bridge_data['mappers'] ?? []; - $mutations = [$mappers]; - } else { - $mutations = $bridge_data['mutations']; - } - - for ( - $i = count($mutations); - $i <= count($bridge_data['workflow']); - $i++ - ) { - $mutations[] = []; - } - - $bridge_data['mutations'] = $mutations; - unset($bridge_data['mappers']); - } - - update_option($option, $data); -} diff --git a/migrations/4.0.0.php b/migrations/4.0.0.php deleted file mode 100644 index 4aa37187..00000000 --- a/migrations/4.0.0.php +++ /dev/null @@ -1,248 +0,0 @@ - $credential) { - $backend = FBAPI::get_backend($name); - if (!$backend) { - continue; - } - - $headers = $backend->headers; - if (isset($headers['api_user'], $headers['token'])) { - $backend_data = [ - 'name' => $backend->name, - 'base_url' => $backend->base_url, - 'headers' => [], - ]; - - $credential_data = [ - 'name' => $credential ?: $backend->name, - 'schema' => 'Token', - 'client_id' => $headers['api_user'], - 'client_secret' => $headers['token'], - ]; - - unset($headers['api_user']); - unset($headers['token']); - - foreach ($headers as $name => $value) { - $backend_data['headers'][] = [ - 'name' => $name, - 'value' => $value, - ]; - } - - $backend_data['credential'] = $credential_data['name']; - FBAPI::save_backend($backend_data); - $credentials[] = $credential_data; - } - } - } elseif ($option === 'forms-bridge_mailchimp') { - foreach ($backends as $name => $credential) { - $backend = FBAPI::get_backend($name); - if (!$backend) { - continue; - } - - $headers = $backend->headers; - if (isset($headers['api-key'])) { - $backend_data = [ - 'name' => $backend->name, - 'base_url' => $backend->base_url, - 'headers' => [], - ]; - - $credential_data = [ - 'name' => $credential ?: $backend->name, - 'schema' => 'Basic', - 'client_id' => 'forms-bridge', - 'client_secret' => $headers['api-key'], - ]; - - unset($headers['api-key']); - - foreach ($headers as $name => $value) { - $backend_data['headers'][] = [ - 'name' => $name, - 'value' => $value, - ]; - } - - $backend_data['credential'] = $credential_data['name']; - FBAPI::save_backend($backend_data); - $credentials[] = $credential_data; - } - } - } elseif ($option === 'forms-bridge_financoop') { - foreach ($backends as $name => $credential) { - $backend = FBAPI::get_backend($name); - if (!$backend) { - continue; - } - - $headers = $backend->headers; - if ( - isset( - $headers['X-Odoo-Db'], - $headers['X-Odoo-Username'], - $headers['X-Odoo-Api-Key'] - ) - ) { - $backend_data = [ - 'name' => $backend->name, - 'base_url' => $backend->base_url, - 'headers' => [], - ]; - - $credential_data = [ - 'name' => $credential ?: $backend->name, - 'schema' => 'RPC', - 'client_id' => $headers['X-Odoo-Username'], - 'client_secret' => $headers['X-Odoo-Api-Key'], - 'database' => $headers['X-Odoo-Db'], - ]; - - unset($headers['X-Odoo-Db']); - unset($headers['X-Odoo-Username']); - unset($headers['X-Odoo-Api-Key']); - - foreach ($headers as $name => $value) { - $backend_data['headers'][] = [ - 'name' => $name, - 'value' => $value, - ]; - } - - $backend_data['credential'] = $credential_data['name']; - FBAPI::save_backend($backend_data); - $credentials[] = $credential_data; - } - } - } elseif ($option === 'forms-bridge_odoo') { - foreach ($data['credentials'] ?? [] as $credential) { - $credentials[] = [ - 'name' => $credential['name'], - 'schema' => 'RPC', - 'client_id' => $credential['user'], - 'client_secret' => $credential['password'], - 'database' => $credential['database'], - ]; - } - - unset($data['credentials']); - - foreach ($backends as $name => $credential) { - $backend = FBAPI::get_backend($name); - if (!$backend) { - continue; - } - - $backend_data = $backend->data(); - $backend_data['credential'] = $credential; - FBAPI::save_backend($backend_data); - } - } elseif ($option === 'forms-bridge_zoho') { - unset($data['credentials']); - } elseif ($option === 'forms-bridge_bigin') { - unset($data['credentials']); - } elseif ($option === 'forms-bridge_gsheets') { - unset($data['credentials']); - } - - update_option($option, $data); -} - -$rest = get_option('forms-bridge_rest-api'); -add_option('forms-bridge_rest', $rest); -delete_option('forms-bridge_rest-api'); - -$registry = get_option('forms_bridge_addons'); -$registry['rest'] = $registry['rest-api']; -unset($registry['rest-api']); -update_option('forms_bridge_addons', $registry); - -$http = get_option('http-bridge_general'); -$http['credentials'] = $credentials; -update_option('http-bridge_general', $http); diff --git a/package-lock.json b/package-lock.json index 6e264702..eb51c3f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "forms-bridge-admin-client", - "version": "4.0.6", + "name": "forms-bridge", + "version": "4.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "forms-bridge-admin-client", - "version": "4.0.6", + "name": "forms-bridge", + "version": "4.0.5", "license": "GPL-2.0-or-later", "dependencies": { "@codemirror/lang-php": "^6.0.2", @@ -18,10 +18,10 @@ }, "devDependencies": { "@eslint/js": "^9.19.0", - "@prettier/plugin-php": "^0.22.4", "esbuild": "^0.24.2", "eslint": "^9.19.0", - "eslint-config-prettier": "^10.0.1", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-react": "^7.37.5", "globals": "^15.14.0", "husky": "^9.1.7", "lint-staged": "^15.4.2", @@ -886,20 +886,6 @@ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "license": "MIT" }, - "node_modules/@prettier/plugin-php": { - "version": "0.22.4", - "resolved": "https://registry.npmjs.org/@prettier/plugin-php/-/plugin-php-0.22.4.tgz", - "integrity": "sha512-uZWqfyrwsxScIYkmVcfnoQGFmKVMXTHD5pqYT4l8fxzm5P3XY94hTPbf8X6TFCi2QTZBIot7GS8lfIjQjldc2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "linguist-languages": "^7.27.0", - "php-parser": "^3.1.5" - }, - "peerDependencies": { - "prettier": "^3.0.0" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1006,6 +992,170 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1037,6 +1187,56 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1177,6 +1377,60 @@ "node": ">= 8" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -1202,6 +1456,70 @@ "dev": true, "license": "MIT" }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -1222,6 +1540,183 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/esbuild": { "version": "0.24.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", @@ -1338,9 +1833,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -1353,6 +1848,39 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -1563,10 +2091,77 @@ "dev": true, "license": "ISC" }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, "license": "MIT", "engines": { @@ -1576,6 +2171,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -1589,6 +2223,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1615,6 +2267,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1625,6 +2320,77 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -1688,6 +2454,156 @@ "node": ">=0.8.19" } }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1698,6 +2614,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", @@ -1711,6 +2643,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1724,6 +2676,32 @@ "node": ">=0.10.0" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1734,6 +2712,71 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", @@ -1747,6 +2790,110 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1754,6 +2901,31 @@ "dev": true, "license": "ISC" }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1788,6 +2960,22 @@ "dev": true, "license": "MIT" }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1825,13 +3013,6 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/linguist-languages": { - "version": "7.31.2", - "resolved": "https://registry.npmjs.org/linguist-languages/-/linguist-languages-7.31.2.tgz", - "integrity": "sha512-UQv5iuzEcGoYWdc/Ea2yqLiFbe8rQmIa5uD1FLwF5vBYmwbTvJlPJfHaIVtfYZTQf2wg7pzhxkwkGPP3q+v+EA==", - "dev": true, - "license": "MIT" - }, "node_modules/lint-staged": { "version": "15.5.2", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", @@ -1980,6 +3161,29 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2083,6 +3287,114 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -2117,6 +3429,24 @@ "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2182,12 +3512,12 @@ "node": ">=8" } }, - "node_modules/php-parser": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/php-parser/-/php-parser-3.2.5.tgz", - "integrity": "sha512-M1ZYlALFFnESbSdmRtTQrBFUHSriHgPhgqtTF/LCbZM4h7swR5PHtUceB2Kzby5CfqcsYwBn7OXTJ0+8Sajwkw==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT" }, "node_modules/picomatch": { "version": "2.3.1", @@ -2215,6 +3545,16 @@ "node": ">=0.10" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2222,33 +3562,114 @@ "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-from": { @@ -2301,6 +3722,120 @@ "dev": true, "license": "MIT" }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2324,6 +3859,82 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -2367,6 +3978,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -2395,6 +4020,104 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -2456,6 +4179,19 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2482,6 +4218,103 @@ "node": ">= 0.8.0" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2514,6 +4347,95 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index fa67e5d2..b6b1b45d 100755 --- a/package.json +++ b/package.json @@ -1,21 +1,22 @@ { - "name": "forms-bridge-admin-client", - "version": "4.0.6", - "main": "Gruntfile.js", + "name": "forms-bridge", + "version": "4.0.5", "author": "Còdec", "license": "GPL-2.0-or-later", "scripts": { "start": "npm run dev", "dev": "node esbuild/dev.cjs", "build": "node esbuild/build.cjs", - "prepare": "husky" + "prepare": "husky", + "test": "echo 'no tests'", + "lint": "eslint" }, "devDependencies": { "@eslint/js": "^9.19.0", - "@prettier/plugin-php": "^0.22.4", "esbuild": "^0.24.2", "eslint": "^9.19.0", - "eslint-config-prettier": "^10.0.1", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-react": "^7.37.5", "globals": "^15.14.0", "husky": "^9.1.7", "lint-staged": "^15.4.2", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 16a39027..9db0540c 100755 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,16 +1,19 @@ +> - + ./tests/ - ./tests/test-sample.php + ./forms-bridge/deps/http/tests/ + ./forms-bridge/deps/plugin/tests/ diff --git a/post_types/bridge-template.php b/post_types/bridge-template.php deleted file mode 100644 index c98771be..00000000 --- a/post_types/bridge-template.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'name' => __('Bridge templates', 'forms-bridge'), - 'singular_name' => __('Bridge template', 'forms-bridge'), - ], - 'public' => false, - 'supports' => ['title', 'excerpt', 'custom-fields'], - 'capability_type' => 'post', - 'can_export' => false, - ]); -}); diff --git a/post_types/job.php b/post_types/job.php deleted file mode 100644 index 31059eb7..00000000 --- a/post_types/job.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'name' => __('Jobs', 'forms-bridge'), - 'singular_name' => __('Job', 'forms-bridge'), - ], - 'public' => false, - 'supports' => ['title', 'excerpt', 'custom-fields'], - 'capability_type' => 'post', - 'can_export' => false, - ]); -}); diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 86c8e26c..00000000 --- a/readme.txt +++ /dev/null @@ -1,220 +0,0 @@ -=== Forms Bridge - Infinite integrations === -Contributors: codeccoop -Tags: odoo, dolibarr, listmonk, forms, woocommerce -Donate link: https://buymeacoffee.com/codeccoop -License: GPLv2 or later -License URI: http://www.gnu.org/licenses/gpl-2.0.html -Stable Tag: 4.0.6 -Tested up to: 6.8 - -Bridge your WordPress forms without code, add custom fields, use field mappers, set up a workflow and make your data flow seamlessly to your backend. - -== Bridges == - -Think of a bridge as a pipeline through which your form submissions data flows to your backend or service. In the middle, you can add custom fields to the form submissions, use field mappers to rename and mutate your form responses, or use workflow jobs to process the data before it is sent over the wire. With bridges you can connect your WordPress forms to any kind of backend, it doesn't matter if it is a CRM, an ERP, a booking system or an email marketing platform, the only requirement is an HTTP API. If it has an API it can be bridged! - -== Form builders == - -Form builders are well known plugins that add forms to WordPress. We do bridges, let them do the forms and then work together to make your business work with ease. - -Forms Bridge supports the following form builders: - -* [Contact Form 7](https://wordpress.org/plugins/contact-form-7/) -* [GravityForms](https://www.gravityforms.com) -* [WP Forms (PRO)](https://wpforms.com/) -* [Ninja Forms](https://wordpress.org/plugins/ninja-forms/) -* [WooCommerce](https://wordpress.org/plugins/woocommerce) - -== Addons == - -Forms Bridge comes with free addons. Each addon adds to the plugin new bridges to work with specific APIs, new workflow jobs and bridge templates. - -Forms Bridge has the following addons: - -* [REST API](https://en.wikipedia.org/wiki/REST) -* [Bigin](https://www.bigin.com/developer/docs/apis/v2/?source=developer) -* [Brevo](https://developers.brevo.com/) -* [Dolibarr](https://wiki.dolibarr.org/index.php/Module_Web_Services_API_REST_(developer)) -* [FinanCoop](https://somit.coop/financoop/) -* [Google Sheets](https://workspace.google.com/products/sheets/) -* [Holded](https://developers.holded.com/reference/api-key) -* [Listmonk](https://listmonk.app/docs/apis/apis/) -* [Nextcloud](https://docs.nextcloud.com/server/20/user_manual/en/files/access_webdav.html) -* [Mailchimp](https://mailchimp.com/developer/) -* [Odoo](https://www.odoo.com/) -* [Zoho CRM](https://www.zoho.com/developer/rest-api.html) - -== Backends == - -In Forms Bridge, a backend is a set of configurations that handles the information required to get your form submissions bridged over HTTP requests to remote systems. - -To register a new backend you only have to set 3 fields: - -1. A unique name for the new connection -2. The URL of your backend -3. An array of HTTP headers with connection metadata and credentials -4. Optional, an HTTP authentication credential (Basic, Bearer, etc) - -Once registered, you can reuse your backend connection on your form bridges. - -== Custom fields == - -Custom fields are data that will be added the bridge payload. Use them to store private data you don’t want to place on your public forms, like user emails, or config values, like product IDs or lead tags. - -== Field Mappers == - -Field mappers are mutations with which you can rename your form submission fields and transform its values. Use them to make your form submissions to fit your backend API endpoint interface. - -== Workflows == - -Make your form submissions flow through a chain of jobs that pre-process the data before it was sent over the wire. Think of workflow as a system to set up automations to run on each form submission. - -== Templates == - -To streamline the bridge setup process, Forms Bridge comes packed with templates. Templates are blueprints of bridges you can use to set up your form integrations in a matter of minutes. - -== Docs == - -Browse the plugin's documentation on [formsbridge.codeccoop.org](https://formsbridge.codeccoop.org/documentation/) - -== Links == - -* [Official website](https://formsbridge.codeccoop.org/) -* [Gitlab](https://gitlab.com/codeccoop/wp/plugins/forms-bridge/) -* [Còdec](https://www.codeccoop.org) -* [Other plugins](https://profiles.wordpress.org/codeccoop/#content-plugins) - -== Screenshots == - -1. Settings page -2. Backends -3. Bridges -4. Custom fields -5. Field mappers -6. Workflows -7. Job editor -8. Template wizard -9. Debug console - -== Changelog == - -= 4.0.6 = -* fix: field serialization based on field type -* feat: add accpet-language http header on odoo's rpc api calls -* feat: new odoo job position and helpdesk ticket templates -* feat: new odoo workflow jobs -* feat: include textarea to the standard field types -= 4.0.5 = -* fix: prepare mappers loop introduced in 4.0.4 -= 4.0.4 = -* feat: add remuneration_type form field on financoop subscription request template -* fix: minnor frontend fixes and improvements -* fix: financoop shortcode, templates and jobs -* fix: template config loading race conditions - -= 4.0.3 = -* fix: load text domain warnings - -= 4.0.2 = -* feat: mailchimp api url selector - -= 4.0.1 = -* feat: rename form integrations to form builders -* feat: oauth grant as GET requests -* feat: dolibarr, odoo and holded sync products templates -* feat: brevo woo orders template -* fix: migrations enqueuement loop - -= 4.0.0 = -* feat: Workflow jobs editor -* feat: Nextcloud addon -* fix: Edge case of mutations and fingers -* feat: HTTP authentication -* feat: Admin UI refactor -* feat: Wipe config button -* feat: Settings API refactor -* feat: Zoho and Google Oauth web based credentials -* feat: drop support for Google service credentials -* feat: drop support for Zoho Self Client credentials - -= 3.5.4 = -* fix: use conditional mappers on stringify attachments -* fix: cast value type for join mutations - -= 3.5.3 = -* feat: nename gsheet default backend -* fix: bridge request filter callback removal - -= 3.5.2 = -* feat: new google sheets woocomerce orders template -* feat: disable default payload prune for gsheet bridges -* feat: update gsheet composer dependencies -* fix: remove php warnings on zoho and listmonk addons - -= 3.5.1 = -* feat: improve dolibarr next code and product search api calls -* feat: add is_bridged woocommerce order meta data -* feat: new validate order job and template for the dolibarr addon -* feat: new delivered order template for the odoo addon -* feat: changes on the holded woocommerce template - -= 3.5.0 = -* fix: woocommerce payload schema -* feat: woocommerce bridge templates support -* feat: woocommerce templates for odoo, dolibarr, holded, bigin, brevo, mailchimp and zoho -* feat: jon finger expansions -* feat: conditional json finger pointers -* feat: improvements on the workflows panel UI -* feat: backend and bridges json exports - -= 3.4.3 = -* feat: bridge template descriptions -* feat: listmonk skip subscription job - -= 3.4.2 = -* fix: holded appointments template jobs -* fix: typos from odoo workflow job descriptions -* feat: add new chapters to the plugin's readme -* feat: settings sanitization with defaults recovery - -= 3.4.1 = -* feat: holded quotation templates -* feat: holded API introspection based on swagger data -* fix: bridge api schema invalidation -* feat: api fields button with disabled state - -= 3.4.0 = -* feat: odoo quotation templates -* feat: dolibarr quotation templates -* feat: country id odoo workflow job -* feat: gmt date tags -* feat: addons data autoload -* feat: odoo state id job -* feat: skip email list subscription jobs -* fix: firefox backend state updates on firefox - -= 3.3.5 = -* feat: support for ninja file fields and conditionals - -= 3.3.4 = -* fix: does not skip empty array submissions on submission filter -* feat: remove gf private uploads module -* fix: scroll to bottom on mutations/custom fields tables - -= 3.3.3 = -* feat: remove minLength constraint from bridge schema -* feat: set null value on mappers with nowhere jsonfinger pointers - -= 3.3.2 = -* feat: update plugin urls and readme -* feat: remote assets from gitlab -* fix: mailchimp template wizard -* feat: update credits, donation link and screenshots - -= 3.3.1 = -* fix: odoo api function bridge patches -* feat: plugin screenshots -* feat: update readme and plugin official url - -= 3.3.0 = -* feat: introspection api diff --git a/src/components/Backends/index.jsx b/src/components/Backends/index.jsx index 6c860951..08ae524a 100644 --- a/src/components/Backends/index.jsx +++ b/src/components/Backends/index.jsx @@ -6,7 +6,7 @@ import TabTitle from "../TabTitle"; import AddIcon from "../icons/Add"; const { useRef, useEffect } = wp.element; -const { PanelBody, TabPanel, __experimentalSpacer: Spacer } = wp.components; +const { TabPanel } = wp.components; const { __ } = wp.i18n; const CSS = `.backends-tabs-panel .components-tab-panel__tabs{overflow-x:auto;} diff --git a/src/components/Credential/Fields.jsx b/src/components/Credential/Fields.jsx index 23973ec2..557e78e1 100644 --- a/src/components/Credential/Fields.jsx +++ b/src/components/Credential/Fields.jsx @@ -103,7 +103,7 @@ export default function CredentialFields({ return fields .filter((field) => !field.value) - .sort((a, b) => (a.name === "name" ? -1 : 0)) + .sort((a) => (a.name === "name" ? -1 : 0)) .map((field) => { switch (field.type) { case "string": diff --git a/src/components/JobEditor/CodeEditor/index.jsx b/src/components/JobEditor/CodeEditor/index.jsx index 1c59b03c..ad8f6788 100644 --- a/src/components/JobEditor/CodeEditor/index.jsx +++ b/src/components/JobEditor/CodeEditor/index.jsx @@ -1,9 +1,6 @@ // vendor import { EditorView, basicSetup } from "codemirror"; -import { EditorState, Prec } from "@codemirror/state"; -import { keymap } from "@codemirror/view"; -import { syntaxTree } from "@codemirror/language"; -import { linter } from "@codemirror/lint"; +import { EditorState } from "@codemirror/state"; import { php } from "@codemirror/lang-php"; const { useEffect, useRef } = wp.element; diff --git a/src/components/JobEditor/Interface.jsx b/src/components/JobEditor/Interface.jsx index d204410a..a7c905f4 100644 --- a/src/components/JobEditor/Interface.jsx +++ b/src/components/JobEditor/Interface.jsx @@ -214,7 +214,7 @@ function InterfaceFieldEditor({ field, update, remove, add, from }) { { - schema = mutateSchema(type, field.schema); + const schema = mutateSchema(type, field.schema); update({ ...field, schema }); }} options={TYPE_OPTIONS} diff --git a/src/components/JobEditor/lib.js b/src/components/JobEditor/lib.js index e0a8f47d..19a72248 100644 --- a/src/components/JobEditor/lib.js +++ b/src/components/JobEditor/lib.js @@ -1,4 +1,4 @@ -const { __ } = wp.i18n; +const { __ } = window.wp.i18n; function pruneEmptySchemas(schema) { if (schema.type === "object") { diff --git a/src/components/Jobs/Snippet.jsx b/src/components/Jobs/Snippet.jsx index 22756575..41bcdd1f 100644 --- a/src/components/Jobs/Snippet.jsx +++ b/src/components/Jobs/Snippet.jsx @@ -1,6 +1,8 @@ const { useMemo } = wp.element; export default function JobSnippet({ id, snippet }) { + /* global hljs */ + const highlighted = useMemo(() => { const code = `function forms_bridge_job_${id.replace(/-/g, "_")}($payload, $bridge) { diff --git a/src/components/Mutations/Layers.jsx b/src/components/Mutations/Layers.jsx index f8a65278..ba378612 100644 --- a/src/components/Mutations/Layers.jsx +++ b/src/components/Mutations/Layers.jsx @@ -129,7 +129,7 @@ const INVALID_TO_STYLE = { "var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9))", }; -function useInputStyle(to = "", from = "") { +function useInputStyle(to = "") { const inputStyle = { height: "40px", paddingLeft: "12px", diff --git a/src/components/Mutations/lib.js b/src/components/Mutations/lib.js index 373ecd88..f9109cb2 100644 --- a/src/components/Mutations/lib.js +++ b/src/components/Mutations/lib.js @@ -6,7 +6,7 @@ import { } from "../../lib/payload"; export function schemaToOptions(schema, name = "") { - const isFlattable = name.match(/\[\](?=[^\[])/g)?.length >= 2; + const isFlattable = name.match(/\[\](?=[^[])/g)?.length >= 2; // const isExpansible = /\[\](?=.+)/g.test(name); if (name !== "") { diff --git a/src/components/Templates/Field.jsx b/src/components/Templates/Field.jsx index 237b91ca..32fffd01 100644 --- a/src/components/Templates/Field.jsx +++ b/src/components/Templates/Field.jsx @@ -178,8 +178,10 @@ function SelectField({ multiple={!!multiple} {...constraints} > - {options.map(({ label, value }) => ( - + {options.map(({ label, value }, i) => ( + ))} @@ -189,7 +191,7 @@ function SelectField({ export default function TemplateField({ data, error }) { const isRequired = !!data.required; return ( -