Merge remote-tracking branch 'upstream/trunk'

This commit is contained in:
David Marín 2022-04-22 05:56:34 +02:00
commit 2a74c4d82f
4614 changed files with 585555 additions and 304660 deletions

View File

@ -1,28 +0,0 @@
.*
.*/
*.lock
*.md
*.zip
/bin/
/build/
/node_modules/
/tests/
babel.config.js
changelog.txt
composer.*
tsconfig.*
contributors.html
docker-compose.yaml
Dockerfile
Gruntfile.js
lerna.json
none
package-lock.json
package.json
packages/woocommerce-admin/docs
phpcs.xml
phpunit.xml
phpunit.xml.dist
README.md
renovate.json
webpack.config.js

View File

@ -22,3 +22,6 @@ trim_trailing_whitespace = false
trim_trailing_whitespace = false
indent_style = space
indent_size = 2
[*.json]
indent_style = tab

View File

@ -1,21 +1 @@
*.min.js
/assets/js/accounting/**
/assets/js/flexslider/**
/assets/js/jquery-blockui/**
/assets/js/jquery-cookie/**
/assets/js/jquery-flot/**
/assets/js/jquery-payment/**
/assets/js/jquery-qrcode/**
/assets/js/jquery-serializejson/**
/assets/js/jquery-tiptip/**
/assets/js/jquery-ui-touch-punch/**
/assets/js/js-cookie/**
/assets/js/photoswipe/**
/assets/js/prettyPhoto/**
/assets/js/round/**
/assets/js/select2/**
/assets/js/selectWoo/**
/assets/js/stupidtable/**
/assets/js/zeroclipboard/**
/assets/js/zoom/**
node_modules

View File

@ -1,33 +1,4 @@
/** @format */
const { useE2EEsLintConfig } = require( './tests/e2e/env/config/use-config' );
module.exports = useE2EEsLintConfig( {
module.exports = {
root: true,
env: {
browser: true,
es6: true,
node: true
},
globals: {
wp: true,
wpApiSettings: true,
wcSettings: true,
es6: true
},
rules: {
camelcase: 0,
indent: 0,
'max-len': [ 2, { 'code': 140 } ],
'no-console': 1
},
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 8,
ecmaFeatures: {
modules: true,
experimentalObjectRestSpread: true,
jsx: true
}
},
} );
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
};

View File

@ -13,7 +13,7 @@ There are many ways to contribute to the project!
If you wish to contribute code, please read the information in the sections below. Then [fork](https://help.github.com/articles/fork-a-repo/) WooCommerce, commit your changes, and [submit a pull request](https://help.github.com/articles/using-pull-requests/) 🎉
We use the `good first issue` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
We use the `good first issue` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+good+first+issue%22).
WooCommerce is licensed under the GPLv3+, and all contributions to the project will be released under the same license. You maintain copyright over any contribution you make, and by submitting a pull request, you are agreeing to release that contribution under the GPLv3+ license.
@ -26,8 +26,8 @@ If you have questions about the process to contribute code or want to discuss de
- [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS)
- [Naming conventions](https://github.com/woocommerce/woocommerce/wiki/Naming-conventions)
- [String localisation guidelines](https://github.com/woocommerce/woocommerce/wiki/String-localisation-guidelines)
- [Running unit tests](https://github.com/woocommerce/woocommerce/blob/master/tests/README.md)
- [Running e2e tests](https://github.com/woocommerce/woocommerce/wiki/End-to-end-Testing)
- [Running unit tests](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/tests/README.md)
- [Running e2e tests](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/tests/e2e/README.md)
## Coding Guidelines and Development 🛠
@ -37,19 +37,18 @@ If you have questions about the process to contribute code or want to discuss de
- Ensure you use LF line endings in your code editor. Use [EditorConfig](http://editorconfig.org/) if your editor supports it so that indentation, line endings and other settings are auto configured.
- When committing, reference your issue number (#1234) and include a note about the fix.
- Ensure that your code supports the minimum supported versions of PHP and WordPress; this is shown at the top of the `readme.txt` file.
- Push the changes to your fork and submit a pull request on the master branch of the WooCommerce repository.
- Push the changes to your fork and submit a pull request on the trunk branch of the WooCommerce repository.
- Make sure to write good and detailed commit messages (see [this post](https://chris.beams.io/posts/git-commit/) for more on this) and follow all the applicable sections of the pull request template.
- Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team.
If you are contributing code to the (Javascript-driven) WooCommerce Admin project or to Gutenberg blocks, note that these are developed in external packages.
If you are contributing code to the (Javascript-driven) Gutenberg blocks, note that it's developed in an external package.
- [WooCommerce Admin](https://github.com/woocommerce/woocommerce-admin)
- [Blocks](https://github.com/woocommerce/woocommerce-gutenberg-products-block)
## Feature Requests 🚀
Feature requests can be [submitted to our issue tracker](https://github.com/woocommerce/woocommerce/issues/new?template=6-Feature-request.md). Be sure to include a description of the expected behavior and use case, and before submitting a request, please search for similar ones in the closed issues.
Feature requests can be [submitted to our issue tracker](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+). Be sure to include a description of the expected behavior and use case, and before submitting a request, please search for similar ones in the closed issues.
Feature request issues will remain closed until we see sufficient interest via comments and [👍 reactions](https://help.github.com/articles/about-discussions-in-issues-and-pull-requests/) from the community.
You can see a [list of current feature requests which require votes here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aclosed+label%3A%22type%3A+enhancement%22+label%3A%22votes+needed%22+sort%3Areactions-%2B1-desc).
You can see a [list of current feature requests which require votes here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3A%22needs%3A+votes%22+).

View File

@ -1,12 +0,0 @@
---
name: "\U0001F512 Security issue"
about: Please report security issues *only* via https://www.hackerone.com
title: ''
labels: ''
assignees: ''
---
For security reasons, please report all security issues via https://hackerone.com/automattic/. Also, if the issue is valid, a bug bounty will be paid out to you.
Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released).

95
.github/ISSUE_TEMPLATE/1-bug-report.yml vendored Normal file
View File

@ -0,0 +1,95 @@
name: 🐞 Bug Report
description: Report a bug if something isn't working as expected in WooCommerce Core.
body:
- type: markdown
attributes:
value: |
### Thanks for contributing!
Please provide us with the information requested in this bug report.
Without these details, we won't be able to fully evaluate this issue.
Bug reports lacking detail, or for any other reason than to report a bug, may be closed without action.
While our goal is to address all the issues reported in this repository, GitHub should be treated as a place to report confirmed bugs only.
- If you have a support request or custom code related question please follow one of the steps below:
- Review [WooCommerce Self-Service Guide](https://woocommerce.com/document/woocommerce-self-service-guide/) to see if the solutions listed there apply to your case;
- If you are a paying customer of WooCommerce, contact WooCommerce support by [opening a ticket or starting a live chat](https://woocommerce.com/contact-us/);
- Make a post on [WooCommerce community forum](https://wordpress.org/support/plugin/woocommerce/)
- To get help on custom code questions go to the [WooCommerce Community Slack](https://woocommerce.com/community-slack/) and visit the `#developers` channel.
Make sure to look through the [existing `type: bug` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+bug%22) to see whether your bug has already been submitted.
Feel free to contribute to any existing issues.
Search tip: You can filter our issues using [our labels](https://github.com/woocommerce/woocommerce/labels).
Search tip: Make use of [GitHub's search syntax to refine your search](https://help.github.com/en/github/searching-for-information-on-github/searching-issues-and-pull-requests).
- type: checkboxes
id: prerequisites
attributes:
label: Prerequisites
description: Please confirm these before submitting the issue.
options:
- label: I have carried out troubleshooting steps and I believe I have found a bug.
- label: I have searched for similar bugs in both open and closed issues and cannot find a duplicate.
validations:
required: true
- type: textarea
id: summary
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
placeholder: |
A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual behavior
placeholder: |
A clear and concise description of what actually happens. Please be as descriptive as possible;
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
description: Attach screenshot(s) or recording(s) directly by dragging & dropping.
placeholder: |
1. Go to
2. Click on
3. Scroll down to
4. See error
validations:
required: true
- type: textarea
id: environment
attributes:
label: WordPress Environment
description: |
We use the [WooCommerce System Status Report](https://woocommerce.com/document/understanding-the-woocommerce-system-status-report/) to help us evaluate the issue.
Without this report we won't be able to fully evaluate this issue.
placeholder: |
The System Status Report is found in your WordPress admin under **WooCommerce > Status**.
Please select “Get system report”, then “Copy for support”, and then paste it here.
validations:
required: true
- type: checkboxes
id: isolating
attributes:
label: Isolating the problem
description: |
Please try testing your site for theme and plugins conflict.
To do that deactivate all plugins except for WooCommerce and switch to a default WordPress theme or [Storefront](https://en-gb.wordpress.org/themes/storefront/). Then test again.
If the issue is resolved with the default theme and all plugins deactivated, it means that one of your plugins or a theme is causing the issue.
You will then need to enable it one by one and test every time you do that in order to figure out which plugin is causing the issue.
options:
- label: I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.
- label: This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).
- label: I can reproduce this bug consistently using the steps above.
validations:
required: true

View File

@ -1,20 +0,0 @@
---
name: "\U0001F47D External issues"
about: Please report WooCommerce Admin, WooCommerce Gutenberg Products Blocks or Action Scheduler issues directly to their respective repositories.
title: ''
labels: ''
assignees: ''
---
Please report issues for the following features directly to their respective repositories.
WooCommerce Admin: https://github.com/woocommerce/woocommerce-admin
WooCommerce Gutenberg Products Blocks: https://github.com/woocommerce/woocommerce-gutenberg-products-block
Action Scheduler: https://github.com/woocommerce/action-scheduler
WooCommerce REST API Docs: https://github.com/woocommerce/woocommerce-rest-api-docs
WooCommerce Code Reference: https://github.com/woocommerce/code-reference

View File

@ -0,0 +1,33 @@
name: ✨ Enhancement Request
description: If you have an idea to improve an existing feature in core or need something for development (such as a new hook) please let us know or submit a Pull Request!
title: "[Enhancement]: "
labels: ["type: enhancement", "status: awaiting triage"]
body:
- type: markdown
attributes:
value: |
### Thanks for contributing!
Please provide us with the information requested in this form.
Make sure to look through [existing `type: enhancement` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+enhancement%22) and [existing `votes needed` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3A%22needs%3A+votes%22+) to see whether your idea is already being discussed.
Feel free to contribute to any existing issues.
Search tip: You can filter our issues using [our enhancement label](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+label%3A%22type%3A+enhancement%22+).
Search tip: Make use of [GitHub's search syntax to refine your search](https://help.github.com/en/github/searching-for-information-on-github/searching-issues-and-pull-requests).
- type: textarea
id: summary
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternative
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.

View File

@ -1,28 +0,0 @@
---
name: "❓ Support Question"
about: "If you have a question \U0001F4AC please see our docs or use our forums, helpdesk,
or Slack Community!"
title: ''
labels: ''
assignees: ''
---
We don't offer technical support on GitHub so we recommend using the following:
**Reading our documentation**
Usage docs can be found here: https://docs.woocommerce.com/
If you have a problem, you may want to start with the self help guide here: https://docs.woocommerce.com/document/woocommerce-self-service-guide/
**Technical support for premium extensions or if you're a WooCommerce.com customer**
Contact WooCommerce support by opening a ticket.
https://woocommerce.com/contact-us/
**For help with custom code**
WooCommerce Slack Community: https://woocommerce.com/community-slack/ in the `#developers` channel.
**General usage and development questions**
- WooCommerce Slack Community: https://woocommerce.com/community-slack/
- WordPress.org Forums: https://wordpress.org/support/plugin/woocommerce
- The Official WooCommerce Facebook Group https://www.facebook.com/groups/advanced.woocommerce/

View File

@ -1,58 +0,0 @@
---
name: "\U0001F41E Bug report"
about: Report a bug if something isn't working as expected in the core WooCommerce
plugin.
title: ''
labels: ''
assignees: ''
---
Please provide us with the information requested in this bug report. Without these details, we won't be able to fully evaluate this issue.
Bug reports lacking detail, or for any other reason than to report a bug, may be closed without action.
<!-- This template is for confirmed bugs only. If you have a support request or custom code related question please see our docs or use our forums, helpdesk, or Slack Community! https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=&template=3-Support.md&title= -->
<!-- Make sure to look through the existing issues to see whether your bug has already been submitted. Feel free to contribute to any existing issues. -->
<!-- Search tip: You can filter our issues using our component labels https://github.com/woocommerce/woocommerce/labels?q=component -->
<!-- Search tip: Make use of GitHub's search syntax to refine your search https://help.github.com/en/github/searching-for-information-on-github/searching-issues-and-pull-requests -->
**Prerequisites (mark completed items with an [x]):**
- [ ] I have have carried out troubleshooting steps and I believe I have found a bug.
- [ ] I have searched for similar bugs in both open and closed issues and cannot find a duplicate.
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Actual behavior**
A clear and concise description of what actually happens. Please be as descriptive as possible;
**Steps to reproduce the bug (We need to be able to reproduce the bug in order to fix it.)**
Steps to reproduce the bug:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
<!-- Please try testing your site for theme and plugins conflict. To do that deactivate all plugins except for WooCommerce and switch to a default WordPress theme or [Storefront](https://en-gb.wordpress.org/themes/storefront/). Then test again. If the issue is resolved with the default theme and all plugins deactivated, it means that one of your plugins or a theme is causing the issue. You will then need to enable it one by one and test every time you do that in order to figure out which plugin is causing the issue. -->
**Isolating the problem (mark completed items with an [x]):**
- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).
- [ ] I can reproduce this bug consistently using the steps above.
**WordPress Environment**
We use the [WooCommerce System Status Report](https://docs.woocommerce.com/document/understanding-the-woocommerce-system-status-report/) to help us evaluate the issue.
Without this report we won't be able to fully evaluate this issue.
<details>
```
The System Status Report is found in your WordPress admin under **WooCommerce > Status**.
Please select “Get system report”, then “Copy for support”, and then paste it here.
```
</details>

View File

@ -1,25 +0,0 @@
---
name: "✨ New Enhancement"
about: If you have an idea to improve an existing feature in core or need something
for development (such as a new hook) please let us know or submit a Pull Request!
title: ''
labels: ''
assignees: ''
---
<!-- Make sure to look through existing issues to see whether your idea is already being discussed. Feel free to contribute to any existing issues. -->
<!-- Search tip: You can filter issues using our enhancement label https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+label%3Aenhancement -->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,25 +0,0 @@
---
name: "\U0001F680 Feature request"
about: "Suggest a new feature \U0001F389 We'll consider building it if it receives
sufficient interest! \U0001F44D"
title: ''
labels: ''
assignees: ''
---
<!-- Make sure to look through existing issues to see whether your idea is already being discussed. Feel free to contribute to any existing issues. -->
<!-- Search tip: You can filter issues using our enhancement label https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+label%3Aenhancement -->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,11 @@
blank_issues_enabled: true
contact_links:
- name: 🔒 Security issue
url: https://hackerone.com/automattic/
about: For security reasons, please report all security issues via HackerOne. If the issue is valid, a bug bounty will be paid out to you. Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released).
- name: ❓ Support Question
url: https://woocommerce.com/document/woocommerce-self-service-guide/
about: If you have a question please see our docs or use our forums, helpdesk, or Slack community!
- name: WooCommerce Blocks
url: https://github.com/woocommerce/woocommerce-gutenberg-products-block
about: Please report issues for WooCommerce Blocks directly to it's repository.

View File

@ -1,6 +1,6 @@
### All Submissions:
* [ ] Have you followed the [WooCommerce Contributing guideline](https://github.com/woocommerce/woocommerce/blob/master/.github/CONTRIBUTING.md)?
* [ ] Have you followed the [WooCommerce Contributing guideline](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md)?
* [ ] Does your code follow the [WordPress' coding standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/)?
* [ ] Have you checked to ensure there aren't other open [Pull Requests](../../pulls) for the same update/change?
@ -25,9 +25,10 @@ Closes # .
* [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
* [ ] Have you written new tests for your changes, as applicable?
* [ ] Have you successfully run tests with your changes locally?
* [ ] Have you created a changelog file by running `pnpm nx affected --target=changelog`?
<!-- Mark completed items with an [x] -->
### Changelog entry
### FOR PR REVIEWER ONLY:
> Enter a summary of all changes on this Pull Request. This will appear in the changelog if accepted.
* [ ] I have reviewed that everything is sanitized/escaped appropriately for any SQL or XSS injection possibilities. I made sure Linting is not ignored or disabled.

76
.github/project-pr-labeler.yml vendored Normal file
View File

@ -0,0 +1,76 @@
'package: @woocommerce/api':
- packages/js/api/**/*
'package: @woocommerce/e2e-utils':
- packages/js/e2e-utils/**/*
'package: @woocommerce/e2e-environment':
- packages/js/e2e-environment/**/*
'package: @woocommerce/api-core-tests':
- packages/js/api-core-tests/**/*
'package: @woocommerce/e2e-core-tests':
- packages/js/e2e-core-tests/**/*
'package: @woocommerce/admin-e2e-tests':
- packages/js/admin-e2e-tests/**/*
'package: @woocommerce/components':
- packages/js/components/**/*
'package: @woocommerce/csv-export':
- packages/js/csv-export/**/*
'package: @woocommerce/currency':
- packages/js/currency/**/*
'package: @woocommerce/customer-effort-score':
- packages/js/customer-effort-score/**/*
'package: @woocommerce/data':
- packages/js/data/**/*
'package: @woocommerce/date':
- packages/js/date/**/*
'package: dependency-extraction-webpack-plugin':
- packages/js/dependency-extraction-webpack-plugin/**/*
'package: @woocommerce/eslint-plugin':
- packages/js/eslint-plugin/**/*
'package: @woocommerce/experimental':
- packages/js/experimental/**/*
'package: @woocommerce/explat':
- packages/js/explat/**/*
'package: @woocommerce/js-tests':
- packages/js/js-tests/**/*
'package: @woocommerce/navigation':
- packages/js/navigation/**/*
'package: @woocommerce/notices':
- packages/js/notices/**/*
'package: @woocommerce/number':
- packages/js/number/**/*
'package: @woocommerce/onboarding':
- packages/js/onboarding/**/*
'package: @woocommerce/style-build':
- packages/js/style-build/**/*
'package: @woocommerce/tracks':
- packages/js/tracks/**/*
'plugin: woocommerce':
- plugins/woocommerce/**/*
'focus: react admin':
- plugins/woocommerce/src/Admin/**/*
- plugins/woocommerce/src/Internal/Admin/**/*
- plugins/woocommerce-admin/**/*

View File

@ -0,0 +1,30 @@
name: Build release zip file
on:
workflow_dispatch:
inputs:
ref:
description: 'By default the zip file is generated from the branch the workflow runs from, but you can specify an explicit reference to use instead here (e.g. refs/tags/tag_name or refs/heads/release/x.x). The resulting file will be available as an artifact on the workflow run.'
required: false
default: ''
jobs:
build:
name: Build release zip file
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.ref || github.ref }}
- name: Build the zip file
id: build
uses: woocommerce/action-build@trunk
- name: Unzip the file (prevents double zip problem)
run: unzip ${{ steps.build.outputs.zip_path }} -d zipfile
- name: Upload the zip file as an artifact
uses: actions/upload-artifact@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: woocommerce
path: zipfile
retention-days: 7

View File

@ -8,10 +8,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Build
id: build
uses: woocommerce/action-build@v2
uses: woocommerce/action-build@trunk
- name: Upload release asset
uses: actions/upload-release-asset@v1
env:
@ -21,3 +21,30 @@ jobs:
asset_path: ${{ steps.build.outputs.zip_path }}
asset_name: woocommerce.zip
asset_content_type: application/zip
update-code-reference:
if: github.event.release.prerelease == false && github.event.release.draft == false && github.repository_owner == 'woocommerce'
name: Update Code Reference
needs: build
runs-on: ubuntu-latest
steps:
- name: Invoke Code Reference build and deploy workflow
uses: aurelien-baudet/workflow-dispatch@v2
with:
workflow: GitHub Pages deploy
repo: woocommerce/code-reference
token: ${{ secrets.CUSTOM_GH_TOKEN }}
ref: refs/heads/trunk
inputs: '{ "version": "${{ github.event.release.tag_name }}" }'
run-release-smoke-tests:
name: Execute Smoke test release
needs: build
runs-on: ubuntu-latest
steps:
- name: Invoke release smoke testing workflow
uses: aurelien-baudet/workflow-dispatch@v2
with:
workflow: Smoke test release
repo: ${{ github.repository }}
token: ${{ secrets.E2E_WORKFLOW_GH_TOKEN }}
ref: refs/heads/trunk
inputs: '{ "release_id": "${{ github.event.release.id }}" }'

97
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,97 @@
name: Run CI
on:
push:
branches:
- trunk
- 'release/**'
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }}
timeout-minutes: 20
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.wp == 'nightly' }}
strategy:
fail-fast: false
matrix:
php: [ '7.2', '7.3', '7.4', '8.0' ]
wp: [ 'latest' ]
include:
- wp: nightly
php: '7.4'
- wp: '5.8'
php: 7.2
- wp: '5.7'
php: 7.2
services:
database:
image: mysql:5.6
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer
extensions: mysql
coverage: none
- name: Tool versions
run: |
php --version
composer --version
- name: Get cached composer and pnpm directories
uses: actions/cache@v3
id: cache-deps
with:
path: |
~/.pnpm-store
plugins/woocommerce/packages
plugins/woocommerce/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('plugins/woocommerce/composer.lock', '**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce
- name: Build Admin feature config
run: pnpm nx build:feature-config woocommerce
- name: Add PHP8 Compatibility.
run: |
if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
cd plugins/woocommerce
composer bin phpunit config --unset platform
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
rm -rf ./vendor/phpunit/
pnpm nx composer-dump-autoload woocommerce
fi
- name: Init DB and WP
working-directory: plugins/woocommerce
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
- name: Run tests
run: pnpm nx test-unit woocommerce

64
.github/workflows/mirrors.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: Mirrors
on:
push:
branches: ["trunk", "release/**"]
jobs:
build:
if: github.repository == 'woocommerce/woocommerce'
name: Build WooCommerce zip
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build
id: build
uses: woocommerce/action-build@trunk
env:
BUILD_ENV: mirrors
- name: Upload PR zip
uses: actions/upload-artifact@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: woocommerce
path: ${{ steps.build.outputs.zip_path }}
retention-days: 7
mirror:
if: github.repository == 'woocommerce/woocommerce'
name: Push to Mirror
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Create directories
run: |
mkdir -p tmp/woocommerce-build
mkdir -p monorepo
- name: Checkout monorepo
uses: actions/checkout@v2
with:
path: monorepo
- name: Download WooCommerce ZIP
uses: actions/download-artifact@v2
with:
name: woocommerce
path: tmp/woocommerce-build
- name: Extract and replace WooCommerce zip.
working-directory: tmp/woocommerce-build
run: |
mkdir -p woocommerce/woocommerce-production
unzip woocommerce.zip -d woocommerce/woocommerce-production
mv woocommerce/woocommerce-production/woocommerce/* woocommerce/woocommerce-production
rm -rf woocommerce/woocommerce-production/woocommerce
- name: Set up mirror
working-directory: tmp/woocommerce-build
run: |
touch mirrors.txt
echo "woocommerce/woocommerce-production" >> mirrors.txt
- name: Push to mirror
uses: Automattic/action-push-to-mirrors@v1
with:
source-directory: ${{ github.workspace }}/monorepo
token: ${{ secrets.API_TOKEN_GITHUB }}
username: matticbot
working-directory: ${{ github.workspace }}/tmp/woocommerce-build
timeout-minutes: 5 # 2021-01-18: Successful runs seem to take about half a minute.

View File

@ -4,20 +4,21 @@ on:
- cron: '0 0 * * *' # Run at 12 AM UTC.
jobs:
build:
if: github.repository_owner == 'woocommerce'
name: Nightly builds
strategy:
fail-fast: false
matrix:
build: [master]
build: [trunk]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ matrix.build }}
- name: Build
id: build
uses: woocommerce/action-build@v2
uses: woocommerce/action-build@trunk
- name: Deploy nightly build
uses: WebFreak001/deploy-nightly@v1.1.0
env:

View File

@ -1,122 +1,222 @@
name: Build zip for PR
on:
pull_request
on: pull_request
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build zip for PR
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
build:
name: Build zip for PR
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build
id: build
uses: woocommerce/action-build@v2
- name: Build
id: build
uses: woocommerce/action-build@trunk
env:
BUILD_ENV: e2e
- name: Upload PR zip
uses: actions/upload-artifact@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: woocommerce
path: ${{ steps.build.outputs.zip_path }}
retention-days: 7
- name: Upload PR zip
uses: actions/upload-artifact@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: woocommerce
path: ${{ steps.build.outputs.zip_path }}
retention-days: 7
- name: Add comment
uses: actions/github-script@v3
if: github.repository_owner == 'woocommerce'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: ':package: Artifacts ready for [download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})!'
})
e2e-tests-cache:
name: Set e2e caches for running tests
runs-on: ubuntu-latest
steps:
- name: Checkout code.
uses: actions/checkout@v2
- name: Load Node.js.
uses: actions/setup-node@v2
with:
node-version: '12'
# From https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action
- name: Cache node modules
uses: actions/cache@v2
id: cache_node_modules
env:
cache-name: cache-node-modules
with:
path: ./node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
e2e-tests-run:
name: Runs E2E tests.
runs-on: ubuntu-18.04
needs: [build]
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
- name: Run npm install, and cache if they aren't.
run: npm install
e2e-tests-run:
name: Runs E2E tests.
runs-on: ubuntu-latest
needs: [ build, e2e-tests-cache ]
steps:
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Checkout code.
uses: actions/checkout@v2
with:
path: package/woocommerce
# From https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action
- name: Cache node modules
uses: actions/cache@v2
id: cache_node_modules
env:
cache-name: cache-node-modules
with:
path: ./node_modules
key: ${{ runner.os }}-build-${{ hashFiles('package-lock.json') }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-
- name: Download WooCommerce ZIP.
uses: actions/download-artifact@v3
with:
name: woocommerce
path: tmp
- name: Restore node modules from cache, if available.
run: mv ./node_modules package/woocommerce/node_modules
- name: Extract and replace WooCommerce zip.
working-directory: tmp
run: |
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Run npm install.
working-directory: package/woocommerce
run: npm install
- name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Load docker images and start containers.
working-directory: package/woocommerce
run: npx wc-e2e docker:up
- name: Install PNPM
run: npm install -g pnpm
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/* ./code/woocommerce
- name: Install dependencies
working-directory: package/woocommerce
run: pnpm install
- name: Download WooCommerce ZIP.
uses: actions/download-artifact@v2
with:
name: woocommerce
path: tmp
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpm exec wc-e2e docker:up
- name: Extract and replace WooCommerce zip.
working-directory: tmp
run: |
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce
env:
WC_E2E_SCREENSHOTS: 1
E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }}
run: pnpm exec wc-e2e test:e2e
- name: Run tests command.
working-directory: code/woocommerce
run: npx wc-e2e test:e2e
- name: Archive E2E test screenshots
uses: actions/upload-artifact@v3
if: always()
with:
name: E2E Screenshots
path: package/woocommerce/plugins/woocommerce/tests/e2e/screenshots
if-no-files-found: ignore
retention-days: 5
api-tests-run:
name: Runs API tests.
runs-on: ubuntu-18.04
needs: [build]
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce ZIP.
uses: actions/download-artifact@v3
with:
name: woocommerce
path: tmp
- name: Extract and replace WooCommerce zip.
working-directory: tmp
run: |
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
working-directory: package/woocommerce
run: pnpm install
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpm exec wc-e2e docker:up
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce
env:
BASE_URL: http://localhost:8084
USER_KEY: admin
USER_SECRET: password
run: pnpm exec wc-api-tests test api
- name: Upload API test report
uses: actions/upload-artifact@v3
with:
name: api-test-report---pr-${{ github.event.number }}
path: |
package/woocommerce/packages/js/api-core-tests/allure-results
package/woocommerce/packages/js/api-core-tests/allure-report
retention-days: 7
k6-tests-run:
name: Runs k6 Performance tests
runs-on: ubuntu-18.04
needs: [build]
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce ZIP.
uses: actions/download-artifact@v3
with:
name: woocommerce
path: tmp
- name: Extract and replace WooCommerce zip.
working-directory: tmp
run: |
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
working-directory: package/woocommerce
run: pnpm install
- name: Workaround to use initialization file with prepopulated data.
working-directory: package/woocommerce/plugins/woocommerce/tests/e2e/docker
run: |
cp init-sample-products.sh initialize.sh
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpm exec wc-e2e docker:up
- name: Install k6
run: |
curl https://github.com/grafana/k6/releases/download/v0.33.0/k6-v0.33.0-linux-amd64.tar.gz -L | tar xvz --strip-components 1
- name: Run k6 tests
run: |
./k6 run package/woocommerce/plugins/woocommerce/tests/performance/tests/gh-action-pr-requests.js

View File

@ -1,10 +1,16 @@
name: Run code coverage on PR
on:
pull_request
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Code coverage (PHP 7.4, WP Latest)
timeout-minutes: 15
timeout-minutes: 20
runs-on: ubuntu-latest
services:
database:
@ -16,7 +22,9 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 100
- name: Setup PHP
uses: shivammathur/setup-php@v2
@ -31,21 +39,38 @@ jobs:
php --version
composer --version
- name: Get cached composer directories
uses: actions/cache@v2
- name: Get cached composer and pnpm directories
uses: actions/cache@v3
id: cache-deps
with:
path: |
./packages
./vendor
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
~/.pnpm-store
plugins/woocommerce/packages
plugins/woocommerce/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('plugins/woocommerce/composer.lock', '**/pnpm-lock.yaml') }}
- name: Setup and install composer
run: composer install
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce
- name: Build Admin feature config
run: |
pnpm nx build:feature-config woocommerce
- name: Init DB and WP
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 latest
run: pnpm nx install-unit-test-db woocommerce
- name: Run unit tests with code coverage. Allow to fail.
run: |
RUN_CODE_COVERAGE=1 bash ./tests/bin/phpunit.sh
pnpm nx test-code-coverage woocommerce
exit 0
- name: Send code coverage to Codecov.
run: |
bash <(curl -s https://codecov.io/bash)

View File

@ -1,49 +1,59 @@
name: Run code sniff on PR
on:
pull_request
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Code sniff (PHP 7.4, WP Latest)
timeout-minutes: 15
runs-on: ubuntu-latest
services:
database:
image: mysql:5.6
env:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 100
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
tools: composer
extensions: mysql
coverage: none
tools: composer, cs2pr
- name: Tool versions
run: |
php --version
composer --version
- name: Get cached composer directories
uses: actions/cache@v2
- name: Get cached composer and pnpm directories
uses: actions/cache@v3
id: cache-deps
with:
path: |
./packages
./vendor
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
~/.pnpm-store
plugins/woocommerce/packages
plugins/woocommerce/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('plugins/woocommerce/composer.lock', '**/pnpm-lock.yaml') }}
- name: Setup and install composer
run: composer install
- name: Install PNPM
run: npm install -g pnpm
- name: Init DB and WP
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 latest
- name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce
- name: Run code sniff
run: RUN_PHPCS=1 bash ./tests/bin/phpcs.sh
continue-on-error: true
working-directory: plugins/woocommerce
run: ./tests/bin/phpcs.sh "${{ github.event.pull_request.base.sha }}" "${{ github.event.after }}"
- name: Show PHPCS results in PR
working-directory: plugins/woocommerce
run: cs2pr ./phpcs-report.xml

26
.github/workflows/pr-lint-monorepo.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Run lint checks potentially affecting projects across the monorepo
on: pull_request
concurrency:
group: changelogger-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changelogger_used:
name: Changelogger use
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
- name: Check change files are touched for touched projects
env:
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
run: php tools/monorepo/check-changelogger-use.php --debug "$BASE" "$HEAD"

47
.github/workflows/pr-lint-test-js.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Lint and tests for JS packages and woocommerce-admin/client
on:
pull_request:
paths:
- 'packages/js/**/**'
- 'plugins/woocommerce-admin/client/**'
- '!**.md'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-test-js:
name: Lint and Test JS
runs-on: ubuntu-18.04
steps:
- name: Check out repository code
uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: '16'
- name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Lint
run: |
pnpm nx lint woocommerce-admin
pnpm nx lint:js-packages woocommerce-admin
- name: Test
run: |
pnpm nx build woocommerce-admin
pnpm nx test woocommerce-admin
pnpm nx test:packages woocommerce-admin

18
.github/workflows/pr-project-label.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: 'Label Pull Request Project'
on:
pull_request_target:
types:
- opened
- synchronize
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
label_project:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v3
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/project-pr-labeler.yml

125
.github/workflows/pr-smoke-test.yml vendored Normal file
View File

@ -0,0 +1,125 @@
name: Run smoke tests against pull request.
on:
pull_request:
branches:
- trunk
types:
- labeled
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
prcheck:
name: Smoke test a pull request.
if: "${{ contains(github.event.label.name, 'run: smoke tests') }}"
runs-on: ubuntu-18.04
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
- name: Get cached composer and pnpm directories
uses: actions/cache@v3
id: cache-deps
with:
path: |
~/.pnpm-store
package/woocommerce/plugins/woocommerce/packages
package/woocommerce/plugins/woocommerce/vendor
key: ${{ runner.os }}-smoke-test-npm-composer-${{ hashFiles('plugins/woocommerce/composer.lock', '**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install-no-dev woocommerce
- name: Install prerequisites.
working-directory: package/woocommerce/plugins/woocommerce
id: installation
run: |
pnpm nx build-assets woocommerce
pnpm install jest
- name: Run smoke test.
working-directory: package/woocommerce/plugins/woocommerce
if: always()
env:
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
SMOKE_TEST_ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }}
SMOKE_TEST_CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }}
SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }}
WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000
run: |
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
- name: Post Smoke tests results comment on PR
if: always()
uses: actions/github-script@v5
env:
TITLE: 'Smoke Test Results'
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const script = require( './package/woocommerce/packages/js/e2e-environment/bin/post-results-to-github-pr.js' )
await script({github, context})
- name: Run E2E tests.
working-directory: package/woocommerce/plugins/woocommerce
if: always()
env:
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
SMOKE_TEST_ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }}
SMOKE_TEST_CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }}
SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }}
WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000
run: |
pnpm exec wc-e2e test:e2e
- name: Post E2E tests results comment on PR
if: always()
uses: actions/github-script@v5
env:
TITLE: 'E2E Test Results'
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const script = require( './package/woocommerce/packages/js/e2e-environment/bin/post-results-to-github-pr.js' )
await script({github, context})
- name: Remove label from pull request.
if: |
always()
&& contains( github.event.pull_request.labels.*.name, format('run{0} smoke tests', ':'))
uses: actions-ecosystem/action-remove-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: 'run: smoke tests'

View File

@ -1,22 +1,30 @@
name: Run unit tests on PR
on:
pull_request
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }}
timeout-minutes: 15
timeout-minutes: 20
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.wp == 'nightly' }}
strategy:
fail-fast: false
matrix:
php: [ '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ]
php: [ '7.2', '7.3', '7.4', '8.0' ]
wp: [ "latest" ]
include:
- wp: nightly
php: '7.4'
- wp: '5.5'
- wp: '5.8'
php: 7.2
- wp: '5.4'
- wp: '5.7'
php: 7.2
services:
database:
@ -43,29 +51,46 @@ jobs:
php --version
composer --version
- name: Get cached composer directories
uses: actions/cache@v2
- name: Get cached composer and pnpm directories
uses: actions/cache@v3
id: cache-deps
with:
path: |
./packages
./vendor
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
~/.pnpm-store
plugins/woocommerce/packages
plugins/woocommerce/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('plugins/woocommerce/composer.lock', '**/pnpm-lock.yaml') }}
- name: Setup and install composer
run: composer install
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce
- name: Build Admin feature config
run: |
pnpm nx build:feature-config woocommerce
- name: Add PHP8 Compatibility.
run: |
if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
cd plugins/woocommerce
composer bin phpunit config --unset platform
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
fi
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
rm -rf ./vendor/phpunit/
pnpm nx composer-dump-autoload woocommerce
fi
- name: Init DB and WP
working-directory: plugins/woocommerce
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
- name: Run tests
run: ./vendor/bin/phpunit -c ./phpunit.xml
run: pnpm nx test-unit woocommerce

View File

@ -0,0 +1,46 @@
name: "Pull request post-merge processing"
on:
pull_request_target:
types: [closed]
jobs:
process-pull-request-after-merge:
name: "Process a pull request after it's merged"
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: "Get the action scripts"
run: |
scripts="assign-milestone-to-merged-pr.php add-post-merge-comment.php post-request-shared.php"
for script in $scripts
do
curl \
--silent \
--fail \
--header 'Authorization: bearer ${{ secrets.GITHUB_TOKEN }}' \
--header 'User-Agent: GitHub action to set the milestone for a pull request' \
--header 'Accept: application/vnd.github.v3.raw' \
--output $script \
--location "$GITHUB_API_URL/repos/${{ github.repository }}/contents/.github/workflows/scripts/$script?ref=${{ github.event.pull_request.base.ref }}"
done
env:
GITHUB_API_URL: ${{ env.GITHUB_API_URL }}
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
- name: "Run the script to assign a milestone"
if: |
contains(github.event.pull_request.labels.*.name, 'plugin: woocommerce') &&
!github.event.pull_request.milestone &&
github.event.pull_request.base.ref == 'trunk'
run: php assign-milestone-to-merged-pr.php
env:
PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Run the script to post a comment with next steps hint"
if: "contains(github.event.pull_request.labels.*.name, 'plugin: woocommerce')"
run: php add-post-merge-comment.php
env:
PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,35 @@
name: "Enforce release code freeze"
on:
schedule:
- cron: '0 16 * * 4' # Run at 1600 UTC on Thursdays.
jobs:
maybe-create-next-milestone-and-release-branch:
name: "Maybe create next milestone and release branch"
runs-on: ubuntu-latest
steps:
- name: "Get the action script"
run: |
scripts="post-request-shared.php release-code-freeze.php"
for script in $scripts
do
curl \
--silent \
--fail \
--header 'Authorization: bearer ${{ secrets.GITHUB_TOKEN }}' \
--header 'User-Agent: GitHub action to enforce release code freeze' \
--header 'Accept: application/vnd.github.v3.raw' \
--output $script \
--location "$GITHUB_API_URL/repos/${{ github.repository }}/contents/.github/workflows/scripts/$script?ref=$GITHUB_REF"
done
env:
GITHUB_API_URL: ${{ env.GITHUB_API_URL }}
GITHUB_REF: ${{ env.GITHUB_REF }}
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
- name: "Run the script to enforce the code freeze"
run: php release-code-freeze.php
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,94 @@
<?php
/**
* Script to automatically add a comment to a pull request when it's merged.
*
* @package WooCommerce/GithubActions
*/
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions
require_once __DIR__ . '/post-request-shared.php';
/*
* Get the merger login name.
*/
echo 'Retrieving the merger user name... ';
$get_pr_merged_username_query = "
node(id: \"$pr_id\") {
... on PullRequest {
timelineItems(first: 1, itemTypes: MERGED_EVENT) {
edges {
node {
... on MergedEvent {
actor {
login
}
}
}
}
}
}
}";
$result = do_graphql_api_request( $get_pr_merged_username_query );
if ( is_array( $result ) ) {
if ( empty( $result['errors'] ) ) {
echo "Ok!\n";
} else {
echo "\n*** Errors found while retrieving the merger user name:\n";
echo var_dump( $result['errors'] );
return;
}
} else {
echo "\n*** Error found while retrieving the merger user name: file_get_contents returned the following:\n";
echo var_dump( $result );
return;
}
$merger_user_name = $result['data']['node']['timelineItems']['edges'][0]['node']['actor']['login'];
echo "The pull request was merged by: $merger_user_name\n";
/*
* Post the comment.
*/
$comment_body = "Hi @$merger_user_name, thanks for merging this pull request. Please take a look at these follow-up tasks you may need to perform:
- [ ] Add the `release: add testing instructions` label";
$add_comment_mutation = "
addComment(input: {subjectId: \"$pr_id\", body: \"$comment_body\", clientMutationId: \"$github_token\"}) {
commentEdge {
node {
id
databaseId
url
}
}
}";
echo 'Publishing the comment... ';
$result = do_graphql_api_request( $add_comment_mutation, true );
if ( is_array( $result ) ) {
if ( empty( $result['errors'] ) ) {
echo "Ok!\n";
} else {
echo "\n*** Errors found while publishing the comment:\n";
echo var_dump( $result['errors'] );
return;
}
} else {
echo "\n*** Error found while publishing the comment: file_get_contents returned the following:\n";
echo var_dump( $result );
return;
}
$comment_url = $result['data']['addComment']['commentEdge']['node']['url'];
echo "Comment URL: $comment_url\n";
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions

View File

@ -0,0 +1,47 @@
<?php
/**
* Script to automatically assign a milestone to a pull request when it's merged.
*
* @package WooCommerce/GithubActions
*/
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions
require_once __DIR__ . '/post-request-shared.php';
$chosen_milestone = get_latest_milestone_from_api( true );
echo 'Milestone that will be assigned: ' . $chosen_milestone['title'] . "\n";
if ( getenv( 'DRY_RUN' ) ) {
echo "Dry run, skipping the actual milestone assignment\n";
return;
}
/*
* Assign the milestone to the pull request.
*/
echo 'Assigning the milestone to the pull request... ';
$milestone_id = $chosen_milestone['id'];
$mutation = "
updatePullRequest(input: {pullRequestId: \"$pr_id\", milestoneId: \"$milestone_id\"}) {
clientMutationId
}
";
$result = do_graphql_api_request( $mutation, true );
if ( is_array( $result ) ) {
if ( empty( $result['errors'] ) ) {
echo "Ok!\n";
} else {
echo "\n*** Errors found while assigning the milestone:\n";
echo var_dump( $result['errors'] );
}
} else {
echo "\n*** Error found while assigning the milestone: file_get_contents returned the following:\n";
echo var_dump( $result );
}
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions

View File

@ -0,0 +1,54 @@
/**
* A script that fetches the asset id of a given release and sets it as the output for the step that calls it
*/
const https = require('https');
const options = {
hostname: 'api.github.com',
port: 443,
path: `/repos/${process.env.REPO}/releases/${process.env.RELEASE_ID}/assets`,
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${ process.env.GITHUB_TOKEN }`,
'User-Agent': 'WooCommerce Smoke Build'
},
};
/**
*
* @returns {Promise}
*/
const fetchAssetId = () => {
return new Promise( ( resolve, reject ) => {
const request = https.get( options, ( response ) => {
response.setEncoding('utf8');
let responseBody = '';
response.on( 'data', ( chunk ) => {
responseBody += chunk;
} );
response.on( 'end', () => {
const assets = JSON.parse( responseBody );
// use the most recently uploaded asset
resolve( assets[ assets.length - 1 ].id );
} );
} );
request.on('error', ( error ) => {
reject( error );
} );
request.end();
} );
}
module.exports = async ( { github, context, core } ) => {
const id = await fetchAssetId();
// set asset_id as the output
core.setOutput( 'asset_id', id );
}

View File

@ -0,0 +1,284 @@
<?php
/**
* Common code for the post-merge GitHub action scripts.
*
* @package WooCommerce/GithubActions
*/
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions
global $repo_owner, $repo_name, $github_token, $graphql_api_url;
/*
* Grab/process input.
*/
$repo_parts = explode( '/', getenv( 'GITHUB_REPOSITORY' ) );
$repo_owner = $repo_parts[0];
$repo_name = $repo_parts[1];
$pr_id = getenv( 'PULL_REQUEST_ID' );
$github_token = getenv( 'GITHUB_TOKEN' );
$github_api_url = getenv( 'GITHUB_API_URL' );
$graphql_api_url = getenv( 'GITHUB_GRAPHQL_URL' );
/**
* Function to get the latest milestone.
*
* @param bool $use_latest_when_null When true, the function returns the latest milestone regardless of release branch status.
* @return string The title of the latest milestone.
*/
function get_latest_milestone_from_api( $use_latest_when_null = false ) {
global $repo_owner, $repo_name;
echo 'Getting the list of milestones...' . PHP_EOL;
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
milestones(first: 10, states: [OPEN], orderBy: {field: CREATED_AT, direction: DESC}) {
nodes {
id
title
state
}
}
}
";
$json = do_graphql_api_request( $query );
$milestones = $json['data']['repository']['milestones']['nodes'];
$milestones = array_map(
function( $x ) {
return 1 === preg_match( '/^\d+\.\d+\.\d+$/D', $x['title'] ) ? $x : null;
},
$milestones
);
$milestones = array_filter( $milestones );
usort(
$milestones,
function( $a, $b ) {
return version_compare( $b['title'], $a['title'] );
}
);
echo 'Latest open milestone: ' . $milestones[0]['title'] . PHP_EOL;
$chosen_milestone = null;
foreach ( $milestones as $milestone ) {
$milestone_title_parts = explode( '.', $milestone['title'] );
$milestone_release_branch = 'release/' . $milestone_title_parts[0] . '.' . $milestone_title_parts[1];
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
ref(qualifiedName: \"refs/heads/$milestone_release_branch\") {
id
}
}
";
$result = do_graphql_api_request( $query );
if ( is_null( $result['data']['repository']['ref'] ) ) {
$chosen_milestone = $milestone;
} else {
break;
}
}
// If all the milestones have a release branch, just take the newest one.
if ( $use_latest_when_null && is_null( $chosen_milestone ) ) {
echo 'WARNING: No milestone without release branch found, the newest one will be assigned.' . PHP_EOL;
$chosen_milestone = $milestones[0];
}
return $chosen_milestone;
}
/**
* Function to get the last major.minor version with a release from the API.
*
* @return string Returns the latest version with a release formatted as "major.minor".
*/
function get_latest_version_with_release() {
global $repo_owner, $repo_name;
echo 'Getting the list of releases...' . PHP_EOL;
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
releases(first: 25, orderBy: { field: CREATED_AT, direction: DESC}) {
nodes {
tagName
}
}
}
";
$json = do_graphql_api_request( $query );
$releases = $json['data']['repository']['releases']['nodes'];
$releases = array_map(
function( $x ) {
return 1 === preg_match( '/^\d+\.\d+\.\d+/D', $x['tagName'] ) ? $x : null;
},
$releases
);
$releases = array_filter( $releases );
usort(
$releases,
function( $a, $b ) {
return version_compare( $b['tagName'], $a['tagName'] );
}
);
$major_minor = preg_replace( '/(^\d+\.\d+).*?$/', '\1', $releases[0]['tagName'] );
echo 'Most recent version with a release: ' . $major_minor . PHP_EOL;
return $major_minor;
}
/**
* Function to retreive the sha1 reference for a given branch name.
*
* @param string $branch The name of the branch.
* @return string Returns the name of the branch, or a falsey value on error.
*/
function get_ref_from_branch( $branch ) {
global $repo_owner, $repo_name;
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
ref(qualifiedName: \"refs/heads/{$branch}\") {
target {
... on Commit {
history(first: 1) {
edges{ node{ oid } }
}
}
}
}
}
";
$result = do_graphql_api_request( $query );
// Warnings suppressed here because traversing this level of arrays with isset / is_array checks would be messy.
return @$result['data']['repository']['ref']['target']['history']['edges'][0]['node']['oid'];
}
/**
* Function to create milestone using the GitHub REST API.
*
* @param string $title The title of the milestone to be created.
* @return bool True on success, False otherwise.
*/
function create_github_milestone( $title ) {
global $repo_owner, $repo_name;
$result = do_github_api_post_request( "/repos/{$repo_owner}/{$repo_name}/milestones", array(
'title' => $title,
) );
return is_array( $result ) && $result['title'] === $title;
}
/**
* Function to create branch using the GitHub REST API.
*
* @param string $branch The branch to be created.
* @param string $sha The sha1 reference for the branch.
* @return bool True on success, False otherwise.
*/
function create_github_branch( $branch, $sha ) {
global $repo_owner, $repo_name;
$ref = "refs/heads/{$branch}";
$result = do_github_api_post_request( "/repos/{$repo_owner}/{$repo_name}/git/refs", array(
'ref' => $ref,
'sha' => $sha,
) );
return is_array( $result ) && $result['ref'] === $ref;
}
/**
* Function to create branch using the GitHub REST API from an existing branch.
*
* @param string $source The branch from which to create.
* @param string $target The branch to be created.
* @return bool True on success, False otherwise.
*/
function create_github_branch_from_branch( $source, $target ) {
$ref = get_ref_from_branch( $source );
if ( ! $ref ) {
return false;
}
return create_github_branch( $target, $ref );
}
/**
* Function to do a GitHub API POST Request.
*
* @param array $request_url
* @param array $body The body of the request to be json encoded.
* @return mixed The json-decoded response if a response is received, 'false' (or whatever file_get_contents returns) otherwise.
*/
function do_github_api_post_request( $request_path, $body ) {
global $github_token, $github_api_url, $github_api_response_code;
$context = stream_context_create(
array(
'http' => array(
'method' => 'POST',
'header' => array(
'Accept: application/vnd.github.v3+json',
'Content-Type: application/json',
'User-Agent: GitHub Actions for creation of milestones',
'Authorization: bearer ' . $github_token,
),
'content' => json_encode( $body ),
),
)
);
$full_request_url = rtrim( $github_api_url, '/' ) . '/' . ltrim( $request_path, '/' );
$result = @file_get_contents( $full_request_url, false, $context );
// Verify that the post request was sucessful.
$status_line = $http_response_header[0];
preg_match( "/^HTTPS?\/\d\.\d\s+(\d{3})\s+/i", $status_line, $matches );
$github_api_response_code = $matches[1];
if ( '2' !== substr( $github_api_response_code, 0, 1 ) ) {
return false;
}
return is_string( $result ) ? json_decode( $result, true ) : $result;
}
/**
* Function to query the GitHub GraphQL API.
*
* @param string $body The GraphQL-formatted request body, without "query" or "mutation" wrapper.
* @param bool $is_mutation True if the request is a mutation, false if it's a query.
* @return mixed The json-decoded response if a response is received, 'false' (or whatever file_get_contents returns) otherwise.
*/
function do_graphql_api_request( $body, $is_mutation = false ) {
global $github_token, $graphql_api_url;
$keyword = $is_mutation ? 'mutation' : 'query';
$data = array( 'query' => "$keyword { $body }" );
$context = stream_context_create(
array(
'http' => array(
'method' => 'POST',
'header' => array(
'Accept: application/json',
'Content-Type: application/json',
'User-Agent: GitHub action to set the milestone for a pull request',
'Authorization: bearer ' . $github_token,
),
'content' => json_encode( $data ),
),
)
);
$result = file_get_contents( $graphql_api_url, false, $context );
return is_string( $result ) ? json_decode( $result, true ) : $result;
}
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.AlternativeFunctions

View File

@ -0,0 +1,65 @@
<?php
/**
* Script to automatically enforce the release code freeze.
*
* @package WooCommerce/GithubActions
*/
require_once __DIR__ . '/post-request-shared.php';
$now = time();
if ( getenv( 'TIME_OVERRIDE' ) ) {
$now = strtotime( getenv( 'TIME_OVERRIDE' ) );
}
// Code freeze comes 26 days prior to release day.
$release_time = strtotime( '+26 days', $now );
$release_day_of_week = date( 'l', $release_time );
$release_day_of_month = (int) date( 'j', $release_time );
// If 26 days from now isn't the second Tuesday, then it's not code freeze day.
if ( 'Tuesday' !== $release_day_of_week || $release_day_of_month < 8 || $release_day_of_month > 14 ) {
echo 'Info: Today is not the Thursday of the code freeze.' . PHP_EOL;
return;
}
$latest_version_with_release = get_latest_version_with_release();
if ( empty( $latest_version_with_release ) ) {
echo '*** Error: Unable to get latest version with release' . PHP_EOL;
return;
}
// Because we go from 5.9 to 6.0, we can get the next major_minor by adding 0.1 and formatting appropriately.
$latest_float = (float) $latest_version_with_release;
$branch_major_minor = number_format( $latest_float + 0.1, 1 );
$milestone_major_minor = number_format( $latest_float + 0.2, 1 );
// We use those values to get the release branch and next milestones that we need to create.
$release_branch_to_create = "release/{$branch_major_minor}";
$milestone_to_create = "{$milestone_major_minor}.0";
if ( getenv( 'DRY_RUN' ) ) {
echo 'DRY RUN: Skipping actual creation of release branch and milestone...' . PHP_EOL;
echo "Release Branch: {$release_branch_to_create}" . PHP_EOL;
echo "Milestone: {$milestone_to_create}" . PHP_EOL;
return;
}
if ( create_github_milestone( $milestone_to_create ) ) {
echo "Created milestone {$milestone_to_create}" . PHP_EOL;
} else if ( '422' === $github_api_response_code ) {
// The milestone already existed when GitHub returns a 422 status.
echo "Notice: Unable to create {$milestone_to_create} milestone. Maybe it already exists? Skipping..." . PHP_EOL;
} else {
echo "*** Error: Unable to create {$milestone_to_create} milestone" . PHP_EOL;
}
if ( create_github_branch_from_branch( 'trunk', $release_branch_to_create ) ) {
echo "Created branch {$release_branch_to_create}" . PHP_EOL;
} else if ( '422' === $github_api_response_code ) {
// The release branch already existed when GitHub returns a 422 status.
echo "Notice: Unable to create {$release_branch_to_create} branch. Maybe it already exists? Skipping..." . PHP_EOL;
} else {
echo "*** Error: Unable to create {$release_branch_to_create}" . PHP_EOL;
}

View File

@ -0,0 +1,24 @@
name: Check daily smoke test site status.
on:
schedule:
- cron: '25 7 * * *'
jobs:
ping_site:
runs-on: ubuntu-latest
name: Check site and notify if not found
steps:
- name: Check site status
id: sitecheck
uses: srt32/uptime@958231f4d95c117f08eb0fc70907e80d0dfedf2b
with:
url-to-hit: "${{ secrets.SMOKE_TEST_URL }}ready/"
expected-statuses: "200,301"
- name: Send message to Slack API
if: failure()
uses: archive/github-actions-slack@deecc2edc496dc642d643de1d7cf3a47f51fb27a
id: notify
with:
slack-bot-user-oauth-access-token: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
slack-channel: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
slack-text: ':warning: <!subteam^${{ secrets.SMOKE_TEST_SLACK_GROUP }}> FYI the URL ${{ secrets.SMOKE_TEST_URL }}ready/ appears to be returning `404 not found` :x:'

153
.github/workflows/smoke-test-daily.yml vendored Normal file
View File

@ -0,0 +1,153 @@
name: Smoke test daily
on:
schedule:
- cron: '25 3 * * *'
jobs:
login-run:
name: Daily smoke test on trunk.
runs-on: ubuntu-18.04
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
ref: trunk
- name: Install prerequisites.
working-directory: package/woocommerce/plugins/woocommerce
run: |
npm install -g pnpm
pnpm install
pnpm nx composer-install-no-dev woocommerce
pnpm nx build-assets woocommerce
pnpm install jest
- name: Run smoke test.
working-directory: package/woocommerce/plugins/woocommerce
env:
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
SMOKE_TEST_ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }}
SMOKE_TEST_CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }}
SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }}
WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: 'C01U0H617MY'
UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
run: |
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
pnpm exec wc-e2e test:e2e
pnpm exec wc-api-tests test api
build:
name: Build zip for PR
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build
id: build
uses: woocommerce/action-build@trunk
env:
BUILD_ENV: e2e
- name: Upload PR zip
uses: actions/upload-artifact@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: woocommerce
path: ${{ steps.build.outputs.zip_path }}
retention-days: 7
test-plugins:
name: Smoke tests with ${{ matrix.plugin }} plugin installed
runs-on: ubuntu-18.04
needs: [build]
strategy:
fail-fast: false
matrix:
include:
- plugin: 'WooCommerce Payments'
repo: 'automattic/woocommerce-payments'
- plugin: 'WooCommerce PayPal Payments'
repo: 'woocommerce/woocommerce-paypal-payments'
- plugin: 'WooCommerce Shipping & Tax'
repo: 'automattic/woocommerce-services'
- plugin: 'WooCommerce Subscriptions'
repo: WC_SUBSCRIPTIONS_REPO
private: true
- plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo
repo: 'Yoast/wordpress-seo'
- plugin: 'Contact Form 7'
repo: 'takayukister/contact-form-7'
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code.
uses: actions/checkout@v2
with:
path: package/woocommerce
- name: Install PNPM and install dependencies
working-directory: package/woocommerce
run: |
npm install -g pnpm
pnpm install
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpm exec wc-e2e docker:up
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce ZIP.
uses: actions/download-artifact@v3
with:
name: woocommerce
path: tmp
- name: Extract and replace WooCommerce zip.
working-directory: tmp
run: |
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Install dependencies again
working-directory: package/woocommerce
run: |
npm install -g pnpm
pnpm install
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce
env:
WC_E2E_SCREENSHOTS: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
PLUGIN_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }}
PLUGIN_NAME: ${{ matrix.plugin }}
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: |
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js
pnpm exec wc-e2e test:e2e

197
.github/workflows/smoke-test-release.yml vendored Normal file
View File

@ -0,0 +1,197 @@
name: Smoke test release
on:
workflow_dispatch:
inputs:
release_id:
description: 'WooCommerce Release Id'
required: true
jobs:
login-run:
name: Daily smoke test on release.
runs-on: ubuntu-18.04
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
ref: trunk
- name: Install prerequisites.
working-directory: package/woocommerce/plugins/woocommerce
run: |
npm install -g pnpm
pnpm install
pnpm nx composer-install-no-dev woocommerce
pnpm nx build-assets woocommerce
pnpm install jest
- name: Run smoke test.
working-directory: package/woocommerce/plugins/woocommerce
env:
SMOKE_TEST_URL: ${{ secrets.RELEASE_TEST_URL }}
SMOKE_TEST_ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.RELEASE_TEST_ADMIN_USER_EMAIL }}
SMOKE_TEST_CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }}
SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }}
WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: 'C02DS4NE72S'
TEST_RELEASE: 1
UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000
BASE_URL: ${{ secrets.RELEASE_TEST_URL }}
USER_KEY: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
run: |
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
pnpm exec wc-e2e test:e2e
pnpm exec wc-api-tests test api
test-wp-version:
name: Smoke test on L-${{ matrix.wp }} WordPress version
runs-on: ubuntu-18.04
strategy:
matrix:
wp: ['1', '2']
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
- name: Fetch Asset ID
id: fetch_asset_id
uses: actions/github-script@v5
env:
RELEASE_ID: ${{ github.event.inputs.release_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
with:
script: |
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' )
await script({github, context, core})
- name: Install PNPM and install dependencies
working-directory: package/woocommerce
run: |
npm install -g pnpm
pnpm install
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
env:
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
run: pnpm nx docker-up woocommerce
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce release zip
working-directory: tmp
run: |
curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream'
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce
env:
WC_E2E_SCREENSHOTS: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
run: pnpm nx test-e2e woocommerce
test-plugins:
name: Smoke tests with ${{ matrix.plugin }} plugin installed
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
include:
- plugin: 'WooCommerce Payments'
repo: 'automattic/woocommerce-payments'
- plugin: 'WooCommerce PayPal Payments'
repo: 'woocommerce/woocommerce-paypal-payments'
- plugin: 'WooCommerce Shipping & Tax'
repo: 'automattic/woocommerce-services'
- plugin: 'WooCommerce Subscriptions'
repo: WC_SUBSCRIPTIONS_REPO
private: true
- plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo
repo: 'Yoast/wordpress-seo'
- plugin: 'Contact Form 7'
repo: 'takayukister/contact-form-7'
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code.
uses: actions/checkout@v2
with:
path: package/woocommerce
- name: Fetch Asset ID
id: fetch_asset_id
uses: actions/github-script@v5
env:
RELEASE_ID: ${{ github.event.inputs.release_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
with:
script: |
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' )
await script({github, context, core})
- name: Install PNPM and install dependencies
working-directory: package/woocommerce
run: |
npm install -g pnpm
pnpm install
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
env:
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
run: pnpm nx docker-up woocommerce
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce release zip
working-directory: tmp
run: |
curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream'
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce
env:
WC_E2E_SCREENSHOTS: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
PLUGIN_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }}
PLUGIN_NAME: ${{ matrix.plugin }}
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: |
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js
pnpm exec wc-e2e test:e2e

24
.github/workflows/stalebot.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: 'Close stale needs-feedback issues'
on:
schedule:
- cron: '21 0 * * *'
jobs:
stale:
if: |
! contains(github.event.issue.labels.*.name, 'type: enhancement')
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "As a part of this repository's maintenance, this issue is being marked as stale due to inactivity. Please feel free to comment on it in case we missed something.\n\n###### After 7 days with no activity this issue will be automatically be closed."
close-issue-message: 'This issue was closed because it has been 14 days with no activity.'
days-before-issue-stale: 7
days-before-issue-close: 7
days-before-pr-close: -1
stale-issue-label: 'status: stale'
stale-pr-label: 'status: stale'
only-issue-labels: 'needs: author feedback'
close-issue-label: "status: can't reproduce"
ascending: true

15
.github/workflows/triage-label.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: Add Triage Label
on:
issues:
types: opened
jobs:
add_label:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-ecosystem/action-add-labels@v1
if: github.event.issue.labels[0] == null
with:
labels: 'status: awaiting triage'

166
.github/workflows/triage-replies.yml vendored Normal file
View File

@ -0,0 +1,166 @@
name: Add issue triage comments.
on:
issues:
types:
- labeled
jobs:
add-dev-comment:
if: "github.event.label.name == 'needs: developer feedback'"
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add developer feedback comment
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Hi @${{ github.event.issue.user.login }},\n\n\
Thank you for opening the issue! It requires further feedback from the WooCommerce Core team.\n\n\
We are adding the `needs developer feedback` label to this issue so that the Core team could take a look.\n\n\
Please note it may take a few days for them to get to this issue. Thank you for your patience.'
})
add-reproduction-comment:
if: "github.event.label.name == 'status: reproduction'"
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add needs reproduction comment
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'We are adding the `status: needs reproduction` label to this issue to try reproduce it on the \
current released version of WooCommerce.\n\n\
Thank you for your patience.'
})
add-support-comment:
if: "github.event.label.name == 'type: support request'"
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add support request comment
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Hi @${{ github.event.issue.user.login }},\n\n\
While our goal is to address all the issues reported in this repository, \
GitHub should be treated as a place to report confirmed bugs only.\n\n\
The type of issue you submitted looks like a support request which may or may not reveal a bug once proper \
troubleshooting is done. In order to confirm the bug, please follow one of the steps below:\n\n\
- Review [WooCommerce Self-Service Guide](https://docs.woocommerce.com/document/woocommerce-self-service-guide/) \
to see if the solutions listed there apply to your case;\n\
- If you are a paying customer of WooCommerce, contact WooCommerce support by \
[opening a ticket or starting a live chat](https://woocommerce.com/contact-us/);\n\
- Make a post on [WooCommerce community forum](https://wordpress.org/support/plugin/woocommerce/)\n\n\
If you confirm the bug, please provide us with clear steps to reproduce it.\n\n\
We are closing this issue for now as it seems to be a support request and not a bug. \
If we missed something, please leave a comment and we will take a second look.'
})
- name: Close support request issue
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
state: 'closed'
})
add-votes-comment:
if: "github.event.label.name == 'needs: votes'"
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add votes needed comment
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: "Thanks for the suggestion @${{ github.event.issue.user.login }},\n\n\
While we appreciate you sharing your ideas with us, it doesn't fit in with our current priorities for the project.\n\
At some point, we may revisit our priorities and look through the list of suggestions like this one to see if it \
warrants a second look.\n\n\
In the meantime, we are going to close this issue with the `votes needed` label and evaluate over time if this \
issue collects more feedback.\n\n\
Don't be alarmed if you don't see any activity on this issue for a while. \
We'll keep an eye on the popularity of this request."
})
- name: Close votes needed issue
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
state: 'closed'
})
fill-template-comment:
if: "github.event.label.name == 'needs: template'"
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add reply to fill template
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: "Hi @${{ github.event.issue.user.login }},\n\n\
Thank you for submitting the issue. However, you didn't fill out the details of the bug report template that we ask for. Without these details, we can't fully evaluate this issue. Please provide us with the information requested so we could take a look further.\n\n\
**Describe the bug**\n\n\
A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action.\n\n\
**To Reproduce**\n\n\
Steps to reproduce the behavior:\n\n\
1. Go to '...'\n\
2. Click on '....'\n\
3. Scroll down to '....'\n\
4. See error\n\n\
**Screenshots**\n\n\
If applicable, add screenshots to help explain your problem.\n\n\
**Expected behavior**\n\n\
A clear and concise description of what you expected to happen.\n\n\
**Isolating the problem (mark completed items with an [x]):**\n\n\
- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.\n\
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).\n\
- [ ] I can reproduce this bug consistently using the steps above.\n\n\
**WordPress Environment**\n\n\
Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin."
})
- name: remove-needs-template-label
uses: actions-ecosystem/action-remove-labels@v1
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
labels: 'needs: template'
- name: add-needs-author-feedback-label
uses: actions-ecosystem/action-add-labels@v1
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
labels: 'needs: author feedback'

View File

@ -0,0 +1,28 @@
name: 'Update contributor feedback labels on comment'
on: 'issue_comment'
jobs:
feedback:
if: |
github.actor != 'github-actions' &&
github.actor == github.event.issue.user.login &&
github.event.issue &&
github.event.issue.state == 'open' &&
contains(github.event.issue.labels.*.name, 'needs: author feedback')
runs-on: ubuntu-latest
steps:
- name: Add has feedback
uses: actions-ecosystem/action-add-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: 'needs: triage feedback'
- name: remove needs feedback
uses: actions-ecosystem/action-remove-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: 'needs: author feedback'
- name: remove stale
uses: actions-ecosystem/action-remove-labels@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: 'status: stale'

107
.gitignore vendored
View File

@ -1,78 +1,85 @@
# Editors
# Operating System files
.DS_Store
Thumbs.db
# IDE files
.idea
.vscode/*
project.xml
project.properties
/nbproject/private/
.buildpath
.project
.settings*
.idea
.vscode
*.sublime-project
*.sublime-workspace
.sublimelinterrc
*.swp
# Excluded IDE Files for developer experience tooling within workspace
!.vscode/tasks.json
# Grunt
node_modules/
none
# Sass
.sass-cache/
# Compiled CSS
/assets/css/*.css
/assets/css/photoswipe/**/*.min.css
# Logs
logs/
# Minified JS
/assets/js/**/*.min.js
# Eslint Cache
.eslintcache
# OS X metadata
.DS_Store
# Environment files
wp-cli.local.yml
yarn-error.log
npm-debug.log
.pnpm-debug.log
# Windows junk
Thumbs.db
# Build files
*.sql
*.swp
*.zip
# Behat/CLI Tests
tests/cli/installer
tests/cli/composer.phar
tests/cli/composer.lock
tests/cli/composer.json
tests/cli/vendor
# Built packages
build/
build-module/
build-style/
build-types/
dist/
# Project files
node_modules/
vendor/
# TypeScript files
tsconfig.tsbuildinfo
# Node Package Dependencies
package-lock.json
# wp-env config
.wp-env.override.json
# Unit tests
/tmp
/tests/bin/tmp
/tests/e2e/config/local-*.json
/tests/e2e/config/local.json
/tests/e2e/config/default.json
/tests/e2e/env/config/default.json
/tests/e2e/docker
/tests/e2e/env/docker/wp-cli/initialize.sh
/tests/e2e/env/build/
/tests/e2e/env/build-module/
/tests/e2e/utils/build/
/tests/e2e/utils/build-module/
# Logs
/logs
tmp/
# Composer
/vendor/
/bin/composer/**/vendor/
/lib/vendor/
vendor/
bin/composer/**/vendor/
lib/vendor/
contributors.md
contributors.html
# Packages
/packages/*
!/packages/README.md
# Yarn
yarn.lock
# Screenshots for e2e tests failures
/screenshots/
# Editors
nbproject/private/
# Language files
i18n/languages/woocommerce.pot
# Test Results
test-results.json
# Build
build/
woocommerce.zip
# Admin Feature config
plugins/woocommerce/includes/react-admin/feature-config.php
# PHP lint
phpcs-report.xml

5
.husky/post-merge Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm install
pnpm nx affected --target="composer-install" --base=ORIG_HEAD --head=HEAD

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm exec lint-staged

4
.husky/pre-push Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
./bin/pre-push.sh

2
.nvmrc
View File

@ -1 +1 @@
v12
v16

3
.prettierrc.js Normal file
View File

@ -0,0 +1,3 @@
// Import the default config file and expose it in the project root.
// Useful for editor integrations.
module.exports = require( '@wordpress/prettier-config' );

View File

@ -1,3 +1,3 @@
{
"extends": "stylelint-config-wordpress",
"extends": "@wordpress/stylelint-config",
}

View File

@ -1,110 +0,0 @@
version: ~> 1.0
# Specifies that Travis should create builds for master and release branches and also tags.
branches:
only:
- master
- /^\d+\.\d+(\.\d+)?(-\S*)?$/
- /^release\//
language: php
os:
- linux
dist: xenial
# Test main supported versions of PHP against latest WP.
php:
- "7.0"
- "7.1"
- "7.2"
- "7.3"
- "7.4"
- "8.0"
env:
- WP_VERSION=latest WP_MULTISITE=0
# Additional tests against stable PHP (min version is 7.0)
# and code coverage report.
jobs:
fast_finish: true
include:
- name: "Core E2E Tests"
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
install:
- nvm install
- npm install
- composer install --no-dev
script:
- npm run build:assets
- npm run docker:up
- npm run test:e2e
after_script:
- npm run docker:down
- name: "WP Nightly"
php: "7.4"
env: WP_VERSION=nightly WP_MULTISITE=0
- name: "WP Latest - 1"
php: "7.2"
env: WP_VERSION=5.5 WP_MULTISITE=0
- name: "WP Latest - 2"
php: "7.2"
env: WP_VERSION=5.4 WP_MULTISITE=0
- name: "Code Standards"
php: "7.4"
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
- name: "Code Coverage"
php: "7.4"
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
allow_failures:
- php: "7.4"
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
# Git clone depth
# By default Travis CI clones repositories to a depth of 50 commits. Using a depth of 1 makes this step a bit faster.
git:
depth: 1
# Since Xenial services are not started by default, we need to instruct it below to start.
services:
- mysql
- docker
cache:
directories:
- $HOME/.composer/cache
# Composer 2.0.7 introduced a change that broke the jetpack autoloader in PHP 7.0 - 7.3.
before_install:
- composer self-update 2.0.6
install:
- export PATH="$HOME/.composer/vendor/bin:$PATH"
- |
# Remove Xdebug for a huge performance increase:
if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then
phpenv config-rm xdebug.ini
else
echo "xdebug.ini does not exist"
fi
- composer install
- |
if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
composer bin phpunit config --unset platform
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
fi
- |
# Install WP Test suite:
if [[ ! -z "$WP_VERSION" ]]; then
bash tests/bin/install.sh woocommerce_test root '' localhost $WP_VERSION
fi
script:
- bash tests/bin/phpunit.sh
- bash tests/bin/phpcs.sh
after_script:
- bash tests/bin/travis.sh after

29
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
"version": "2.0.0",
"tasks": [
{
"command": "pnpm tsc -b tsconfig.base.json",
"type": "shell",
"problemMatcher": [ "$tsc" ],
"label": "Typescript compile",
"detail": "Run tsc against tsconfig.base.json",
"runOptions": {
"runOn": "default"
}
},
{
"command": "pnpm tsc -b tsconfig.base.json --watch",
"type": "shell",
"problemMatcher": {
"base": "$tsc-watch",
"applyTo": "allDocuments"
},
"isBackground": true,
"label": "Incremental Typescript compile",
"detail": "Incremental background type checks",
"runOptions": {
"runOn": "folderOpen"
}
}
]
}

138
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,138 @@
# WooCommerce Development Setup with WP-ENV
Docker development setup for WooCommerce with WP-ENV.
## Prerequisites
Please install WP-ENV before getting started. You can find more about WP-ENV on [here](https://github.com/WordPress/gutenberg/tree/master/packages/env).
The following command installs WP-ENV globally.
`npm -g i @wordpress/env`
If you don't already have [pnpm](https://pnpm.io/installation) installed, you can quickly add it using NPM.
`npm install -g pnpm`
## Starting WP-ENV
1. Navigate to the root of WooCommerce source code.
2. Start the docker container by running `wp-env start`
You should see the following output
```
WordPress development site started at http://localhost:8888/
WordPress test site started at http://localhost:8889/
MySQL is listening on port 55003
```
The port # might be different depending on your `.wp-env.override.json` configuration.
## Getting Started with Developing
Once you have WP-ENV container up, we need to run a few commands to start developing.
1. Run `pnpm install` to install npm modules.
2. Run `pnpm nx build woocommerce` to build core.
3. Run `pnpm nx composer-install woocommerce` to install PHP dependencies.
If you don't have Composer available locally, run the following command. It runs the command in WP-ENV container.
`wp-env run composer composer install`
You might also want to run `pnpm start` to watch your CSS and JS changes if you are working on the frontend.
You're now ready to develop!
### Typescript Checking
Typescript is progressively being implemented in this repository, and you might come across some files that are `.ts` or `.tsx`. By default, a VSCode environment will run type checking on such files that are currently open.
As of now, some parts of the codebase that were imported from the Woocommerce-Admin repository, into the `plugins/woocommerce-admin/client` directory, still fail Typescript checking. This has been scheduled on the team's backlog to be fixed.
In order to run type checking across the entire repository, you can run this command in your shell, from the root of this repository:
```sh
pnpm tsc -b tsconfig.base.json
```
For better developer experience, the folder `.vscode/tasks.json` has two VSCode tasks to run these commands automatically as well as to parse the output and highlight the errors in the `Problems` tab and in the file explorer pane. The first task runs it once, the second one runs it in the background upon saving of any modified files. This task is also automatically prompted by VSCode to be run upon opening the folder.
## Using Xdebug
Please refer to [WP-ENV official README](https://github.com/WordPress/gutenberg/tree/master/packages/env#using-xdebug) section for setting up Xdebug.
## Overriding the Default Configuration
The default configuration comes with PHP 7.4, WooCommerce 5.0, and a few WordPress config values.
You can create `.wp-env.override.json` file and override the default configuration values.
You can find more about `.wp-env.override.json` configuration [here](https://github.com/WordPress/gutenberg/tree/master/packages/env#wp-envoverridejson).
**Example: Overriding PHP version to 8.0**
Create `.wp-env.override.json` in the root directory with the following content.
```json
{
"phpVersion": "8.0"
}
```
**Exampe: Adding a locally installed plugin**
Method 1 - Adding to the `plugins` array
Open the default `.wp-env.json` and copy `plugins` array and paste it into the `.wp-env.override.json` and add your locally installed plugin. Copying the default `plugins` is needed as WP-ENV does not merge the values of the `plugins`.
```json
{
"plugins": [
"./plugins/woocommerce",
"https://downloads.wordpress.org/plugin/wp-crontrol.1.10.0.zip"
]
}
```
Method 2 - Adding to the `mappings`
This method is simpler, but the plugin does not get activated on startup. You need to manually activate it yourself on the first startup.
```json
{
"mappings": {
"wp-content/plugins/wp-crontrol": "../woocommerce"
}
}
```
## Accessing MySQL
The MySQL port can change when you restart your container.
You can get the current MySQL port from the output of `wp-env start` command.
1. Open your choice of MySQL tool.
2. Use the following values to access the MySQL container.
| Name | Value |
| -------- | --------------------- |
| Host | 127.0.0.1 |
| Username | root |
| Password | password |
| Port | Port from the command |
## HOWTOs
##### How do I ssh into the container?
Run the following command to ssh into the container
`wp-env run wordpress /bin/bash`
You can run a command in the container with the following syntax. You can find more about on the `run` command [here](https://github.com/WordPress/gutenberg/tree/master/packages/env#wp-env-run-container-command)
Syntax:
`wp-env run :container-type :linux-command`

View File

@ -1,231 +0,0 @@
module.exports = function( grunt ) {
'use strict';
var sass = require( 'node-sass' );
grunt.initConfig({
// Setting folder templates.
dirs: {
css: 'assets/css',
fonts: 'assets/fonts',
images: 'assets/images',
js: 'assets/js',
php: 'includes'
},
// JavaScript linting with ESLint.
eslint: {
src: [
'<%= dirs.js %>/admin/*.js',
'!<%= dirs.js %>/admin/*.min.js',
'<%= dirs.js %>/frontend/*.js',
'!<%= dirs.js %>/frontend/*.min.js'
]
},
// Sass linting with Stylelint.
stylelint: {
options: {
configFile: '.stylelintrc'
},
all: [
'<%= dirs.css %>/*.scss',
'!<%= dirs.css %>/select2.scss'
]
},
// Minify .js files.
uglify: {
options: {
ie8: true,
parse: {
strict: false
},
output: {
comments : /@license|@preserve|^!/
}
},
js_assets: {
files: [{
expand: true,
cwd: '<%= dirs.js %>/',
src: [
'**/*.js',
'!**/*.min.js'
],
extDot: 'last',
dest: '<%= dirs.js %>',
ext: '.min.js'
}]
}
},
// Compile all .scss files.
sass: {
compile: {
options: {
implementation: sass,
sourceMap: 'none'
},
files: [{
expand: true,
cwd: '<%= dirs.css %>/',
src: ['*.scss'],
dest: '<%= dirs.css %>/',
ext: '.css'
}]
}
},
// Generate RTL .css files.
rtlcss: {
woocommerce: {
expand: true,
cwd: '<%= dirs.css %>',
src: [
'*.css',
'!select2.css',
'!*-rtl.css'
],
dest: '<%= dirs.css %>/',
ext: '-rtl.css'
}
},
// Minify all .css files.
cssmin: {
minify: {
files: [
{
expand: true,
cwd: '<%= dirs.css %>/',
src: ['*.css'],
dest: '<%= dirs.css %>/',
ext: '.css'
},
{
expand: true,
cwd: '<%= dirs.css %>/photoswipe/',
src: ['*.css', '!*.min.css'],
dest: '<%= dirs.css %>/photoswipe/',
ext: '.min.css'
},
{
expand: true,
cwd: '<%= dirs.css %>/photoswipe/default-skin/',
src: ['*.css', '!*.min.css'],
dest: '<%= dirs.css %>/photoswipe/default-skin/',
ext: '.min.css'
}
]
}
},
// Concatenate select2.css onto the admin.css files.
concat: {
admin: {
files: {
'<%= dirs.css %>/admin.css' : ['<%= dirs.css %>/select2.css', '<%= dirs.css %>/admin.css'],
'<%= dirs.css %>/admin-rtl.css' : ['<%= dirs.css %>/select2.css', '<%= dirs.css %>/admin-rtl.css']
}
}
},
// Watch changes for assets.
watch: {
css: {
files: ['<%= dirs.css %>/*.scss'],
tasks: ['sass', 'rtlcss', 'postcss', 'cssmin', 'concat']
},
js: {
files: [
'GruntFile.js',
'<%= dirs.js %>/**/*.js',
'!<%= dirs.js %>/**/*.min.js'
],
tasks: ['eslint','newer:uglify']
}
},
// PHP Code Sniffer.
phpcs: {
options: {
bin: 'vendor/bin/phpcs'
},
dist: {
src: [
'**/*.php', // Include all php files.
'!includes/api/legacy/**',
'!includes/libraries/**',
'!node_modules/**',
'!tests/cli/**',
'!tmp/**',
'!vendor/**'
]
}
},
// Autoprefixer.
postcss: {
options: {
processors: [
require( 'autoprefixer' )
]
},
dist: {
src: [
'<%= dirs.css %>/*.css'
]
}
}
});
// Load NPM tasks to be used here.
grunt.loadNpmTasks( 'grunt-sass' );
grunt.loadNpmTasks( 'grunt-phpcs' );
grunt.loadNpmTasks( 'grunt-rtlcss' );
grunt.loadNpmTasks( 'grunt-postcss' );
grunt.loadNpmTasks( 'grunt-stylelint' );
grunt.loadNpmTasks( 'gruntify-eslint' );
grunt.loadNpmTasks( 'grunt-contrib-uglify' );
grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
grunt.loadNpmTasks( 'grunt-contrib-concat' );
grunt.loadNpmTasks( 'grunt-contrib-copy' );
grunt.loadNpmTasks( 'grunt-contrib-watch' );
grunt.loadNpmTasks( 'grunt-contrib-clean' );
grunt.loadNpmTasks( 'grunt-newer' );
// Register tasks.
grunt.registerTask( 'default', [
'js',
'css'
]);
grunt.registerTask( 'js', [
'eslint',
'uglify:js_assets'
]);
grunt.registerTask( 'css', [
'sass',
'rtlcss',
'postcss',
'cssmin',
'concat'
]);
grunt.registerTask( 'assets', [
'js',
'css'
]);
grunt.registerTask( 'e2e-build', [
'uglify:js_assets',
'css'
]);
// Only an alias to 'default' task.
grunt.registerTask( 'dev', [
'default'
]);
};

View File

@ -5,8 +5,8 @@
<a href="https://packagist.org/packages/woocommerce/woocommerce"><img src="https://poser.pugx.org/woocommerce/woocommerce/v/stable" alt="Latest Stable Version"></a>
<img src="https://img.shields.io/wordpress/plugin/dt/woocommerce.svg" alt="WordPress.org downloads">
<img src="https://img.shields.io/wordpress/plugin/r/woocommerce.svg" alt="WordPress.org rating">
<a href="https://travis-ci.com/woocommerce/woocommerce"><img src="https://travis-ci.com/woocommerce/woocommerce.svg?branch=master" alt="Build Status"></a>
<a href="https://codecov.io/gh/woocommerce/woocommerce"><img src="https://codecov.io/gh/woocommerce/woocommerce/branch/master/graph/badge.svg" alt="codecov"></a>
<a href="https://github.com/woocommerce/woocommerce/actions/workflows/ci.yml"><img src="https://github.com/woocommerce/woocommerce/actions/workflows/ci.yml/badge.svg?branch=trunk" alt="Build Status"></a>
<a href="https://codecov.io/gh/woocommerce/woocommerce"><img src="https://codecov.io/gh/woocommerce/woocommerce/branch/trunk/graph/badge.svg" alt="codecov"></a>
</p>
Welcome to the WooCommerce repository on GitHub. Here you can browse the source, look at open issues and keep track of development. We recommend all developers to follow the [WooCommerce development blog](https://woocommerce.wordpress.com/) to stay up to date about everything happening in the project. You can also [follow @DevelopWC](https://twitter.com/DevelopWC) on Twitter for the latest development updates.
@ -18,6 +18,7 @@ If you are not a developer, please use the [WooCommerce plugin page](https://wor
* [WooCommerce Developer Documentation](https://github.com/woocommerce/woocommerce/wiki)
* [WooCommerce Code Reference](https://docs.woocommerce.com/wc-apidocs/)
* [WooCommerce REST API Docs](https://woocommerce.github.io/woocommerce-rest-api-docs/)
* [Setting up a development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment)
## Reporting Security Issues
To disclose a security issue to our team, [please submit a report via HackerOne here](https://hackerone.com/automattic/).
@ -34,4 +35,10 @@ This repository is not suitable for support. Please don't use our issue tracker
Support requests in issues on this repository will be closed on sight.
## Contributing to WooCommerce
If you have a patch or have stumbled upon an issue with WooCommerce core, you can contribute this back to the code. Please read our [contributor guidelines](https://github.com/woocommerce/woocommerce/blob/master/.github/CONTRIBUTING.md) for more information how you can do this.
If you have a patch or have stumbled upon an issue with WooCommerce core, you can contribute this back to the code. Please read our [contributor guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) for more information how you can do this.
<p align="center">
<br/><br/>
Made with 💜 by <a href="https://woocommerce.com/">WooCommerce</a>.<br/>
<a href="https://woocommerce.com/careers/">We're hiring</a>! Come work with us!
</p>

View File

@ -1,288 +0,0 @@
/**
* Deprecated
* Fallback for bourbon equivalent
*/
@mixin clearfix() {
*zoom: 1;
&::before,
&::after {
content: " ";
display: table;
}
&::after {
clear: both;
}
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin border_radius($radius: 4px) {
border-radius: $radius;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin border_radius_right($radius: 4px) {
border-top-right-radius: $radius;
border-bottom-right-radius: $radius;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin border_radius_left($radius: 4px) {
border-top-left-radius: $radius;
border-bottom-left-radius: $radius;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin border_radius_bottom($radius: 4px) {
border-bottom-left-radius: $radius;
border-bottom-right-radius: $radius;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin border_radius_top($radius: 4px) {
border-top-left-radius: $radius;
border-top-right-radius: $radius;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin opacity( $opacity: 0.75 ) {
opacity: $opacity;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin box_shadow($shadow_x: 3px, $shadow_y: 3px, $shadow_rad: 3px, $shadow_in: 3px, $shadow_color: #888) {
box-shadow: $shadow_x $shadow_y $shadow_rad $shadow_in $shadow_color;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin inset_box_shadow($shadow_x: 3px, $shadow_y: 3px, $shadow_rad: 3px, $shadow_in: 3px, $shadow_color: #888) {
box-shadow: inset $shadow_x $shadow_y $shadow_rad $shadow_in $shadow_color;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin text_shadow($shadow_x: 3px, $shadow_y: 3px, $shadow_rad: 3px, $shadow_color: #fff) {
text-shadow: $shadow_x $shadow_y $shadow_rad $shadow_color;
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin vertical_gradient($from: #000, $to: #fff) {
background-color: $from;
background: -webkit-linear-gradient($from, $to);
}
/**
* Deprecated
* Vendor prefix no longer required.
*/
@mixin transition($selector: all, $animation: ease-in-out, $duration: 0.2s) {
transition: $selector $animation $duration;
}
/**
* Deprecated
* Use bourbon mixin instead `@include transform(scale(1.5));`
*/
@mixin scale($ratio: 1.5) {
-webkit-transform: scale($ratio);
transform: scale($ratio);
}
/**
* Deprecated
* Use bourbon mixin instead `@include box-sizing(border-box);`
*/
@mixin borderbox() {
box-sizing: border-box;
}
@mixin darkorlighttextshadow($a, $opacity: 0.8) {
@if lightness($a) >= 65% {
@include text_shadow(0, -1px, 0, rgba(0, 0, 0, $opacity));
}
@else {
@include text_shadow(0, 1px, 0, rgba(255, 255, 255, $opacity));
}
}
/**
* Objects
*/
@mixin menu() {
@include clearfix();
li {
display: inline-block;
}
}
@mixin mediaright() {
@include clearfix();
img {
float: right;
height: auto;
}
}
@mixin medialeft() {
@include clearfix();
img {
float: right;
height: auto;
}
}
@mixin ir() {
display: block;
text-indent: -9999px;
position: relative;
height: 1em;
width: 1em;
}
@mixin icon( $glyph: "\e001" ) {
font-family: "WooCommerce";
speak: never;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
margin: 0;
text-indent: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
content: $glyph;
}
@mixin icon_dashicons( $glyph: "\f333" ) {
font-family: "Dashicons";
speak: never;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
margin: 0;
text-indent: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
content: $glyph;
}
@mixin iconbefore( $glyph: "\e001" ) {
font-family: "WooCommerce";
speak: never;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
margin-right: 0.618em;
content: $glyph;
text-decoration: none;
}
@mixin iconbeforedashicons( $glyph: "\f333" ) {
font-family: "Dashicons";
speak: never;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
content: $glyph;
text-decoration: none;
}
@mixin iconafter( $glyph: "\e001" ) {
font-family: "WooCommerce";
speak: never;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
margin-left: 0.618em;
content: $glyph;
text-decoration: none;
}
@mixin loader() {
&::before {
height: 1em;
width: 1em;
display: block;
position: absolute;
top: 50%;
left: 50%;
margin-left: -0.5em;
margin-top: -0.5em;
content: "";
animation: spin 1s ease-in-out infinite;
background: url("../images/icons/loader.svg") center center;
background-size: cover;
line-height: 1;
text-align: center;
font-size: 2em;
color: rgba(#000, 0.75);
}
}
@mixin inversebuttoncolors {
background-color: transparent !important;
color: var(--button--color-text-hover) !important;
&:hover {
background-color: var(--button--color-background) !important;
color: var(--button--color-text) !important;
text-decoration: none !important;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#4267b2;}.cls-2{fill:#fff;}</style></defs><title>flogo_RGB_HEX-512</title><path class="cls-1" d="M483.74,0H28.26A28.26,28.26,0,0,0,0,28.26V483.74A28.26,28.26,0,0,0,28.26,512H273.5V314H207V236.5h66.5v-57c0-66.13,40.39-102.14,99.38-102.14,28.25,0,52.54,2.1,59.62,3v69.11l-40.68,0c-32.1,0-38.32,15.25-38.32,37.64V236.5h76.74l-10,77.5H353.5V512H483.74A28.26,28.26,0,0,0,512,483.74V28.26A28.26,28.26,0,0,0,483.74,0Z"/><path id="f" class="cls-2" d="M353.5,512V314h66.75l10-77.5H353.5V187.14c0-22.39,6.22-37.64,38.32-37.64l40.68,0V80.37c-7.08-.94-31.37-3-59.62-3-59,0-99.38,36-99.38,102.14v57H207V314h66.5V512Z"/></svg>

Before

Width:  |  Height:  |  Size: 732 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1 +0,0 @@
<svg viewBox="0 0 367 300" xmlns="http://www.w3.org/2000/svg"><path d="M251 135.1c-6.4 4.9-14.4 7.8-23 7.8-8.9 0-17-3.1-23.5-8.1-6.5 5.1-14.6 8.1-23.5 8.1s-17.1-3.1-23.6-8.2c-6.5 5.1-14.7 8.2-23.6 8.2s-17-3.1-23.5-8.1c-6.5 5.1-14.6 8.1-23.5 8.1-2 0-4-.2-6-.5v73.4c0 6.3 5.7 12 12 12h54.8c6.3 0 10.9-5.7 10.9-12v-30.7c6.9 2.2 14.1 3.5 22.8 3.5s17.1-1.3 22.8-3.5v30.7c0 6.3 5.7 12 12 12h54.8c6.3 0 10.9-5.7 10.9-12V142c-2.6.6-5.3.9-8 .9-8.4-.1-16.4-3-22.8-7.8zm-115.5 49.8c0 5-4.1 9.1-9.1 9.1h-14.1c-5 0-9.1-4.1-9.1-9.1v-14.1c0-5 4.1-9.1 9.1-9.1h14.1c5 0 9.1 4.1 9.1 9.1zm123.3 0c0 5-4.1 9.1-9.1 9.1h-14.1c-5 0-9.1-4.1-9.1-9.1v-14.1c0-5 4.1-9.1 9.1-9.1h14.1c5 0 9.1 4.1 9.1 9.1zm36.1-85l-30.1-30.1c-3.9-3.9-11.6-7.1-17.1-7.1H113.3c-5.5 0-13.2 3.2-17.1 7.1L66.1 99.9l-7 7c.9 12.5 10 22.7 21.9 25.3h.1c.4.1.9.2 1.3.3h.1c.4.1.8.1 1.3.2h.2c.4 0 .8.1 1.2.1H87c.7 0 1.4 0 2-.1.2 0 .4 0 .6-.1.5 0 .9-.1 1.4-.2.2 0 .5-.1.7-.1.4-.1.8-.1 1.2-.2.2-.1.5-.1.7-.2l1.2-.3c.2-.1.4-.1.6-.2.5-.2 1-.3 1.5-.5.1 0 .2-.1.4-.1.6-.2 1.2-.5 1.7-.8.2-.1.4-.2.5-.3.4-.2.8-.4 1.1-.6.2-.1.4-.3.7-.4l.9-.6c.1-.1.2-.1.3-.2 3.1-2.1 5.7-4.8 7.7-7.9 5 7.7 13.6 12.7 23.5 12.7s18.6-5.1 23.6-12.9c2 3.2 4.7 5.9 7.8 8.1.5.4 1.1.7 1.6 1 .1.1.2.1.3.2 1.1.6 2.2 1.2 3.4 1.6.1 0 .2.1.3.1l1.8.6c.6.2 1.2.3 1.8.5.1 0 .3.1.4.1.6.1 1.2.3 1.8.4s1.3.2 1.9.2h.5c.6 0 1.3.1 1.9.1s1.3 0 1.9-.1h.5c.6-.1 1.3-.1 1.9-.2s1.2-.2 1.8-.4c.1 0 .3-.1.4-.1.6-.1 1.2-.3 1.8-.5l1.8-.6c.1 0 .2-.1.3-.1 1.2-.5 2.3-1 3.4-1.6.1-.1.2-.1.3-.2.5-.3 1.1-.7 1.6-1 3.1-2.1 5.7-4.8 7.7-7.9 5 7.7 13.6 12.7 23.5 12.7 9.5 0 18-4.8 23-12.1 2 2.8 4.4 5.3 7.3 7.2.5.4 1.1.7 1.6 1 .1.1.2.1.3.2 1.1.6 2.2 1.2 3.4 1.6.1 0 .2.1.3.1l1.8.6c.6.2 1.2.3 1.8.5.1 0 .3.1.4.1.6.1 1.2.3 1.8.4s1.3.2 1.9.2h.5c.6 0 1.3.1 1.9.1s1.2 0 1.9-.1h.4c.6 0 1.2-.1 1.7-.2h.1c.6-.1 1.3-.2 1.9-.4h.1c11.9-2.7 20.9-12.8 21.8-25.3zM150 107c-.1 8.8-7.3 15.9-16.1 15.9s-16-7.1-16.1-15.9l15-37h25zm77.8 15.9c-8.8 0-16-7.1-16.1-15.9l-7.8-37h25l15 37c-.1 8.8-7.3 15.9-16.1 15.9z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="266 396.7 25.5 23.6"><path fill="#FFF" d="M266 402.5c0-3.5 2.2-5.8 5.5-5.8s5.5 2.3 5.5 5.8-2.2 5.8-5.5 5.8-5.5-2.3-5.5-5.8zm2.9 17.3l8.5-11.8 6.2-10.7h5.2l-8.6 11.9-6 10.7h-5.3zm4.1-17.3c0-1.6-.5-2.5-1.5-2.5s-1.5.9-1.5 2.5c0 1.5.6 2.5 1.5 2.5.9-.1 1.5-1 1.5-2.5zm7.5 12c0-3.5 2.2-5.8 5.5-5.8s5.5 2.3 5.5 5.8-2.2 5.8-5.5 5.8-5.5-2.3-5.5-5.8zm7 0c0-1.6-.6-2.5-1.5-2.5s-1.5.9-1.5 2.5c0 1.5.6 2.5 1.5 2.5.9-.1 1.5-1 1.5-2.5z"/></svg>

Before

Width:  |  Height:  |  Size: 478 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.5 27.78"><defs><style>.cls-1,.cls-2{fill:#fff;}.cls-2{stroke:#fff;stroke-linecap:round;stroke-miterlimit:10;stroke-width:1.5px;}</style></defs><title>Asset 5</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M4.8,16.57H6.61a2,2,0,0,1,2,2v8.55a0,0,0,0,1,0,0H2.8a0,0,0,0,1,0,0V18.57A2,2,0,0,1,4.8,16.57Z"/><path class="cls-1" d="M14.85,7.53h1.81a2,2,0,0,1,2,2V27.12a0,0,0,0,1,0,0H12.85a0,0,0,0,1,0,0V9.53A2,2,0,0,1,14.85,7.53Z"/><path class="cls-1" d="M24.89,0H26.7a2,2,0,0,1,2,2V27.12a0,0,0,0,1,0,0H22.89a0,0,0,0,1,0,0V2A2,2,0,0,1,24.89,0Z"/><line class="cls-2" x1="0.75" y1="27.03" x2="30.75" y2="27.03"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,158 +0,0 @@
/*global jQuery, Backbone, _, woocommerce_admin_api_keys, wcSetClipboard, wcClearClipboard */
(function( $ ) {
var APIView = Backbone.View.extend({
/**
* Element
*
* @param {Object} '#key-fields'
*/
el: $( '#key-fields' ),
/**
* Events
*
* @type {Object}
*/
events: {
'click input#update_api_key': 'saveKey'
},
/**
* Initialize actions
*/
initialize: function(){
_.bindAll( this, 'saveKey' );
},
/**
* Init jQuery.BlockUI
*/
block: function() {
$( this.el ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
},
/**
* Remove jQuery.BlockUI
*/
unblock: function() {
$( this.el ).unblock();
},
/**
* Init TipTip
*/
initTipTip: function( css_class ) {
$( document.body )
.on( 'click', css_class, function( evt ) {
evt.preventDefault();
if ( ! document.queryCommandSupported( 'copy' ) ) {
$( css_class ).parent().find( 'input' ).focus().select();
$( '#copy-error' ).text( woocommerce_admin_api_keys.clipboard_failed );
} else {
$( '#copy-error' ).text( '' );
wcClearClipboard();
wcSetClipboard( $( this ).prev( 'input' ).val().trim(), $( css_class ) );
}
} )
.on( 'aftercopy', css_class, function() {
$( '#copy-error' ).text( '' );
$( css_class ).tipTip( {
'attribute': 'data-tip',
'activation': 'focus',
'fadeIn': 50,
'fadeOut': 50,
'delay': 0
} ).focus();
} )
.on( 'aftercopyerror', css_class, function() {
$( css_class ).parent().find( 'input' ).focus().select();
$( '#copy-error' ).text( woocommerce_admin_api_keys.clipboard_failed );
} );
},
/**
* Create qrcode
*
* @param {string} consumer_key
* @param {string} consumer_secret
*/
createQRCode: function( consumer_key, consumer_secret ) {
$( '#keys-qrcode' ).qrcode({
text: consumer_key + '|' + consumer_secret,
width: 120,
height: 120
});
},
/**
* Save API Key using ajax
*
* @param {Object} e
*/
saveKey: function( e ) {
e.preventDefault();
var self = this;
self.block();
Backbone.ajax({
method: 'POST',
dataType: 'json',
url: woocommerce_admin_api_keys.ajax_url,
data: {
action: 'woocommerce_update_api_key',
security: woocommerce_admin_api_keys.update_api_nonce,
key_id: $( '#key_id', self.el ).val(),
description: $( '#key_description', self.el ).val(),
user: $( '#key_user', self.el ).val(),
permissions: $( '#key_permissions', self.el ).val()
},
success: function( response ) {
$( '.wc-api-message', self.el ).remove();
if ( response.success ) {
var data = response.data;
$( 'h2, h3', self.el ).first().append( '<div class="wc-api-message updated"><p>' + data.message + '</p></div>' );
if ( 0 < data.consumer_key.length && 0 < data.consumer_secret.length ) {
$( '#api-keys-options', self.el ).remove();
$( 'p.submit', self.el ).empty().append( data.revoke_url );
var template = wp.template( 'api-keys-template' );
$( 'p.submit', self.el ).before( template({
consumer_key: data.consumer_key,
consumer_secret: data.consumer_secret
}) );
self.createQRCode( data.consumer_key, data.consumer_secret );
self.initTipTip( '.copy-key' );
self.initTipTip( '.copy-secret' );
} else {
$( '#key_description', self.el ).val( data.description );
$( '#key_user', self.el ).val( data.user_id );
$( '#key_permissions', self.el ).val( data.permissions );
}
} else {
$( 'h2, h3', self.el )
.first()
.append( '<div class="wc-api-message error"><p>' + response.data.message + '</p></div>' );
}
self.unblock();
}
});
}
});
new APIView();
})( jQuery );

View File

@ -1,147 +0,0 @@
/*global jQuery, Backbone, _ */
( function( $, Backbone, _ ) {
'use strict';
/**
* WooCommerce Backbone Modal plugin
*
* @param {object} options
*/
$.fn.WCBackboneModal = function( options ) {
return this.each( function() {
( new $.WCBackboneModal( $( this ), options ) );
});
};
/**
* Initialize the Backbone Modal
*
* @param {object} element [description]
* @param {object} options [description]
*/
$.WCBackboneModal = function( element, options ) {
// Set settings
var settings = $.extend( {}, $.WCBackboneModal.defaultOptions, options );
if ( settings.template ) {
new $.WCBackboneModal.View({
target: settings.template,
string: settings.variable
});
}
};
/**
* Set default options
*
* @type {object}
*/
$.WCBackboneModal.defaultOptions = {
template: '',
variable: {}
};
/**
* Create the Backbone Modal
*
* @return {null}
*/
$.WCBackboneModal.View = Backbone.View.extend({
tagName: 'div',
id: 'wc-backbone-modal-dialog',
_target: undefined,
_string: undefined,
events: {
'click .modal-close': 'closeButton',
'click #btn-ok' : 'addButton',
'touchstart #btn-ok': 'addButton',
'keydown' : 'keyboardActions'
},
resizeContent: function() {
var $content = $( '.wc-backbone-modal-content' ).find( 'article' );
var max_h = $( window ).height() * 0.75;
$content.css({
'max-height': max_h + 'px'
});
},
initialize: function( data ) {
var view = this;
this._target = data.target;
this._string = data.string;
_.bindAll( this, 'render' );
this.render();
$( window ).resize(function() {
view.resizeContent();
});
},
render: function() {
var template = wp.template( this._target );
this.$el.append(
template( this._string )
);
$( document.body ).css({
'overflow': 'hidden'
}).append( this.$el );
this.resizeContent();
this.$( '.wc-backbone-modal-content' ).attr( 'tabindex' , '0' ).focus();
$( document.body ).trigger( 'init_tooltips' );
$( document.body ).trigger( 'wc_backbone_modal_loaded', this._target );
},
closeButton: function( e ) {
e.preventDefault();
$( document.body ).trigger( 'wc_backbone_modal_before_remove', this._target );
this.undelegateEvents();
$( document ).off( 'focusin' );
$( document.body ).css({
'overflow': 'auto'
});
this.remove();
$( document.body ).trigger( 'wc_backbone_modal_removed', this._target );
},
addButton: function( e ) {
$( document.body ).trigger( 'wc_backbone_modal_response', [ this._target, this.getFormData() ] );
this.closeButton( e );
},
getFormData: function() {
var data = {};
$( document.body ).trigger( 'wc_backbone_modal_before_update', this._target );
$.each( $( 'form', this.$el ).serializeArray(), function( index, item ) {
if ( item.name.indexOf( '[]' ) !== -1 ) {
item.name = item.name.replace( '[]', '' );
data[ item.name ] = $.makeArray( data[ item.name ] );
data[ item.name ].push( item.value );
} else {
data[ item.name ] = item.value;
}
});
return data;
},
keyboardActions: function( e ) {
var button = e.keyCode || e.which;
// Enter key
if (
13 === button &&
! ( e.target.tagName && ( e.target.tagName.toLowerCase() === 'input' || e.target.tagName.toLowerCase() === 'textarea' ) )
) {
this.addButton( e );
}
// ESC key
if ( 27 === button ) {
this.closeButton( e );
}
}
});
}( jQuery, Backbone, _ ));

View File

@ -1,449 +0,0 @@
/* global marketplace_suggestions, ajaxurl, Cookies */
( function( $, marketplace_suggestions, ajaxurl ) {
$( function() {
if ( 'undefined' === typeof marketplace_suggestions ) {
return;
}
// Stand-in wcTracks.recordEvent in case tracks is not available (for any reason).
window.wcTracks = window.wcTracks || {};
window.wcTracks.recordEvent = window.wcTracks.recordEvent || function() { };
// Tracks events sent in this file:
// - marketplace_suggestion_displayed
// - marketplace_suggestion_clicked
// - marketplace_suggestion_dismissed
// All are prefixed by {WC_Tracks::PREFIX}.
// All have one property for `suggestionSlug`, to identify the specific suggestion message.
// Dismiss the specified suggestion from the UI, and save the dismissal in settings.
function dismissSuggestion( context, product, promoted, url, suggestionSlug ) {
// hide the suggestion in the UI
var selector = '[data-suggestion-slug=' + suggestionSlug + ']';
$( selector ).fadeOut( function() {
$( this ).remove();
tidyProductEditMetabox();
} );
// save dismissal in user settings
jQuery.post(
ajaxurl,
{
'action': 'woocommerce_add_dismissed_marketplace_suggestion',
'_wpnonce': marketplace_suggestions.dismiss_suggestion_nonce,
'slug': suggestionSlug
}
);
// if this is a high-use area, delay new suggestion that area for a short while
var highUseSuggestionContexts = [ 'products-list-inline' ];
if ( _.contains( highUseSuggestionContexts, context ) ) {
// snooze suggestions in that area for 2 days
var contextSnoozeCookie = 'woocommerce_snooze_suggestions__' + context;
Cookies.set( contextSnoozeCookie, 'true', { expires: 2 } );
// keep track of how often this area gets dismissed in a cookie
var contextDismissalCountCookie = 'woocommerce_dismissed_suggestions__' + context;
var previousDismissalsInThisContext = parseInt( Cookies.get( contextDismissalCountCookie ), 10 ) || 0;
Cookies.set( contextDismissalCountCookie, previousDismissalsInThisContext + 1, { expires: 31 } );
}
window.wcTracks.recordEvent( 'marketplace_suggestion_dismissed', {
suggestion_slug: suggestionSlug,
context: context,
product: product || '',
promoted: promoted || '',
target: url || ''
} );
}
// Render DOM element for suggestion dismiss button.
function renderDismissButton( context, product, promoted, url, suggestionSlug ) {
var dismissButton = document.createElement( 'a' );
dismissButton.classList.add( 'suggestion-dismiss' );
dismissButton.setAttribute( 'title', marketplace_suggestions.i18n_marketplace_suggestions_dismiss_tooltip );
dismissButton.setAttribute( 'href', '#' );
dismissButton.onclick = function( event ) {
event.preventDefault();
dismissSuggestion( context, product, promoted, url, suggestionSlug );
};
return dismissButton;
}
function addURLParameters( context, url ) {
var urlParams = marketplace_suggestions.in_app_purchase_params;
urlParams.utm_source = 'unknown';
urlParams.utm_campaign = 'marketplacesuggestions';
urlParams.utm_medium = 'product';
var sourceContextMap = {
'productstable': [
'products-list-inline'
],
'productsempty': [
'products-list-empty-header',
'products-list-empty-footer',
'products-list-empty-body'
],
'ordersempty': [
'orders-list-empty-header',
'orders-list-empty-footer',
'orders-list-empty-body'
],
'editproduct': [
'product-edit-meta-tab-header',
'product-edit-meta-tab-footer',
'product-edit-meta-tab-body'
]
};
var utmSource = _.findKey( sourceContextMap, function( sourceInfo ) {
return _.contains( sourceInfo, context );
} );
if ( utmSource ) {
urlParams.utm_source = utmSource;
}
return url + '?' + jQuery.param( urlParams );
}
// Render DOM element for suggestion linkout, optionally with button style.
function renderLinkout( context, product, promoted, slug, url, text, isButton ) {
var linkoutButton = document.createElement( 'a' );
var utmUrl = addURLParameters( context, url );
linkoutButton.setAttribute( 'href', utmUrl );
// By default, CTA links should open in same tab (and feel integrated with Woo).
// Exception: when editing products, use new tab. User may have product edits
// that need to be saved.
var newTabContexts = [
'product-edit-meta-tab-header',
'product-edit-meta-tab-footer',
'product-edit-meta-tab-body'
];
if ( _.includes( newTabContexts, context ) ) {
linkoutButton.setAttribute( 'target', 'blank' );
}
linkoutButton.textContent = text;
linkoutButton.onclick = function() {
window.wcTracks.recordEvent( 'marketplace_suggestion_clicked', {
suggestion_slug: slug,
context: context,
product: product || '',
promoted: promoted || '',
target: url || ''
} );
};
if ( isButton ) {
linkoutButton.classList.add( 'button' );
} else {
linkoutButton.classList.add( 'linkout' );
var linkoutIcon = document.createElement( 'span' );
linkoutIcon.classList.add( 'dashicons', 'dashicons-external' );
linkoutButton.appendChild( linkoutIcon );
}
return linkoutButton;
}
// Render DOM element for suggestion icon image.
function renderSuggestionIcon( iconUrl ) {
if ( ! iconUrl ) {
return null;
}
var image = document.createElement( 'img' );
image.src = iconUrl;
image.classList.add( 'marketplace-suggestion-icon' );
return image;
}
// Render DOM elements for suggestion content.
function renderSuggestionContent( slug, title, copy ) {
var container = document.createElement( 'div' );
container.classList.add( 'marketplace-suggestion-container-content' );
if ( title ) {
var titleHeading = document.createElement( 'h4' );
titleHeading.textContent = title;
container.appendChild( titleHeading );
}
if ( copy ) {
var body = document.createElement( 'p' );
body.textContent = copy;
container.appendChild( body );
}
// Conditionally add in a Manage suggestions link to product edit
// metabox footer (based on suggestion slug).
var slugsWithManage = [
'product-edit-empty-footer-browse-all',
'product-edit-meta-tab-footer-browse-all'
];
if ( -1 !== slugsWithManage.indexOf( slug ) ) {
container.classList.add( 'has-manage-link' );
var manageSuggestionsLink = document.createElement( 'a' );
manageSuggestionsLink.classList.add( 'marketplace-suggestion-manage-link', 'linkout' );
manageSuggestionsLink.setAttribute(
'href',
marketplace_suggestions.manage_suggestions_url
);
manageSuggestionsLink.textContent = marketplace_suggestions.i18n_marketplace_suggestions_manage_suggestions;
container.appendChild( manageSuggestionsLink );
}
return container;
}
// Render DOM elements for suggestion call-to-action button or link with dismiss 'x'.
function renderSuggestionCTA( context, product, promoted, slug, url, linkText, linkIsButton, allowDismiss ) {
var container = document.createElement( 'div' );
if ( ! linkText ) {
linkText = marketplace_suggestions.i18n_marketplace_suggestions_default_cta;
}
container.classList.add( 'marketplace-suggestion-container-cta' );
if ( url && linkText ) {
var linkoutElement = renderLinkout( context, product, promoted, slug, url, linkText, linkIsButton );
container.appendChild( linkoutElement );
}
if ( allowDismiss ) {
container.appendChild( renderDismissButton( context, product, promoted, url, slug ) );
}
return container;
}
// Render a "list item" style suggestion.
// These are used in onboarding style contexts, e.g. products list empty state.
function renderListItem( context, product, promoted, slug, iconUrl, title, copy, url, linkText, linkIsButton, allowDismiss ) {
var container = document.createElement( 'div' );
container.classList.add( 'marketplace-suggestion-container' );
container.dataset.suggestionSlug = slug;
var icon = renderSuggestionIcon( iconUrl );
if ( icon ) {
container.appendChild( icon );
}
container.appendChild(
renderSuggestionContent( slug, title, copy )
);
container.appendChild(
renderSuggestionCTA( context, product, promoted, slug, url, linkText, linkIsButton, allowDismiss )
);
return container;
}
// Filter suggestion data to remove less-relevant suggestions.
function getRelevantPromotions( marketplaceSuggestionsApiData, displayContext ) {
// select based on display context
var promos = _.filter( marketplaceSuggestionsApiData, function( promo ) {
if ( _.isArray( promo.context ) ) {
return _.contains( promo.context, displayContext );
}
return ( displayContext === promo.context );
} );
// hide promos the user has dismissed
promos = _.filter( promos, function( promo ) {
return ! _.contains( marketplace_suggestions.dismissed_suggestions, promo.slug );
} );
// hide promos for things the user already has installed
promos = _.filter( promos, function( promo ) {
return ! _.contains( marketplace_suggestions.active_plugins, promo.product );
} );
// hide promos that are not applicable based on user's installed extensions
promos = _.filter( promos, function( promo ) {
if ( ! promo['show-if-active'] ) {
// this promotion is relevant to all
return true;
}
// if the user has any of the prerequisites, show the promo
return ( _.intersection( marketplace_suggestions.active_plugins, promo['show-if-active'] ).length > 0 );
} );
return promos;
}
// Show and hide page elements dependent on suggestion state.
function hidePageElementsForSuggestionState( usedSuggestionsContexts ) {
var showingEmptyStateSuggestions = _.intersection(
usedSuggestionsContexts,
[ 'products-list-empty-body', 'orders-list-empty-body' ]
).length > 0;
// Streamline onboarding UI if we're in 'empty state' welcome mode.
if ( showingEmptyStateSuggestions ) {
$( '#screen-meta-links' ).hide();
$( '#wpfooter' ).hide();
}
// Hide the header & footer, they don't make sense without specific promotion content
if ( ! showingEmptyStateSuggestions ) {
$( '.marketplace-suggestions-container[data-marketplace-suggestions-context="products-list-empty-header"]' ).hide();
$( '.marketplace-suggestions-container[data-marketplace-suggestions-context="products-list-empty-footer"]' ).hide();
$( '.marketplace-suggestions-container[data-marketplace-suggestions-context="orders-list-empty-header"]' ).hide();
$( '.marketplace-suggestions-container[data-marketplace-suggestions-context="orders-list-empty-footer"]' ).hide();
}
}
// Streamline the product edit suggestions tab dependent on what's visible.
function tidyProductEditMetabox() {
var productMetaboxSuggestions = $(
'.marketplace-suggestions-container[data-marketplace-suggestions-context="product-edit-meta-tab-body"]'
).children();
if ( 0 >= productMetaboxSuggestions.length ) {
var metaboxSuggestionsUISelector =
'.marketplace-suggestions-container[data-marketplace-suggestions-context="product-edit-meta-tab-body"]';
metaboxSuggestionsUISelector +=
', .marketplace-suggestions-container[data-marketplace-suggestions-context="product-edit-meta-tab-header"]';
metaboxSuggestionsUISelector +=
', .marketplace-suggestions-container[data-marketplace-suggestions-context="product-edit-meta-tab-footer"]';
$( metaboxSuggestionsUISelector ).fadeOut( {
complete: function() {
$( '.marketplace-suggestions-metabox-nosuggestions-placeholder' ).fadeIn();
}
} );
}
}
function addManageSuggestionsTracksHandler() {
$( 'a.marketplace-suggestion-manage-link' ).on( 'click', function() {
window.wcTracks.recordEvent( 'marketplace_suggestions_manage_clicked' );
} );
}
function isContextHiddenOnPageLoad( context ) {
// Some suggestions are not visible on page load;
// e.g. the user reveals them by selecting a tab.
var revealableSuggestionsContexts = [
'product-edit-meta-tab-header',
'product-edit-meta-tab-body',
'product-edit-meta-tab-footer'
];
return _.includes( revealableSuggestionsContexts, context );
}
// track the current product data tab to avoid over-tracking suggestions
var currentTab = false;
// Render suggestion data in appropriate places in UI.
function displaySuggestions( marketplaceSuggestionsApiData ) {
var usedSuggestionsContexts = [];
// iterate over all suggestions containers, rendering promos
$( '.marketplace-suggestions-container' ).each( function() {
// determine the context / placement we're populating
var context = this.dataset.marketplaceSuggestionsContext;
// find promotions that target this context
var promos = getRelevantPromotions( marketplaceSuggestionsApiData, context );
// shuffle/randomly select five suggestions to display
var suggestionsToDisplay = _.sample( promos, 5 );
// render the promo content
for ( var i in suggestionsToDisplay ) {
var linkText = suggestionsToDisplay[ i ]['link-text'];
var linkoutIsButton = true;
if ( suggestionsToDisplay[ i ]['link-text'] ) {
linkText = suggestionsToDisplay[ i ]['link-text'];
linkoutIsButton = false;
}
// dismiss is allowed by default
var allowDismiss = true;
if ( suggestionsToDisplay[ i ]['allow-dismiss'] === false ) {
allowDismiss = false;
}
var content = renderListItem(
context,
suggestionsToDisplay[ i ].product,
suggestionsToDisplay[ i ].promoted,
suggestionsToDisplay[ i ].slug,
suggestionsToDisplay[ i ].icon,
suggestionsToDisplay[ i ].title,
suggestionsToDisplay[ i ].copy,
suggestionsToDisplay[ i ].url,
linkText,
linkoutIsButton,
allowDismiss
);
$( this ).append( content );
$( this ).addClass( 'showing-suggestion' );
usedSuggestionsContexts.push( context );
if ( ! isContextHiddenOnPageLoad( context ) ) {
// Fire 'displayed' tracks events for immediately visible suggestions.
window.wcTracks.recordEvent( 'marketplace_suggestion_displayed', {
suggestion_slug: suggestionsToDisplay[ i ].slug,
context: context,
product: suggestionsToDisplay[ i ].product || '',
promoted: suggestionsToDisplay[ i ].promoted || '',
target: suggestionsToDisplay[ i ].url || ''
} );
}
}
// Track when suggestions are displayed (and not already visible).
$( 'ul.product_data_tabs li.marketplace-suggestions_options a' ).on( 'click', function( e ) {
e.preventDefault();
if ( '#marketplace_suggestions' === currentTab ) {
return;
}
if ( ! isContextHiddenOnPageLoad( context ) ) {
// We've already fired 'displayed' event above.
return;
}
for ( var i in suggestionsToDisplay ) {
window.wcTracks.recordEvent( 'marketplace_suggestion_displayed', {
suggestion_slug: suggestionsToDisplay[ i ].slug,
context: context,
product: suggestionsToDisplay[ i ].product || '',
promoted: suggestionsToDisplay[ i ].promoted || '',
target: suggestionsToDisplay[ i ].url || ''
} );
}
} );
} );
hidePageElementsForSuggestionState( usedSuggestionsContexts );
tidyProductEditMetabox();
}
if ( marketplace_suggestions.suggestions_data ) {
displaySuggestions( marketplace_suggestions.suggestions_data );
// track the current product data tab to avoid over-reporting suggestion views
$( 'ul.product_data_tabs' ).on( 'click', 'li a', function( e ) {
e.preventDefault();
currentTab = $( this ).attr( 'href' );
} );
}
addManageSuggestionsTracksHandler();
});
})( jQuery, marketplace_suggestions, ajaxurl );

View File

@ -1,70 +0,0 @@
/* global woocommerce_admin_meta_boxes_coupon */
jQuery(function( $ ) {
/**
* Coupon actions
*/
var wc_meta_boxes_coupon_actions = {
/**
* Initialize variations actions
*/
init: function() {
$( 'select#discount_type' )
.on( 'change', this.type_options )
.trigger( 'change' );
this.insert_generate_coupon_code_button();
$( '.button.generate-coupon-code' ).on( 'click', this.generate_coupon_code );
},
/**
* Show/hide fields by coupon type options
*/
type_options: function() {
// Get value
var select_val = $( this ).val();
if ( 'percent' === select_val ) {
$( '#coupon_amount' ).removeClass( 'wc_input_price' ).addClass( 'wc_input_decimal' );
} else {
$( '#coupon_amount' ).removeClass( 'wc_input_decimal' ).addClass( 'wc_input_price' );
}
if ( select_val !== 'fixed_cart' ) {
$( '.limit_usage_to_x_items_field' ).show();
} else {
$( '.limit_usage_to_x_items_field' ).hide();
}
},
/**
* Insert generate coupon code buttom HTML.
*/
insert_generate_coupon_code_button: function() {
$( '.post-type-shop_coupon' ).find( '#title' ).after(
'<a href="#" class="button generate-coupon-code">' + woocommerce_admin_meta_boxes_coupon.generate_button_text + '</a>'
);
},
/**
* Generate a random coupon code
*/
generate_coupon_code: function( e ) {
e.preventDefault();
var $coupon_code_field = $( '#title' ),
$coupon_code_label = $( '#title-prompt-text' ),
$result = '';
for ( var i = 0; i < woocommerce_admin_meta_boxes_coupon.char_length; i++ ) {
$result += woocommerce_admin_meta_boxes_coupon.characters.charAt(
Math.floor( Math.random() * woocommerce_admin_meta_boxes_coupon.characters.length )
);
}
$result = woocommerce_admin_meta_boxes_coupon.prefix + $result + woocommerce_admin_meta_boxes_coupon.suffix;
$coupon_code_field.focus().val( $result );
$coupon_code_label.addClass( 'screen-reader-text' );
}
};
wc_meta_boxes_coupon_actions.init();
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,691 +0,0 @@
/*global woocommerce_admin_meta_boxes */
jQuery( function( $ ) {
// Scroll to first checked category
// https://github.com/scribu/wp-category-checklist-tree/blob/d1c3c1f449e1144542efa17dde84a9f52ade1739/category-checklist-tree.php
$( function() {
$( '[id$="-all"] > ul.categorychecklist' ).each( function() {
var $list = $( this );
var $firstChecked = $list.find( ':checked' ).first();
if ( ! $firstChecked.length ) {
return;
}
var pos_first = $list.find( 'input' ).position().top;
var pos_checked = $firstChecked.position().top;
$list.closest( '.tabs-panel' ).scrollTop( pos_checked - pos_first + 5 );
});
});
// Prevent enter submitting post form.
$( '#upsell_product_data' ).on( 'keypress', function( e ) {
if ( e.keyCode === 13 ) {
return false;
}
});
// Type box.
if ( $( 'body' ).hasClass( 'wc-wp-version-gte-55' ) ) {
$( '.type_box' ).appendTo( '#woocommerce-product-data .hndle' );
} else {
$( '.type_box' ).appendTo( '#woocommerce-product-data .hndle span' );
}
$( function() {
// Prevent inputs in meta box headings opening/closing contents.
$( '#woocommerce-product-data' ).find( '.hndle' ).unbind( 'click.postboxes' );
$( '#woocommerce-product-data' ).on( 'click', '.hndle', function( event ) {
// If the user clicks on some form input inside the h3 the box should not be toggled.
if ( $( event.target ).filter( 'input, option, label, select' ).length ) {
return;
}
$( '#woocommerce-product-data' ).toggleClass( 'closed' );
});
});
// Catalog Visibility.
$( '#catalog-visibility' ).find( '.edit-catalog-visibility' ).on( 'click', function() {
if ( $( '#catalog-visibility-select' ).is( ':hidden' ) ) {
$( '#catalog-visibility-select' ).slideDown( 'fast' );
$( this ).hide();
}
return false;
});
$( '#catalog-visibility' ).find( '.save-post-visibility' ).on( 'click', function() {
$( '#catalog-visibility-select' ).slideUp( 'fast' );
$( '#catalog-visibility' ).find( '.edit-catalog-visibility' ).show();
var label = $( 'input[name=_visibility]:checked' ).attr( 'data-label' );
if ( $( 'input[name=_featured]' ).is( ':checked' ) ) {
label = label + ', ' + woocommerce_admin_meta_boxes.featured_label;
$( 'input[name=_featured]' ).attr( 'checked', 'checked' );
}
$( '#catalog-visibility-display' ).text( label );
return false;
});
$( '#catalog-visibility' ).find( '.cancel-post-visibility' ).on( 'click', function() {
$( '#catalog-visibility-select' ).slideUp( 'fast' );
$( '#catalog-visibility' ).find( '.edit-catalog-visibility' ).show();
var current_visibility = $( '#current_visibility' ).val();
var current_featured = $( '#current_featured' ).val();
$( 'input[name=_visibility]' ).removeAttr( 'checked' );
$( 'input[name=_visibility][value=' + current_visibility + ']' ).attr( 'checked', 'checked' );
var label = $( 'input[name=_visibility]:checked' ).attr( 'data-label' );
if ( 'yes' === current_featured ) {
label = label + ', ' + woocommerce_admin_meta_boxes.featured_label;
$( 'input[name=_featured]' ).attr( 'checked', 'checked' );
} else {
$( 'input[name=_featured]' ).removeAttr( 'checked' );
}
$( '#catalog-visibility-display' ).text( label );
return false;
});
// Product type specific options.
$( 'select#product-type' ).change( function() {
// Get value.
var select_val = $( this ).val();
if ( 'variable' === select_val ) {
$( 'input#_manage_stock' ).trigger( 'change' );
$( 'input#_downloadable' ).prop( 'checked', false );
$( 'input#_virtual' ).removeAttr( 'checked' );
} else if ( 'grouped' === select_val ) {
$( 'input#_downloadable' ).prop( 'checked', false );
$( 'input#_virtual' ).removeAttr( 'checked' );
} else if ( 'external' === select_val ) {
$( 'input#_downloadable' ).prop( 'checked', false );
$( 'input#_virtual' ).removeAttr( 'checked' );
}
show_and_hide_panels();
$( 'ul.wc-tabs li:visible' ).eq( 0 ).find( 'a' ).trigger( 'click' );
$( document.body ).trigger( 'woocommerce-product-type-change', select_val, $( this ) );
}).trigger( 'change' );
$( 'input#_downloadable, input#_virtual' ).change( function() {
show_and_hide_panels();
});
function show_and_hide_panels() {
var product_type = $( 'select#product-type' ).val();
var is_virtual = $( 'input#_virtual:checked' ).length;
var is_downloadable = $( 'input#_downloadable:checked' ).length;
// Hide/Show all with rules.
var hide_classes = '.hide_if_downloadable, .hide_if_virtual';
var show_classes = '.show_if_downloadable, .show_if_virtual';
$.each( woocommerce_admin_meta_boxes.product_types, function( index, value ) {
hide_classes = hide_classes + ', .hide_if_' + value;
show_classes = show_classes + ', .show_if_' + value;
});
$( hide_classes ).show();
$( show_classes ).hide();
// Shows rules.
if ( is_downloadable ) {
$( '.show_if_downloadable' ).show();
}
if ( is_virtual ) {
$( '.show_if_virtual' ).show();
// If user enables virtual while on shipping tab, switch to general tab.
if ( $( '.shipping_options.shipping_tab' ).hasClass( 'active' ) ) {
$( '.general_options.general_tab > a' ).trigger( 'click' );
}
}
$( '.show_if_' + product_type ).show();
// Hide rules.
if ( is_downloadable ) {
$( '.hide_if_downloadable' ).hide();
}
if ( is_virtual ) {
$( '.hide_if_virtual' ).hide();
}
$( '.hide_if_' + product_type ).hide();
$( 'input#_manage_stock' ).trigger( 'change' );
// Hide empty panels/tabs after display.
$( '.woocommerce_options_panel' ).each( function() {
var $children = $( this ).children( '.options_group' );
if ( 0 === $children.length ) {
return;
}
var $invisble = $children.filter( function() {
return 'none' === $( this ).css( 'display' );
});
// Hide panel.
if ( $invisble.length === $children.length ) {
var $id = $( this ).prop( 'id' );
$( '.product_data_tabs' ).find( 'li a[href="#' + $id + '"]' ).parent().hide();
}
});
}
// Sale price schedule.
$( '.sale_price_dates_fields' ).each( function() {
var $these_sale_dates = $( this );
var sale_schedule_set = false;
var $wrap = $these_sale_dates.closest( 'div, table' );
$these_sale_dates.find( 'input' ).each( function() {
if ( '' !== $( this ).val() ) {
sale_schedule_set = true;
}
});
if ( sale_schedule_set ) {
$wrap.find( '.sale_schedule' ).hide();
$wrap.find( '.sale_price_dates_fields' ).show();
} else {
$wrap.find( '.sale_schedule' ).show();
$wrap.find( '.sale_price_dates_fields' ).hide();
}
});
$( '#woocommerce-product-data' ).on( 'click', '.sale_schedule', function() {
var $wrap = $( this ).closest( 'div, table' );
$( this ).hide();
$wrap.find( '.cancel_sale_schedule' ).show();
$wrap.find( '.sale_price_dates_fields' ).show();
return false;
});
$( '#woocommerce-product-data' ).on( 'click', '.cancel_sale_schedule', function() {
var $wrap = $( this ).closest( 'div, table' );
$( this ).hide();
$wrap.find( '.sale_schedule' ).show();
$wrap.find( '.sale_price_dates_fields' ).hide();
$wrap.find( '.sale_price_dates_fields' ).find( 'input' ).val('');
return false;
});
// File inputs.
$( '#woocommerce-product-data' ).on( 'click','.downloadable_files a.insert', function() {
$( this ).closest( '.downloadable_files' ).find( 'tbody' ).append( $( this ).data( 'row' ) );
return false;
});
$( '#woocommerce-product-data' ).on( 'click','.downloadable_files a.delete',function() {
$( this ).closest( 'tr' ).remove();
return false;
});
// Stock options.
$( 'input#_manage_stock' ).change( function() {
if ( $( this ).is( ':checked' ) ) {
$( 'div.stock_fields' ).show();
$( 'p.stock_status_field' ).hide();
} else {
var product_type = $( 'select#product-type' ).val();
$( 'div.stock_fields' ).hide();
$( 'p.stock_status_field:not( .hide_if_' + product_type + ' )' ).show();
}
$( 'input.variable_manage_stock' ).trigger( 'change' );
}).trigger( 'change' );
// Date picker fields.
function date_picker_select( datepicker ) {
var option = $( datepicker ).next().is( '.hasDatepicker' ) ? 'minDate' : 'maxDate',
otherDateField = 'minDate' === option ? $( datepicker ).next() : $( datepicker ).prev(),
date = $( datepicker ).datepicker( 'getDate' );
$( otherDateField ).datepicker( 'option', option, date );
$( datepicker ).trigger( 'change' );
}
$( '.sale_price_dates_fields' ).each( function() {
$( this ).find( 'input' ).datepicker({
defaultDate: '',
dateFormat: 'yy-mm-dd',
numberOfMonths: 1,
showButtonPanel: true,
onSelect: function() {
date_picker_select( $( this ) );
}
});
$( this ).find( 'input' ).each( function() { date_picker_select( $( this ) ); } );
});
// Attribute Tables.
// Initial order.
var woocommerce_attribute_items = $( '.product_attributes' ).find( '.woocommerce_attribute' ).get();
woocommerce_attribute_items.sort( function( a, b ) {
var compA = parseInt( $( a ).attr( 'rel' ), 10 );
var compB = parseInt( $( b ).attr( 'rel' ), 10 );
return ( compA < compB ) ? -1 : ( compA > compB ) ? 1 : 0;
});
$( woocommerce_attribute_items ).each( function( index, el ) {
$( '.product_attributes' ).append( el );
});
function attribute_row_indexes() {
$( '.product_attributes .woocommerce_attribute' ).each( function( index, el ) {
$( '.attribute_position', el ).val( parseInt( $( el ).index( '.product_attributes .woocommerce_attribute' ), 10 ) );
});
}
$( '.product_attributes .woocommerce_attribute' ).each( function( index, el ) {
if ( $( el ).css( 'display' ) !== 'none' && $( el ).is( '.taxonomy' ) ) {
$( 'select.attribute_taxonomy' ).find( 'option[value="' + $( el ).data( 'taxonomy' ) + '"]' ).attr( 'disabled', 'disabled' );
}
});
// Add rows.
$( 'button.add_attribute' ).on( 'click', function() {
var size = $( '.product_attributes .woocommerce_attribute' ).length;
var attribute = $( 'select.attribute_taxonomy' ).val();
var $wrapper = $( this ).closest( '#product_attributes' );
var $attributes = $wrapper.find( '.product_attributes' );
var product_type = $( 'select#product-type' ).val();
var data = {
action: 'woocommerce_add_attribute',
taxonomy: attribute,
i: size,
security: woocommerce_admin_meta_boxes.add_attribute_nonce
};
$wrapper.block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
$.post( woocommerce_admin_meta_boxes.ajax_url, data, function( response ) {
$attributes.append( response );
if ( 'variable' !== product_type ) {
$attributes.find( '.enable_variation' ).hide();
}
$( document.body ).trigger( 'wc-enhanced-select-init' );
attribute_row_indexes();
$attributes.find( '.woocommerce_attribute' ).last().find( 'h3' ).trigger( 'click' );
$wrapper.unblock();
$( document.body ).trigger( 'woocommerce_added_attribute' );
});
if ( attribute ) {
$( 'select.attribute_taxonomy' ).find( 'option[value="' + attribute + '"]' ).attr( 'disabled','disabled' );
$( 'select.attribute_taxonomy' ).val( '' );
}
return false;
});
$( '.product_attributes' ).on( 'blur', 'input.attribute_name', function() {
$( this ).closest( '.woocommerce_attribute' ).find( 'strong.attribute_name' ).text( $( this ).val() );
});
$( '.product_attributes' ).on( 'click', 'button.select_all_attributes', function() {
$( this ).closest( 'td' ).find( 'select option' ).prop( 'selected', 'selected' );
$( this ).closest( 'td' ).find( 'select' ).trigger( 'change' );
return false;
});
$( '.product_attributes' ).on( 'click', 'button.select_no_attributes', function() {
$( this ).closest( 'td' ).find( 'select option' ).removeAttr( 'selected' );
$( this ).closest( 'td' ).find( 'select' ).trigger( 'change' );
return false;
});
$( '.product_attributes' ).on( 'click', '.remove_row', function() {
if ( window.confirm( woocommerce_admin_meta_boxes.remove_attribute ) ) {
var $parent = $( this ).parent().parent();
if ( $parent.is( '.taxonomy' ) ) {
$parent.find( 'select, input[type=text]' ).val( '' );
$parent.hide();
$( 'select.attribute_taxonomy' ).find( 'option[value="' + $parent.data( 'taxonomy' ) + '"]' ).removeAttr( 'disabled' );
} else {
$parent.find( 'select, input[type=text]' ).val( '' );
$parent.hide();
attribute_row_indexes();
}
}
return false;
});
// Attribute ordering.
$( '.product_attributes' ).sortable({
items: '.woocommerce_attribute',
cursor: 'move',
axis: 'y',
handle: 'h3',
scrollSensitivity: 40,
forcePlaceholderSize: true,
helper: 'clone',
opacity: 0.65,
placeholder: 'wc-metabox-sortable-placeholder',
start: function( event, ui ) {
ui.item.css( 'background-color', '#f6f6f6' );
},
stop: function( event, ui ) {
ui.item.removeAttr( 'style' );
attribute_row_indexes();
}
});
// Add a new attribute (via ajax).
$( '.product_attributes' ).on( 'click', 'button.add_new_attribute', function() {
$( '.product_attributes' ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
var $wrapper = $( this ).closest( '.woocommerce_attribute' );
var attribute = $wrapper.data( 'taxonomy' );
var new_attribute_name = window.prompt( woocommerce_admin_meta_boxes.new_attribute_prompt );
if ( new_attribute_name ) {
var data = {
action: 'woocommerce_add_new_attribute',
taxonomy: attribute,
term: new_attribute_name,
security: woocommerce_admin_meta_boxes.add_attribute_nonce
};
$.post( woocommerce_admin_meta_boxes.ajax_url, data, function( response ) {
if ( response.error ) {
// Error.
window.alert( response.error );
} else if ( response.slug ) {
// Success.
$wrapper.find( 'select.attribute_values' )
.append( '<option value="' + response.term_id + '" selected="selected">' + response.name + '</option>' );
$wrapper.find( 'select.attribute_values' ).trigger( 'change' );
}
$( '.product_attributes' ).unblock();
});
} else {
$( '.product_attributes' ).unblock();
}
return false;
});
// Save attributes and update variations.
$( '.save_attributes' ).on( 'click', function() {
$( '.product_attributes' ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
var original_data = $( '.product_attributes' ).find( 'input, select, textarea' );
var data = {
post_id : woocommerce_admin_meta_boxes.post_id,
product_type: $( '#product-type' ).val(),
data : original_data.serialize(),
action : 'woocommerce_save_attributes',
security : woocommerce_admin_meta_boxes.save_attributes_nonce
};
$.post( woocommerce_admin_meta_boxes.ajax_url, data, function( response ) {
if ( response.error ) {
// Error.
window.alert( response.error );
} else if ( response.data ) {
// Success.
$( '.product_attributes' ).html( response.data.html );
$( '.product_attributes' ).unblock();
// Hide the 'Used for variations' checkbox if not viewing a variable product
show_and_hide_panels();
// Make sure the dropdown is not disabled for empty value attributes.
$( 'select.attribute_taxonomy' ).find( 'option' ).prop( 'disabled', false );
$( '.product_attributes .woocommerce_attribute' ).each( function( index, el ) {
if ( $( el ).css( 'display' ) !== 'none' && $( el ).is( '.taxonomy' ) ) {
$( 'select.attribute_taxonomy' )
.find( 'option[value="' + $( el ).data( 'taxonomy' ) + '"]' )
.prop( 'disabled', true );
}
});
// Reload variations panel.
var this_page = window.location.toString();
this_page = this_page.replace( 'post-new.php?', 'post.php?post=' + woocommerce_admin_meta_boxes.post_id + '&action=edit&' );
$( '#variable_product_options' ).load( this_page + ' #variable_product_options_inner', function() {
$( '#variable_product_options' ).trigger( 'reload' );
} );
}
});
});
// Uploading files.
var downloadable_file_frame;
var file_path_field;
$( document.body ).on( 'click', '.upload_file_button', function( event ) {
var $el = $( this );
file_path_field = $el.closest( 'tr' ).find( 'td.file_url input' );
event.preventDefault();
// If the media frame already exists, reopen it.
if ( downloadable_file_frame ) {
downloadable_file_frame.open();
return;
}
var downloadable_file_states = [
// Main states.
new wp.media.controller.Library({
library: wp.media.query(),
multiple: true,
title: $el.data('choose'),
priority: 20,
filterable: 'uploaded'
})
];
// Create the media frame.
downloadable_file_frame = wp.media.frames.downloadable_file = wp.media({
// Set the title of the modal.
title: $el.data('choose'),
library: {
type: ''
},
button: {
text: $el.data('update')
},
multiple: true,
states: downloadable_file_states
});
// When an image is selected, run a callback.
downloadable_file_frame.on( 'select', function() {
var file_path = '';
var selection = downloadable_file_frame.state().get( 'selection' );
selection.map( function( attachment ) {
attachment = attachment.toJSON();
if ( attachment.url ) {
file_path = attachment.url;
}
});
file_path_field.val( file_path ).trigger( 'change' );
});
// Set post to 0 and set our custom type.
downloadable_file_frame.on( 'ready', function() {
downloadable_file_frame.uploader.options.uploader.params = {
type: 'downloadable_product'
};
});
// Finally, open the modal.
downloadable_file_frame.open();
});
// Download ordering.
$( '.downloadable_files tbody' ).sortable({
items: 'tr',
cursor: 'move',
axis: 'y',
handle: 'td.sort',
scrollSensitivity: 40,
forcePlaceholderSize: true,
helper: 'clone',
opacity: 0.65
});
// Product gallery file uploads.
var product_gallery_frame;
var $image_gallery_ids = $( '#product_image_gallery' );
var $product_images = $( '#product_images_container' ).find( 'ul.product_images' );
$( '.add_product_images' ).on( 'click', 'a', function( event ) {
var $el = $( this );
event.preventDefault();
// If the media frame already exists, reopen it.
if ( product_gallery_frame ) {
product_gallery_frame.open();
return;
}
// Create the media frame.
product_gallery_frame = wp.media.frames.product_gallery = wp.media({
// Set the title of the modal.
title: $el.data( 'choose' ),
button: {
text: $el.data( 'update' )
},
states: [
new wp.media.controller.Library({
title: $el.data( 'choose' ),
filterable: 'all',
multiple: true
})
]
});
// When an image is selected, run a callback.
product_gallery_frame.on( 'select', function() {
var selection = product_gallery_frame.state().get( 'selection' );
var attachment_ids = $image_gallery_ids.val();
selection.map( function( attachment ) {
attachment = attachment.toJSON();
if ( attachment.id ) {
attachment_ids = attachment_ids ? attachment_ids + ',' + attachment.id : attachment.id;
var attachment_image = attachment.sizes && attachment.sizes.thumbnail ? attachment.sizes.thumbnail.url : attachment.url;
$product_images.append(
'<li class="image" data-attachment_id="' + attachment.id + '"><img src="' + attachment_image +
'" /><ul class="actions"><li><a href="#" class="delete" title="' + $el.data('delete') + '">' +
$el.data('text') + '</a></li></ul></li>'
);
}
});
$image_gallery_ids.val( attachment_ids );
});
// Finally, open the modal.
product_gallery_frame.open();
});
// Image ordering.
$product_images.sortable({
items: 'li.image',
cursor: 'move',
scrollSensitivity: 40,
forcePlaceholderSize: true,
forceHelperSize: false,
helper: 'clone',
opacity: 0.65,
placeholder: 'wc-metabox-sortable-placeholder',
start: function( event, ui ) {
ui.item.css( 'background-color', '#f6f6f6' );
},
stop: function( event, ui ) {
ui.item.removeAttr( 'style' );
},
update: function() {
var attachment_ids = '';
$( '#product_images_container' ).find( 'ul li.image' ).css( 'cursor', 'default' ).each( function() {
var attachment_id = $( this ).attr( 'data-attachment_id' );
attachment_ids = attachment_ids + attachment_id + ',';
});
$image_gallery_ids.val( attachment_ids );
}
});
// Remove images.
$( '#product_images_container' ).on( 'click', 'a.delete', function() {
$( this ).closest( 'li.image' ).remove();
var attachment_ids = '';
$( '#product_images_container' ).find( 'ul li.image' ).css( 'cursor', 'default' ).each( function() {
var attachment_id = $( this ).attr( 'data-attachment_id' );
attachment_ids = attachment_ids + attachment_id + ',';
});
$image_gallery_ids.val( attachment_ids );
// Remove any lingering tooltips.
$( '#tiptip_holder' ).removeAttr( 'style' );
$( '#tiptip_arrow' ).removeAttr( 'style' );
return false;
});
});

View File

@ -1,68 +0,0 @@
jQuery( function ( $ ) {
// Run tipTip
function runTipTip() {
// Remove any lingering tooltips
$( '#tiptip_holder' ).removeAttr( 'style' );
$( '#tiptip_arrow' ).removeAttr( 'style' );
$( '.tips' ).tipTip({
'attribute': 'data-tip',
'fadeIn': 50,
'fadeOut': 50,
'delay': 200,
'keepAlive': true
});
}
runTipTip();
$( '.wc-metaboxes-wrapper' ).on( 'click', '.wc-metabox > h3', function() {
$( this ).parent( '.wc-metabox' ).toggleClass( 'closed' ).toggleClass( 'open' );
});
// Tabbed Panels
$( document.body ).on( 'wc-init-tabbed-panels', function() {
$( 'ul.wc-tabs' ).show();
$( 'ul.wc-tabs a' ).on( 'click', function( e ) {
e.preventDefault();
var panel_wrap = $( this ).closest( 'div.panel-wrap' );
$( 'ul.wc-tabs li', panel_wrap ).removeClass( 'active' );
$( this ).parent().addClass( 'active' );
$( 'div.panel', panel_wrap ).hide();
$( $( this ).attr( 'href' ) ).show();
});
$( 'div.panel-wrap' ).each( function() {
$( this ).find( 'ul.wc-tabs li' ).eq( 0 ).find( 'a' ).trigger( 'click' );
});
}).trigger( 'wc-init-tabbed-panels' );
// Date Picker
$( document.body ).on( 'wc-init-datepickers', function() {
$( '.date-picker-field, .date-picker' ).datepicker({
dateFormat: 'yy-mm-dd',
numberOfMonths: 1,
showButtonPanel: true
});
}).trigger( 'wc-init-datepickers' );
// Meta-Boxes - Open/close
$( '.wc-metaboxes-wrapper' ).on( 'click', '.wc-metabox h3', function( event ) {
// If the user clicks on some form input inside the h3, like a select list (for variations), the box should not be toggled
if ( $( event.target ).filter( ':input, option, .sort' ).length ) {
return;
}
$( this ).next( '.wc-metabox-content' ).stop().slideToggle();
})
.on( 'click', '.expand_all', function() {
$( this ).closest( '.wc-metaboxes-wrapper' ).find( '.wc-metabox > .wc-metabox-content' ).show();
return false;
})
.on( 'click', '.close_all', function() {
$( this ).closest( '.wc-metaboxes-wrapper' ).find( '.wc-metabox > .wc-metabox-content' ).hide();
return false;
});
$( '.wc-metabox.closed' ).each( function() {
$( this ).find( '.wc-metabox-content' ).hide();
});
});

View File

@ -1,167 +0,0 @@
/*global inlineEditPost, woocommerce_admin, woocommerce_quick_edit */
jQuery(
function( $ ) {
$( '#the-list' ).on(
'click',
'.editinline',
function() {
inlineEditPost.revert();
var post_id = $( this ).closest( 'tr' ).attr( 'id' );
post_id = post_id.replace( 'post-', '' );
var $wc_inline_data = $( '#woocommerce_inline_' + post_id );
var sku = $wc_inline_data.find( '.sku' ).text(),
regular_price = $wc_inline_data.find( '.regular_price' ).text(),
sale_price = $wc_inline_data.find( '.sale_price ' ).text(),
weight = $wc_inline_data.find( '.weight' ).text(),
length = $wc_inline_data.find( '.length' ).text(),
width = $wc_inline_data.find( '.width' ).text(),
height = $wc_inline_data.find( '.height' ).text(),
shipping_class = $wc_inline_data.find( '.shipping_class' ).text(),
visibility = $wc_inline_data.find( '.visibility' ).text(),
stock_status = $wc_inline_data.find( '.stock_status' ).text(),
stock = $wc_inline_data.find( '.stock' ).text(),
featured = $wc_inline_data.find( '.featured' ).text(),
manage_stock = $wc_inline_data.find( '.manage_stock' ).text(),
menu_order = $wc_inline_data.find( '.menu_order' ).text(),
tax_status = $wc_inline_data.find( '.tax_status' ).text(),
tax_class = $wc_inline_data.find( '.tax_class' ).text(),
backorders = $wc_inline_data.find( '.backorders' ).text(),
product_type = $wc_inline_data.find( '.product_type' ).text();
var formatted_regular_price = regular_price.replace( '.', woocommerce_admin.mon_decimal_point ),
formatted_sale_price = sale_price.replace( '.', woocommerce_admin.mon_decimal_point );
$( 'input[name="_sku"]', '.inline-edit-row' ).val( sku );
$( 'input[name="_regular_price"]', '.inline-edit-row' ).val( formatted_regular_price );
$( 'input[name="_sale_price"]', '.inline-edit-row' ).val( formatted_sale_price );
$( 'input[name="_weight"]', '.inline-edit-row' ).val( weight );
$( 'input[name="_length"]', '.inline-edit-row' ).val( length );
$( 'input[name="_width"]', '.inline-edit-row' ).val( width );
$( 'input[name="_height"]', '.inline-edit-row' ).val( height );
$( 'select[name="_shipping_class"] option:selected', '.inline-edit-row' ).attr( 'selected', false ).trigger( 'change' );
$( 'select[name="_shipping_class"] option[value="' + shipping_class + '"]' ).attr( 'selected', 'selected' )
.trigger( 'change' );
$( 'input[name="_stock"]', '.inline-edit-row' ).val( stock );
$( 'input[name="menu_order"]', '.inline-edit-row' ).val( menu_order );
$(
'select[name="_tax_status"] option, ' +
'select[name="_tax_class"] option, ' +
'select[name="_visibility"] option, ' +
'select[name="_stock_status"] option, ' +
'select[name="_backorders"] option'
).removeAttr( 'selected' );
var is_variable_product = 'variable' === product_type;
$( 'select[name="_stock_status"] ~ .wc-quick-edit-warning', '.inline-edit-row' ).toggle( is_variable_product );
$( 'select[name="_stock_status"] option[value="' + (is_variable_product ? '' : stock_status) + '"]', '.inline-edit-row' )
.attr( 'selected', 'selected' );
$( 'select[name="_tax_status"] option[value="' + tax_status + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
$( 'select[name="_tax_class"] option[value="' + tax_class + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
$( 'select[name="_visibility"] option[value="' + visibility + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
$( 'select[name="_backorders"] option[value="' + backorders + '"]', '.inline-edit-row' ).attr( 'selected', 'selected' );
if ( 'yes' === featured ) {
$( 'input[name="_featured"]', '.inline-edit-row' ).attr( 'checked', 'checked' );
} else {
$( 'input[name="_featured"]', '.inline-edit-row' ).removeAttr( 'checked' );
}
// Conditional display.
var product_is_virtual = $wc_inline_data.find( '.product_is_virtual' ).text();
var product_supports_stock_status = 'external' !== product_type;
var product_supports_stock_fields = 'external' !== product_type && 'grouped' !== product_type;
$( '.stock_fields, .manage_stock_field, .stock_status_field, .backorder_field' ).show();
if ( product_supports_stock_fields ) {
if ( 'yes' === manage_stock ) {
$( '.stock_qty_field, .backorder_field', '.inline-edit-row' ).show().removeAttr( 'style' );
$( '.stock_status_field' ).hide();
$( '.manage_stock_field input' ).prop( 'checked', true );
} else {
$( '.stock_qty_field, .backorder_field', '.inline-edit-row' ).hide();
$( '.stock_status_field' ).show().removeAttr( 'style' );
$( '.manage_stock_field input' ).prop( 'checked', false );
}
} else if ( product_supports_stock_status ) {
$( '.stock_fields, .manage_stock_field, .backorder_field' ).hide();
} else {
$( '.stock_fields, .manage_stock_field, .stock_status_field, .backorder_field' ).hide();
}
if ( 'simple' === product_type || 'external' === product_type ) {
$( '.price_fields', '.inline-edit-row' ).show().removeAttr( 'style' );
} else {
$( '.price_fields', '.inline-edit-row' ).hide();
}
if ( 'yes' === product_is_virtual ) {
$( '.dimension_fields', '.inline-edit-row' ).hide();
} else {
$( '.dimension_fields', '.inline-edit-row' ).show().removeAttr( 'style' );
}
// Rename core strings.
$( 'input[name="comment_status"]' ).parent().find( '.checkbox-title' ).text( woocommerce_quick_edit.strings.allow_reviews );
}
);
$( '#the-list' ).on(
'change',
'.inline-edit-row input[name="_manage_stock"]',
function() {
if ( $( this ).is( ':checked' ) ) {
$( '.stock_qty_field, .backorder_field', '.inline-edit-row' ).show().removeAttr( 'style' );
$( '.stock_status_field' ).hide();
} else {
$( '.stock_qty_field, .backorder_field', '.inline-edit-row' ).hide();
$( '.stock_status_field' ).show().removeAttr( 'style' );
}
}
);
$( '#wpbody' ).on(
'click',
'#doaction, #doaction2',
function() {
$( 'input.text', '.inline-edit-row' ).val( '' );
$( '#woocommerce-fields' ).find( 'select' ).prop( 'selectedIndex', 0 );
$( '#woocommerce-fields-bulk' ).find( '.inline-edit-group .change-input' ).hide();
}
);
$( '#wpbody' ).on(
'change',
'#woocommerce-fields-bulk .inline-edit-group .change_to',
function() {
if ( 0 < $( this ).val() ) {
$( this ).closest( 'div' ).find( '.change-input' ).show();
} else {
$( this ).closest( 'div' ).find( '.change-input' ).hide();
}
}
);
$( '#wpbody' ).on(
'click',
'.trash-product',
function() {
return window.confirm( woocommerce_admin.i18n_delete_product_notice );
}
);
}
);

View File

@ -1,383 +0,0 @@
/* global htmlSettingsTaxLocalizeScript, ajaxurl */
/**
* Used by woocommerce/includes/admin/settings/views/html-settings-tax.php
*/
( function( $, data, wp, ajaxurl ) {
$( function() {
if ( ! String.prototype.trim ) {
String.prototype.trim = function () {
return this.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '' );
};
}
var rowTemplate = wp.template( 'wc-tax-table-row' ),
rowTemplateEmpty = wp.template( 'wc-tax-table-row-empty' ),
paginationTemplate = wp.template( 'wc-tax-table-pagination' ),
$table = $( '.wc_tax_rates' ),
$tbody = $( '#rates' ),
$save_button = $( ':input[name="save"]' ),
$pagination = $( '#rates-pagination' ),
$search_field = $( '#rates-search .wc-tax-rates-search-field' ),
$submit = $( '.submit .button-primary[type=submit]' ),
WCTaxTableModelConstructor = Backbone.Model.extend({
changes: {},
setRateAttribute: function( rateID, attribute, value ) {
var rates = _.indexBy( this.get( 'rates' ), 'tax_rate_id' ),
changes = {};
if ( rates[ rateID ][ attribute ] !== value ) {
changes[ rateID ] = {};
changes[ rateID ][ attribute ] = value;
rates[ rateID ][ attribute ] = value;
}
this.logChanges( changes );
},
logChanges: function( changedRows ) {
var changes = this.changes || {};
_.each( changedRows, function( row, id ) {
changes[ id ] = _.extend( changes[ id ] || {
tax_rate_id : id
}, row );
} );
this.changes = changes;
this.trigger( 'change:rates' );
},
getFilteredRates: function() {
var rates = this.get( 'rates' ),
search = $search_field.val().toLowerCase();
if ( search.length ) {
rates = _.filter( rates, function( rate ) {
var search_text = _.toArray( rate ).join( ' ' ).toLowerCase();
return ( -1 !== search_text.indexOf( search ) );
} );
}
rates = _.sortBy( rates, function( rate ) {
return parseInt( rate.tax_rate_order, 10 );
} );
return rates;
},
block: function() {
$( '.wc_tax_rates' ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
},
unblock: function() {
$( '.wc_tax_rates' ).unblock();
},
save: function() {
var self = this;
self.block();
Backbone.ajax({
method: 'POST',
dataType: 'json',
url: ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_tax_rates_save_changes',
data: {
current_class: data.current_class,
wc_tax_nonce: data.wc_tax_nonce,
changes: self.changes
},
success: function( response, textStatus ) {
if ( 'success' === textStatus && response.success ) {
WCTaxTableModelInstance.set( 'rates', response.data.rates );
WCTaxTableModelInstance.trigger( 'change:rates' );
WCTaxTableModelInstance.changes = {};
WCTaxTableModelInstance.trigger( 'saved:rates' );
// Reload view.
WCTaxTableInstance.render();
}
self.unblock();
}
});
}
} ),
WCTaxTableViewConstructor = Backbone.View.extend({
rowTemplate: rowTemplate,
per_page: data.limit,
page: data.page,
initialize: function() {
var qty_pages = Math.ceil( _.toArray( this.model.get( 'rates' ) ).length / this.per_page );
this.qty_pages = 0 === qty_pages ? 1 : qty_pages;
this.page = this.sanitizePage( data.page );
this.listenTo( this.model, 'change:rates', this.setUnloadConfirmation );
this.listenTo( this.model, 'saved:rates', this.clearUnloadConfirmation );
$tbody.on( 'change autocompletechange', ':input', { view: this }, this.updateModelOnChange );
$search_field.on( 'keyup search', { view: this }, this.onSearchField );
$pagination.on( 'click', 'a', { view: this }, this.onPageChange );
$pagination.on( 'change', 'input', { view: this }, this.onPageChange );
$( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
$submit.on( 'click', { view: this }, this.onSubmit );
$save_button.prop( 'disabled', true );
// Can bind these directly to the buttons, as they won't get overwritten.
$table.find( '.insert' ).on( 'click', { view: this }, this.onAddNewRow );
$table.find( '.remove_tax_rates' ).on( 'click', { view: this }, this.onDeleteRow );
$table.find( '.export' ).on( 'click', { view: this }, this.onExport );
},
render: function() {
var rates = this.model.getFilteredRates(),
qty_rates = _.size( rates ),
qty_pages = Math.ceil( qty_rates / this.per_page ),
first_index = 0 === qty_rates ? 0 : this.per_page * ( this.page - 1 ),
last_index = this.per_page * this.page,
paged_rates = _.toArray( rates ).slice( first_index, last_index ),
view = this;
// Blank out the contents.
this.$el.empty();
if ( paged_rates.length ) {
// Populate $tbody with the current page of results.
$.each( paged_rates, function( id, rowData ) {
view.$el.append( view.rowTemplate( rowData ) );
} );
} else {
view.$el.append( rowTemplateEmpty() );
}
// Initialize autocomplete for countries.
this.$el.find( 'td.country input' ).autocomplete({
source: data.countries,
minLength: 2
});
// Initialize autocomplete for states.
this.$el.find( 'td.state input' ).autocomplete({
source: data.states,
minLength: 3
});
// Postcode and city don't have `name` values by default.
// They're only created if the contents changes, to save on database queries (I think)
this.$el.find( 'td.postcode input, td.city input' ).change( function() {
$( this ).attr( 'name', $( this ).data( 'name' ) );
});
if ( qty_pages > 1 ) {
// We've now displayed our initial page, time to render the pagination box.
$pagination.html( paginationTemplate( {
qty_rates: qty_rates,
current_page: this.page,
qty_pages: qty_pages
} ) );
} else {
$pagination.empty();
view.page = 1;
}
},
updateUrl: function() {
if ( ! window.history.replaceState ) {
return;
}
var url = data.base_url,
search = $search_field.val();
if ( 1 < this.page ) {
url += '&p=' + encodeURIComponent( this.page );
}
if ( search.length ) {
url += '&s=' + encodeURIComponent( search );
}
window.history.replaceState( {}, '', url );
},
onSubmit: function( event ) {
event.data.view.model.save();
event.preventDefault();
},
onAddNewRow: function( event ) {
var view = event.data.view,
model = view.model,
rates = _.indexBy( model.get( 'rates' ), 'tax_rate_id' ),
changes = {},
size = _.size( rates ),
newRow = _.extend( {}, data.default_rate, {
tax_rate_id: 'new-' + size + '-' + Date.now(),
newRow: true
} ),
$current, current_id, current_order, rates_to_reorder, reordered_rates;
$current = $tbody.children( '.current' );
if ( $current.length ) {
current_id = $current.last().data( 'id' );
current_order = parseInt( rates[ current_id ].tax_rate_order, 10 );
newRow.tax_rate_order = 1 + current_order;
rates_to_reorder = _.filter( rates, function( rate ) {
if ( parseInt( rate.tax_rate_order, 10 ) > current_order ) {
return true;
}
return false;
} );
reordered_rates = _.map( rates_to_reorder, function( rate ) {
rate.tax_rate_order++;
changes[ rate.tax_rate_id ] = _.extend(
changes[ rate.tax_rate_id ] || {}, { tax_rate_order : rate.tax_rate_order }
);
return rate;
} );
} else {
newRow.tax_rate_order = 1 + _.max(
_.pluck( rates, 'tax_rate_order' ),
function ( val ) {
// Cast them all to integers, because strings compare funky. Sighhh.
return parseInt( val, 10 );
}
);
// Move the last page
view.page = view.qty_pages;
}
rates[ newRow.tax_rate_id ] = newRow;
changes[ newRow.tax_rate_id ] = newRow;
model.set( 'rates', rates );
model.logChanges( changes );
view.render();
},
onDeleteRow: function( event ) {
var view = event.data.view,
model = view.model,
rates = _.indexBy( model.get( 'rates' ), 'tax_rate_id' ),
changes = {},
$current, current_id;
event.preventDefault();
if ( $current = $tbody.children( '.current' ) ) {
$current.each(function(){
current_id = $( this ).data('id');
delete rates[ current_id ];
changes[ current_id ] = _.extend( changes[ current_id ] || {}, { deleted : 'deleted' } );
});
model.set( 'rates', rates );
model.logChanges( changes );
view.render();
} else {
window.alert( data.strings.no_rows_selected );
}
},
onSearchField: function( event ){
event.data.view.updateUrl();
event.data.view.render();
},
onPageChange: function( event ) {
var $target = $( event.currentTarget );
event.preventDefault();
event.data.view.page = $target.data( 'goto' ) ? $target.data( 'goto' ) : $target.val();
event.data.view.render();
event.data.view.updateUrl();
},
onExport: function( event ) {
var csv_data = 'data:application/csv;charset=utf-8,' + data.strings.csv_data_cols.join(',') + '\n';
$.each( event.data.view.model.getFilteredRates(), function( id, rowData ) {
var row = '';
row += rowData.tax_rate_country + ',';
row += rowData.tax_rate_state + ',';
row += ( rowData.postcode ? rowData.postcode.join( '; ' ) : '' ) + ',';
row += ( rowData.city ? rowData.city.join( '; ' ) : '' ) + ',';
row += rowData.tax_rate + ',';
row += rowData.tax_rate_name + ',';
row += rowData.tax_rate_priority + ',';
row += rowData.tax_rate_compound + ',';
row += rowData.tax_rate_shipping + ',';
row += data.current_class;
csv_data += row + '\n';
});
$( this ).attr( 'href', encodeURI( csv_data ) );
return true;
},
setUnloadConfirmation: function() {
this.needsUnloadConfirm = true;
$save_button.prop( 'disabled', false );
},
clearUnloadConfirmation: function() {
this.needsUnloadConfirm = false;
$save_button.prop( 'disabled', true );
},
unloadConfirmation: function( event ) {
if ( event.data.view.needsUnloadConfirm ) {
event.returnValue = data.strings.unload_confirmation_msg;
window.event.returnValue = data.strings.unload_confirmation_msg;
return data.strings.unload_confirmation_msg;
}
},
updateModelOnChange: function( event ) {
var model = event.data.view.model,
$target = $( event.target ),
id = $target.closest( 'tr' ).data( 'id' ),
attribute = $target.data( 'attribute' ),
val = $target.val();
if ( 'city' === attribute || 'postcode' === attribute ) {
val = val.split( ';' );
val = $.map( val, function( thing ) {
return thing.trim();
});
}
if ( 'tax_rate_compound' === attribute || 'tax_rate_shipping' === attribute ) {
if ( $target.is( ':checked' ) ) {
val = 1;
} else {
val = 0;
}
}
model.setRateAttribute( id, attribute, val );
},
sanitizePage: function( page_num ) {
page_num = parseInt( page_num, 10 );
if ( page_num < 1 ) {
page_num = 1;
} else if ( page_num > this.qty_pages ) {
page_num = this.qty_pages;
}
return page_num;
}
} ),
WCTaxTableModelInstance = new WCTaxTableModelConstructor({
rates: data.rates
} ),
WCTaxTableInstance = new WCTaxTableViewConstructor({
model: WCTaxTableModelInstance,
el: '#rates'
} );
WCTaxTableInstance.render();
});
})( jQuery, htmlSettingsTaxLocalizeScript, wp, ajaxurl );

View File

@ -1,183 +0,0 @@
/* global woocommerce_settings_params, wp */
( function( $, params, wp ) {
$( function() {
// Sell Countries
$( 'select#woocommerce_allowed_countries' ).change( function() {
if ( 'specific' === $( this ).val() ) {
$( this ).closest('tr').next( 'tr' ).hide();
$( this ).closest('tr').next().next( 'tr' ).show();
} else if ( 'all_except' === $( this ).val() ) {
$( this ).closest('tr').next( 'tr' ).show();
$( this ).closest('tr').next().next( 'tr' ).hide();
} else {
$( this ).closest('tr').next( 'tr' ).hide();
$( this ).closest('tr').next().next( 'tr' ).hide();
}
}).trigger( 'change' );
// Ship Countries
$( 'select#woocommerce_ship_to_countries' ).change( function() {
if ( 'specific' === $( this ).val() ) {
$( this ).closest('tr').next( 'tr' ).show();
} else {
$( this ).closest('tr').next( 'tr' ).hide();
}
}).trigger( 'change' );
// Stock management
$( 'input#woocommerce_manage_stock' ).change( function() {
if ( $( this ).is(':checked') ) {
$( this ).closest('tbody').find( '.manage_stock_field' ).closest( 'tr' ).show();
} else {
$( this ).closest('tbody').find( '.manage_stock_field' ).closest( 'tr' ).hide();
}
}).trigger( 'change' );
// Color picker
$( '.colorpick' )
.iris({
change: function( event, ui ) {
$( this ).parent().find( '.colorpickpreview' ).css({ backgroundColor: ui.color.toString() });
},
hide: true,
border: true
})
.on( 'click focus', function( event ) {
event.stopPropagation();
$( '.iris-picker' ).hide();
$( this ).closest( 'td' ).find( '.iris-picker' ).show();
$( this ).data( 'original-value', $( this ).val() );
})
.on( 'change', function() {
if ( $( this ).is( '.iris-error' ) ) {
var original_value = $( this ).data( 'original-value' );
if ( original_value.match( /^\#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/ ) ) {
$( this ).val( $( this ).data( 'original-value' ) ).trigger( 'change' );
} else {
$( this ).val( '' ).trigger( 'change' );
}
}
});
$( 'body' ).on( 'click', function() {
$( '.iris-picker' ).hide();
});
// Edit prompt
$( function() {
var changed = false;
$( 'input, textarea, select, checkbox' ).change( function() {
if ( ! changed ) {
window.onbeforeunload = function() {
return params.i18n_nav_warning;
};
changed = true;
}
});
$( '.submit :input' ).on( 'click', function() {
window.onbeforeunload = '';
});
});
// Sorting
$( 'table.wc_gateways tbody, table.wc_shipping tbody' ).sortable({
items: 'tr',
cursor: 'move',
axis: 'y',
handle: 'td.sort',
scrollSensitivity: 40,
helper: function( event, ui ) {
ui.children().each( function() {
$( this ).width( $( this ).width() );
});
ui.css( 'left', '0' );
return ui;
},
start: function( event, ui ) {
ui.item.css( 'background-color', '#f6f6f6' );
},
stop: function( event, ui ) {
ui.item.removeAttr( 'style' );
ui.item.trigger( 'updateMoveButtons' );
}
});
// Select all/none
$( '.woocommerce' ).on( 'click', '.select_all', function() {
$( this ).closest( 'td' ).find( 'select option' ).attr( 'selected', 'selected' );
$( this ).closest( 'td' ).find( 'select' ).trigger( 'change' );
return false;
});
$( '.woocommerce' ).on( 'click', '.select_none', function() {
$( this ).closest( 'td' ).find( 'select option' ).removeAttr( 'selected' );
$( this ).closest( 'td' ).find( 'select' ).trigger( 'change' );
return false;
});
// Re-order buttons.
$( '.wc-item-reorder-nav').find( '.wc-move-up, .wc-move-down' ).on( 'click', function() {
var moveBtn = $( this ),
$row = moveBtn.closest( 'tr' );
moveBtn.focus();
var isMoveUp = moveBtn.is( '.wc-move-up' ),
isMoveDown = moveBtn.is( '.wc-move-down' );
if ( isMoveUp ) {
var $previewRow = $row.prev( 'tr' );
if ( $previewRow && $previewRow.length ) {
$previewRow.before( $row );
wp.a11y.speak( params.i18n_moved_up );
}
} else if ( isMoveDown ) {
var $nextRow = $row.next( 'tr' );
if ( $nextRow && $nextRow.length ) {
$nextRow.after( $row );
wp.a11y.speak( params.i18n_moved_down );
}
}
moveBtn.focus(); // Re-focus after the container was moved.
moveBtn.closest( 'table' ).trigger( 'updateMoveButtons' );
} );
$( '.wc-item-reorder-nav').closest( 'table' ).on( 'updateMoveButtons', function() {
var table = $( this ),
lastRow = $( this ).find( 'tbody tr:last' ),
firstRow = $( this ).find( 'tbody tr:first' );
table.find( '.wc-item-reorder-nav .wc-move-disabled' ).removeClass( 'wc-move-disabled' )
.attr( { 'tabindex': '0', 'aria-hidden': 'false' } );
firstRow.find( '.wc-item-reorder-nav .wc-move-up' ).addClass( 'wc-move-disabled' )
.attr( { 'tabindex': '-1', 'aria-hidden': 'true' } );
lastRow.find( '.wc-item-reorder-nav .wc-move-down' ).addClass( 'wc-move-disabled' )
.attr( { 'tabindex': '-1', 'aria-hidden': 'true' } );
} );
$( '.wc-item-reorder-nav').closest( 'table' ).trigger( 'updateMoveButtons' );
$( '.submit button' ).on( 'click', function() {
if (
$( 'select#woocommerce_allowed_countries' ).val() === 'specific' &&
! $( '[name="woocommerce_specific_allowed_countries[]"]' ).val()
) {
if ( window.confirm( woocommerce_settings_params.i18n_no_specific_countries_selected ) ) {
return true;
}
return false;
}
} );
});
})( jQuery, woocommerce_settings_params, wp );

View File

@ -1,126 +0,0 @@
/* global jQuery, woocommerce_admin_system_status, wcSetClipboard, wcClearClipboard */
jQuery( function ( $ ) {
/**
* Users country and state fields
*/
var wcSystemStatus = {
init: function() {
$( document.body )
.on( 'click', 'a.help_tip, a.woocommerce-help-tip', this.preventTipTipClick )
.on( 'click', 'a.debug-report', this.generateReport )
.on( 'click', '#copy-for-support', this.copyReport )
.on( 'aftercopy', '#copy-for-support', this.copySuccess )
.on( 'aftercopyfailure', '#copy-for-support', this.copyFail );
},
/**
* Prevent anchor behavior when click on TipTip.
*
* @return {Bool}
*/
preventTipTipClick: function() {
return false;
},
/**
* Generate system status report.
*
* @return {Bool}
*/
generateReport: function() {
var report = '';
$( '.wc_status_table thead, .wc_status_table tbody' ).each( function() {
if ( $( this ).is( 'thead' ) ) {
var label = $( this ).find( 'th:eq(0)' ).data( 'export-label' ) || $( this ).text();
report = report + '\n### ' + label.trim() + ' ###\n\n';
} else {
$( 'tr', $( this ) ).each( function() {
var label = $( this ).find( 'td:eq(0)' ).data( 'export-label' ) || $( this ).find( 'td:eq(0)' ).text();
var the_name = label.trim().replace( /(<([^>]+)>)/ig, '' ); // Remove HTML.
// Find value
var $value_html = $( this ).find( 'td:eq(2)' ).clone();
$value_html.find( '.private' ).remove();
$value_html.find( '.dashicons-yes' ).replaceWith( '&#10004;' );
$value_html.find( '.dashicons-no-alt, .dashicons-warning' ).replaceWith( '&#10060;' );
// Format value
var the_value = $value_html.text().trim();
var value_array = the_value.split( ', ' );
if ( value_array.length > 1 ) {
// If value have a list of plugins ','.
// Split to add new line.
var temp_line ='';
$.each( value_array, function( key, line ) {
temp_line = temp_line + line + '\n';
});
the_value = temp_line;
}
report = report + '' + the_name + ': ' + the_value + '\n';
});
}
});
try {
$( '#debug-report' ).slideDown();
$( '#debug-report' ).find( 'textarea' ).val( '`' + report + '`' ).focus().select();
$( this ).fadeOut();
return false;
} catch ( e ) {
/* jshint devel: true */
console.log( e );
}
return false;
},
/**
* Copy for report.
*
* @param {Object} evt Copy event.
*/
copyReport: function( evt ) {
wcClearClipboard();
wcSetClipboard( $( '#debug-report' ).find( 'textarea' ).val(), $( this ) );
evt.preventDefault();
},
/**
* Display a "Copied!" tip when success copying
*/
copySuccess: function() {
$( '#copy-for-support' ).tipTip({
'attribute': 'data-tip',
'activation': 'focus',
'fadeIn': 50,
'fadeOut': 50,
'delay': 0
}).focus();
},
/**
* Displays the copy error message when failure copying.
*/
copyFail: function() {
$( '.copy-error' ).removeClass( 'hidden' );
$( '#debug-report' ).find( 'textarea' ).focus().select();
}
};
wcSystemStatus.init();
$( '.wc_status_table' ).on( 'click', '.run-tool .button', function( evt ) {
evt.stopImmediatePropagation();
return window.confirm( woocommerce_admin_system_status.run_tool_confirmation );
});
$( '#log-viewer-select' ).on( 'click', 'h2 a.page-title-action', function( evt ) {
evt.stopImmediatePropagation();
return window.confirm( woocommerce_admin_system_status.delete_log_confirmation );
});
});

View File

@ -1,120 +0,0 @@
/*global wc_users_params */
jQuery( function ( $ ) {
/**
* Users country and state fields
*/
var wc_users_fields = {
states: null,
init: function() {
if ( typeof wc_users_params.countries !== 'undefined' ) {
/* State/Country select boxes */
this.states = JSON.parse( wc_users_params.countries.replace( /&quot;/g, '"' ) );
}
$( '.js_field-country' ).selectWoo().change( this.change_country );
$( '.js_field-country' ).trigger( 'change', [ true ] );
$( document.body ).on( 'change', 'select.js_field-state', this.change_state );
$( document.body ).on( 'click', 'button.js_copy-billing', this.copy_billing );
},
change_country: function( e, stickValue ) {
// Check for stickValue before using it
if ( typeof stickValue === 'undefined' ) {
stickValue = false;
}
// Prevent if we don't have the metabox data
if ( wc_users_fields.states === null ) {
return;
}
var $this = $( this ),
country = $this.val(),
$state = $this.parents( '.form-table' ).find( ':input.js_field-state' ),
$parent = $state.parent(),
input_name = $state.attr( 'name' ),
input_id = $state.attr( 'id' ),
stickstatefield = 'woocommerce.stickState-' + country,
value = $this.data( stickstatefield ) ? $this.data( stickstatefield ) : $state.val(),
placeholder = $state.attr( 'placeholder' ),
$newstate;
if ( stickValue ){
$this.data( 'woocommerce.stickState-' + country, value );
}
// Remove the previous DOM element
$parent.show().find( '.select2-container' ).remove();
if ( ! $.isEmptyObject( wc_users_fields.states[ country ] ) ) {
var state = wc_users_fields.states[ country ],
$defaultOption = $( '<option value=""></option>' )
.text( wc_users_fields.i18n_select_state_text );
$newstate = $( '<select style="width: 25em;"></select>' )
.prop( 'id', input_id )
.prop( 'name', input_name )
.prop( 'placeholder', placeholder )
.addClass( 'js_field-state' )
.append( $defaultOption );
$.each( state, function( index ) {
var $option = $( '<option></option>' )
.prop( 'value', index )
.text( state[ index ] );
$newstate.append( $option );
} );
$newstate.val( value );
$state.replaceWith( $newstate );
$newstate.show().selectWoo().hide().trigger( 'change' );
} else {
$newstate = $( '<input type="text" />' )
.prop( 'id', input_id )
.prop( 'name', input_name )
.prop( 'placeholder', placeholder )
.addClass( 'js_field-state regular-text' )
.val( value );
$state.replaceWith( $newstate );
}
// This event has a typo - deprecated in 2.5.0
$( document.body ).trigger( 'contry-change.woocommerce', [country, $( this ).closest( 'div' )] );
$( document.body ).trigger( 'country-change.woocommerce', [country, $( this ).closest( 'div' )] );
},
change_state: function() {
// Here we will find if state value on a select has changed and stick it to the country data
var $this = $( this ),
state = $this.val(),
$country = $this.parents( '.form-table' ).find( ':input.js_field-country' ),
country = $country.val();
$country.data( 'woocommerce.stickState-' + country, state );
},
copy_billing: function( event ) {
event.preventDefault();
$( '#fieldset-billing' ).find( 'input, select' ).each( function( i, el ) {
// The address keys match up, except for the prefix
var shipName = el.name.replace( /^billing_/, 'shipping_' );
// Swap prefix, then check if there are any elements
var shipEl = $( '[name="' + shipName + '"]' );
// No corresponding shipping field, skip this item
if ( ! shipEl.length ) {
return;
}
// Found a matching shipping element, update the value
shipEl.val( el.value ).trigger( 'change' );
} );
}
};
wc_users_fields.init();
});

View File

@ -1,38 +0,0 @@
/* exported wcSetClipboard, wcClearClipboard */
/**
* Simple text copy functions using native browser clipboard capabilities.
* @since 3.2.0
*/
/**
* Set the user's clipboard contents.
*
* @param string data: Text to copy to clipboard.
* @param object $el: jQuery element to trigger copy events on. (Default: document)
*/
function wcSetClipboard( data, $el ) {
if ( 'undefined' === typeof $el ) {
$el = jQuery( document );
}
var $temp_input = jQuery( '<textarea style="opacity:0">' );
jQuery( 'body' ).append( $temp_input );
$temp_input.val( data ).select();
$el.trigger( 'beforecopy' );
try {
document.execCommand( 'copy' );
$el.trigger( 'aftercopy' );
} catch ( err ) {
$el.trigger( 'aftercopyfailure' );
}
$temp_input.remove();
}
/**
* Clear the user's clipboard.
*/
function wcClearClipboard() {
wcSetClipboard( '' );
}

View File

@ -1,280 +0,0 @@
/*global wc_enhanced_select_params */
jQuery( function( $ ) {
function getEnhancedSelectFormatString() {
return {
'language': {
errorLoading: function() {
// Workaround for https://github.com/select2/select2/issues/4355 instead of i18n_ajax_error.
return wc_enhanced_select_params.i18n_searching;
},
inputTooLong: function( args ) {
var overChars = args.input.length - args.maximum;
if ( 1 === overChars ) {
return wc_enhanced_select_params.i18n_input_too_long_1;
}
return wc_enhanced_select_params.i18n_input_too_long_n.replace( '%qty%', overChars );
},
inputTooShort: function( args ) {
var remainingChars = args.minimum - args.input.length;
if ( 1 === remainingChars ) {
return wc_enhanced_select_params.i18n_input_too_short_1;
}
return wc_enhanced_select_params.i18n_input_too_short_n.replace( '%qty%', remainingChars );
},
loadingMore: function() {
return wc_enhanced_select_params.i18n_load_more;
},
maximumSelected: function( args ) {
if ( args.maximum === 1 ) {
return wc_enhanced_select_params.i18n_selection_too_long_1;
}
return wc_enhanced_select_params.i18n_selection_too_long_n.replace( '%qty%', args.maximum );
},
noResults: function() {
return wc_enhanced_select_params.i18n_no_matches;
},
searching: function() {
return wc_enhanced_select_params.i18n_searching;
}
}
};
}
try {
$( document.body )
.on( 'wc-enhanced-select-init', function() {
// Regular select boxes
$( ':input.wc-enhanced-select, :input.chosen_select' ).filter( ':not(.enhanced)' ).each( function() {
var select2_args = $.extend({
minimumResultsForSearch: 10,
allowClear: $( this ).data( 'allow_clear' ) ? true : false,
placeholder: $( this ).data( 'placeholder' )
}, getEnhancedSelectFormatString() );
$( this ).selectWoo( select2_args ).addClass( 'enhanced' );
});
$( ':input.wc-enhanced-select-nostd, :input.chosen_select_nostd' ).filter( ':not(.enhanced)' ).each( function() {
var select2_args = $.extend({
minimumResultsForSearch: 10,
allowClear: true,
placeholder: $( this ).data( 'placeholder' )
}, getEnhancedSelectFormatString() );
$( this ).selectWoo( select2_args ).addClass( 'enhanced' );
});
// Ajax product search box
$( ':input.wc-product-search' ).filter( ':not(.enhanced)' ).each( function() {
var select2_args = {
allowClear: $( this ).data( 'allow_clear' ) ? true : false,
placeholder: $( this ).data( 'placeholder' ),
minimumInputLength: $( this ).data( 'minimum_input_length' ) ? $( this ).data( 'minimum_input_length' ) : '3',
escapeMarkup: function( m ) {
return m;
},
ajax: {
url: wc_enhanced_select_params.ajax_url,
dataType: 'json',
delay: 250,
data: function( params ) {
return {
term : params.term,
action : $( this ).data( 'action' ) || 'woocommerce_json_search_products_and_variations',
security : wc_enhanced_select_params.search_products_nonce,
exclude : $( this ).data( 'exclude' ),
exclude_type : $( this ).data( 'exclude_type' ),
include : $( this ).data( 'include' ),
limit : $( this ).data( 'limit' ),
display_stock: $( this ).data( 'display_stock' )
};
},
processResults: function( data ) {
var terms = [];
if ( data ) {
$.each( data, function( id, text ) {
terms.push( { id: id, text: text } );
});
}
return {
results: terms
};
},
cache: true
}
};
select2_args = $.extend( select2_args, getEnhancedSelectFormatString() );
$( this ).selectWoo( select2_args ).addClass( 'enhanced' );
if ( $( this ).data( 'sortable' ) ) {
var $select = $(this);
var $list = $( this ).next( '.select2-container' ).find( 'ul.select2-selection__rendered' );
$list.sortable({
placeholder : 'ui-state-highlight select2-selection__choice',
forcePlaceholderSize: true,
items : 'li:not(.select2-search__field)',
tolerance : 'pointer',
stop: function() {
$( $list.find( '.select2-selection__choice' ).get().reverse() ).each( function() {
var id = $( this ).data( 'data' ).id;
var option = $select.find( 'option[value="' + id + '"]' )[0];
$select.prepend( option );
} );
}
});
// Keep multiselects ordered alphabetically if they are not sortable.
} else if ( $( this ).prop( 'multiple' ) ) {
$( this ).on( 'change', function(){
var $children = $( this ).children();
$children.sort(function(a, b){
var atext = a.text.toLowerCase();
var btext = b.text.toLowerCase();
if ( atext > btext ) {
return 1;
}
if ( atext < btext ) {
return -1;
}
return 0;
});
$( this ).html( $children );
});
}
});
// Ajax customer search boxes
$( ':input.wc-customer-search' ).filter( ':not(.enhanced)' ).each( function() {
var select2_args = {
allowClear: $( this ).data( 'allow_clear' ) ? true : false,
placeholder: $( this ).data( 'placeholder' ),
minimumInputLength: $( this ).data( 'minimum_input_length' ) ? $( this ).data( 'minimum_input_length' ) : '1',
escapeMarkup: function( m ) {
return m;
},
ajax: {
url: wc_enhanced_select_params.ajax_url,
dataType: 'json',
delay: 1000,
data: function( params ) {
return {
term: params.term,
action: 'woocommerce_json_search_customers',
security: wc_enhanced_select_params.search_customers_nonce,
exclude: $( this ).data( 'exclude' )
};
},
processResults: function( data ) {
var terms = [];
if ( data ) {
$.each( data, function( id, text ) {
terms.push({
id: id,
text: text
});
});
}
return {
results: terms
};
},
cache: true
}
};
select2_args = $.extend( select2_args, getEnhancedSelectFormatString() );
$( this ).selectWoo( select2_args ).addClass( 'enhanced' );
if ( $( this ).data( 'sortable' ) ) {
var $select = $(this);
var $list = $( this ).next( '.select2-container' ).find( 'ul.select2-selection__rendered' );
$list.sortable({
placeholder : 'ui-state-highlight select2-selection__choice',
forcePlaceholderSize: true,
items : 'li:not(.select2-search__field)',
tolerance : 'pointer',
stop: function() {
$( $list.find( '.select2-selection__choice' ).get().reverse() ).each( function() {
var id = $( this ).data( 'data' ).id;
var option = $select.find( 'option[value="' + id + '"]' )[0];
$select.prepend( option );
} );
}
});
}
});
// Ajax category search boxes
$( ':input.wc-category-search' ).filter( ':not(.enhanced)' ).each( function() {
var select2_args = $.extend( {
allowClear : $( this ).data( 'allow_clear' ) ? true : false,
placeholder : $( this ).data( 'placeholder' ),
minimumInputLength: $( this ).data( 'minimum_input_length' ) ? $( this ).data( 'minimum_input_length' ) : 3,
escapeMarkup : function( m ) {
return m;
},
ajax: {
url: wc_enhanced_select_params.ajax_url,
dataType: 'json',
delay: 250,
data: function( params ) {
return {
term: params.term,
action: 'woocommerce_json_search_categories',
security: wc_enhanced_select_params.search_categories_nonce
};
},
processResults: function( data ) {
var terms = [];
if ( data ) {
$.each( data, function( id, term ) {
terms.push({
id: term.slug,
text: term.formatted_name
});
});
}
return {
results: terms
};
},
cache: true
}
}, getEnhancedSelectFormatString() );
$( this ).selectWoo( select2_args ).addClass( 'enhanced' );
});
})
// WooCommerce Backbone Modal
.on( 'wc_backbone_modal_before_remove', function() {
$( '.wc-enhanced-select, :input.wc-product-search, :input.wc-customer-search' ).filter( '.select2-hidden-accessible' )
.selectWoo( 'close' );
})
.trigger( 'wc-enhanced-select-init' );
$( 'html' ).on( 'click', function( event ) {
if ( this === event.target ) {
$( '.wc-enhanced-select, :input.wc-product-search, :input.wc-customer-search' ).filter( '.select2-hidden-accessible' )
.selectWoo( 'close' );
}
} );
} catch( err ) {
// If select2 failed (conflict?) log the error but don't stop other scripts breaking.
window.console.log( err );
}
});

View File

@ -1,88 +0,0 @@
/* global wc_orders_params */
jQuery( function( $ ) {
if ( typeof wc_orders_params === 'undefined' ) {
return false;
}
/**
* WCOrdersTable class.
*/
var WCOrdersTable = function() {
$( document )
.on( 'click', '.post-type-shop_order .wp-list-table tbody td', this.onRowClick )
.on( 'click', '.order-preview:not(.disabled)', this.onPreview );
};
/**
* Click a row.
*/
WCOrdersTable.prototype.onRowClick = function( e ) {
if ( $( e.target ).filter( 'a, a *, .no-link, .no-link *, button, button *' ).length ) {
return true;
}
if ( window.getSelection && window.getSelection().toString().length ) {
return true;
}
var $row = $( this ).closest( 'tr' ),
href = $row.find( 'a.order-view' ).attr( 'href' );
if ( href && href.length ) {
e.preventDefault();
if ( e.metaKey || e.ctrlKey ) {
window.open( href, '_blank' );
} else {
window.location = href;
}
}
};
/**
* Preview an order.
*/
WCOrdersTable.prototype.onPreview = function() {
var $previewButton = $( this ),
$order_id = $previewButton.data( 'order-id' );
if ( $previewButton.data( 'order-data' ) ) {
$( this ).WCBackboneModal({
template: 'wc-modal-view-order',
variable : $previewButton.data( 'order-data' )
});
} else {
$previewButton.addClass( 'disabled' );
$.ajax({
url: wc_orders_params.ajax_url,
data: {
order_id: $order_id,
action : 'woocommerce_get_order_details',
security: wc_orders_params.preview_nonce
},
type: 'GET',
success: function( response ) {
$( '.order-preview' ).removeClass( 'disabled' );
if ( response.success ) {
$previewButton.data( 'order-data', response.data );
$( this ).WCBackboneModal({
template: 'wc-modal-view-order',
variable : response.data
});
}
}
});
}
return false;
};
/**
* Init WCOrdersTable.
*/
new WCOrdersTable();
} );

View File

@ -1,315 +0,0 @@
/*global wc_setup_params */
/*global wc_setup_currencies */
/*global wc_base_state */
/* @deprecated 4.6.0 */
jQuery( function( $ ) {
function blockWizardUI() {
$('.wc-setup-content').block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
}
$( '.button-next' ).on( 'click', function() {
var form = $( this ).parents( 'form' ).get( 0 );
if ( ( 'function' !== typeof form.checkValidity ) || form.checkValidity() ) {
blockWizardUI();
}
return true;
} );
$( 'form.address-step' ).on( 'submit', function( e ) {
var form = $( this );
if ( ( 'function' !== typeof form.checkValidity ) || form.checkValidity() ) {
blockWizardUI();
}
e.preventDefault();
$('.wc-setup-content').unblock();
$( this ).WCBackboneModal( {
template: 'wc-modal-tracking-setup'
} );
$( document.body ).on( 'wc_backbone_modal_response', function() {
form.unbind( 'submit' ).trigger( 'submit' );
} );
$( '#wc_tracker_checkbox_dialog' ).on( 'change', function( e ) {
var eventTarget = $( e.target );
$( '#wc_tracker_checkbox' ).prop( 'checked', eventTarget.prop( 'checked' ) );
} );
$( '#wc_tracker_submit' ).on( 'click', function () {
form.unbind( 'submit' ).trigger( 'submit' );
} );
return true;
} );
$( '#store_country' ).on( 'change', function() {
// Prevent if we don't have the metabox data
if ( wc_setup_params.states === null ){
return;
}
var $this = $( this ),
country = $this.val(),
$state_select = $( '#store_state' );
if ( ! $.isEmptyObject( wc_setup_params.states[ country ] ) ) {
var states = wc_setup_params.states[ country ];
$state_select.empty();
$.each( states, function( index ) {
$state_select.append( $( '<option value="' + index + '">' + states[ index ] + '</option>' ) );
} );
$( '.store-state-container' ).show();
$state_select.selectWoo().val( wc_base_state ).trigger( 'change' ).prop( 'required', true );
} else {
$( '.store-state-container' ).hide();
$state_select.empty().val( '' ).trigger( 'change' ).prop( 'required', false );
}
$( '#currency_code' ).val( wc_setup_currencies[ country ] ).trigger( 'change' );
} );
/* Setup postcode field and validations */
$( '#store_country' ).on( 'change', function() {
if ( ! wc_setup_params.postcodes ) {
return;
}
var $this = $( this ),
country = $this.val(),
$store_postcode_input = $( '#store_postcode' ),
country_postcode_obj = wc_setup_params.postcodes[ country ];
// Default to required, if its unknown whether postcode is required or not.
if ( $.isEmptyObject( country_postcode_obj ) || country_postcode_obj.required ) {
$store_postcode_input.attr( 'required', 'true' );
} else {
$store_postcode_input.removeAttr( 'required' );
}
} );
$( '#store_country' ).trigger( 'change' );
$( '.wc-wizard-services' ).on( 'change', '.wc-wizard-service-enable input', function() {
if ( $( this ).is( ':checked' ) ) {
$( this ).closest( '.wc-wizard-service-toggle' ).removeClass( 'disabled' );
$( this ).closest( '.wc-wizard-service-item' ).addClass( 'checked' );
$( this ).closest( '.wc-wizard-service-item' )
.find( '.wc-wizard-service-settings' ).removeClass( 'hide' );
} else {
$( this ).closest( '.wc-wizard-service-toggle' ).addClass( 'disabled' );
$( this ).closest( '.wc-wizard-service-item' ).removeClass( 'checked' );
$( this ).closest( '.wc-wizard-service-item' )
.find( '.wc-wizard-service-settings' ).addClass( 'hide' );
}
} );
$( '.wc-wizard-services' ).on( 'keyup', function( e ) {
var code = e.keyCode || e.which,
$focused = $( document.activeElement );
if ( $focused.is( '.wc-wizard-service-toggle, .wc-wizard-service-enable' ) && ( 13 === code || 32 === code ) ) {
$focused.find( ':input' ).trigger( 'click' );
}
} );
$( '.wc-wizard-services' ).on( 'click', '.wc-wizard-service-enable', function( e ) {
var eventTarget = $( e.target );
if ( eventTarget.is( 'input' ) ) {
e.stopPropagation();
return;
}
var $checkbox = $( this ).find( 'input[type="checkbox"]' );
$checkbox.prop( 'checked', ! $checkbox.prop( 'checked' ) ).trigger( 'change' );
} );
$( '.wc-wizard-services-list-toggle' ).on( 'click', function() {
$( this ).closest( '.wc-wizard-services-list-toggle' ).toggleClass( 'closed' );
$( this ).closest( '.wc-wizard-services' ).find( '.wc-wizard-service-item' )
.slideToggle()
.css( 'display', 'flex' );
} );
$( '.wc-wizard-services' ).on( 'change', '.wc-wizard-shipping-method-select .method', function( e ) {
var zone = $( this ).closest( '.wc-wizard-service-description' );
var selectedMethod = e.target.value;
var description = zone.find( '.shipping-method-descriptions' );
description.find( '.shipping-method-description' ).addClass( 'hide' );
description.find( '.' + selectedMethod ).removeClass( 'hide' );
var $checkbox = zone.parent().find( 'input[type="checkbox"]' );
var settings = zone.find( '.shipping-method-settings' );
settings
.find( '.shipping-method-setting' )
.addClass( 'hide' )
.find( '.shipping-method-required-field' )
.prop( 'required', false );
settings
.find( '.' + selectedMethod )
.removeClass( 'hide' )
.find( '.shipping-method-required-field' )
.prop( 'required', $checkbox.prop( 'checked' ) );
} ).find( '.wc-wizard-shipping-method-select .method' ).trigger( 'change' );
$( '.wc-wizard-services' ).on( 'change', '.wc-wizard-shipping-method-enable', function() {
var checked = $( this ).is( ':checked' );
var selectedMethod = $( this )
.closest( '.wc-wizard-service-item' )
.find( '.wc-wizard-shipping-method-select .method' )
.val();
$( this )
.closest( '.wc-wizard-service-item' )
.find( '.' + selectedMethod )
.find( '.shipping-method-required-field' )
.prop( 'required', checked );
} );
function submitActivateForm() {
$( 'form.activate-jetpack' ).trigger( 'submit' );
}
function waitForJetpackInstall() {
wp.ajax.post( 'setup_wizard_check_jetpack' )
.then( function( result ) {
// If we receive success, or an unexpected result
// let the form submit.
if (
! result ||
! result.is_active ||
'yes' === result.is_active
) {
return submitActivateForm();
}
// Wait until checking the status again
setTimeout( waitForJetpackInstall, 3000 );
} )
.fail( function() {
// Submit the form as normal if the request fails
submitActivateForm();
} );
}
// Wait for a pending Jetpack install to finish before triggering a "save"
// on the activate step, which launches the Jetpack connection flow.
$( '.activate-jetpack' ).on( 'click', '.button-primary', function( e ) {
blockWizardUI();
if ( 'no' === wc_setup_params.pending_jetpack_install ) {
return true;
}
e.preventDefault();
waitForJetpackInstall();
} );
$( '.activate-new-onboarding' ).on( 'click', '.button-primary', function() {
// Show pending spinner while activate happens.
blockWizardUI();
} );
$( '.wc-wizard-services' ).on( 'change', 'input#stripe_create_account, input#ppec_paypal_reroute_requests', function() {
if ( $( this ).is( ':checked' ) ) {
$( this ).closest( '.wc-wizard-service-settings' )
.find( 'input.payment-email-input' )
.attr( 'type', 'email' )
.prop( 'disabled', false )
.prop( 'required', true );
} else {
$( this ).closest( '.wc-wizard-service-settings' )
.find( 'input.payment-email-input' )
.attr( 'type', null )
.prop( 'disabled', true )
.prop( 'required', false );
}
} ).find( 'input#stripe_create_account, input#ppec_paypal_reroute_requests' ).trigger( 'change' );
function addPlugins( bySlug, $el, hover ) {
var plugins = $el.data( 'plugins' );
for ( var i in Array.isArray( plugins ) ? plugins : [] ) {
var slug = plugins[ i ].slug;
bySlug[ slug ] = bySlug[ slug ] ||
$( '<span class="plugin-install-info-list-item">' )
.append( '<a href="https://wordpress.org/plugins/' + slug + '/" target="_blank">' + plugins[ i ].name + '</a>' );
bySlug[ slug ].find( 'a' )
.on( 'mouseenter mouseleave', ( function( $hover, event ) {
$hover.toggleClass( 'plugin-install-source', 'mouseenter' === event.type );
} ).bind( null, hover ? $el.closest( hover ) : $el ) );
}
}
function updatePluginInfo() {
var pluginLinkBySlug = {};
var extraPlugins = [];
$( '.wc-wizard-service-enable input:checked' ).each( function() {
addPlugins( pluginLinkBySlug, $( this ), '.wc-wizard-service-item' );
var $container = $( this ).closest( '.wc-wizard-service-item' );
$container.find( 'input.payment-checkbox-input:checked' ).each( function() {
extraPlugins.push( $( this ).attr( 'id' ) );
addPlugins( pluginLinkBySlug, $( this ), '.wc-wizard-service-settings' );
} );
$container.find( '.wc-wizard-shipping-method-select .method' ).each( function() {
var $this = $( this );
if ( 'live_rates' === $this.val() ) {
addPlugins( pluginLinkBySlug, $this, '.wc-wizard-service-item' );
}
} );
} );
$( '.recommended-item input:checked' ).each( function() {
addPlugins( pluginLinkBySlug, $( this ), '.recommended-item' );
} );
var $list = $( 'span.plugin-install-info-list' ).empty();
for ( var slug in pluginLinkBySlug ) {
$list.append( pluginLinkBySlug[ slug ] );
}
if (
extraPlugins &&
wc_setup_params.current_step &&
wc_setup_params.i18n.extra_plugins[ wc_setup_params.current_step ] &&
wc_setup_params.i18n.extra_plugins[ wc_setup_params.current_step ][ extraPlugins.join( ',' ) ]
) {
$list.append(
wc_setup_params.i18n.extra_plugins[ wc_setup_params.current_step ][ extraPlugins.join( ',' ) ]
);
}
$( 'span.plugin-install-info' ).toggle( $list.children().length > 0 );
}
updatePluginInfo();
$( '.wc-setup-content' ).on( 'change', '[data-plugins]', updatePluginInfo );
$( document.body ).on( 'init_tooltips', function() {
$( '.help_tip' ).tipTip( {
'attribute': 'data-tip',
'fadeIn': 50,
'fadeOut': 50,
'delay': 200,
'defaultPosition': 'top'
} );
} ).trigger( 'init_tooltips' );
} );

View File

@ -1,245 +0,0 @@
/* global shippingClassesLocalizeScript, ajaxurl */
( function( $, data, wp, ajaxurl ) {
$( function() {
var $tbody = $( '.wc-shipping-class-rows' ),
$save_button = $( '.wc-shipping-class-save' ),
$row_template = wp.template( 'wc-shipping-class-row' ),
$blank_template = wp.template( 'wc-shipping-class-row-blank' ),
// Backbone model
ShippingClass = Backbone.Model.extend({
changes: {},
logChanges: function( changedRows ) {
var changes = this.changes || {};
_.each( changedRows, function( row, id ) {
changes[ id ] = _.extend( changes[ id ] || { term_id : id }, row );
} );
this.changes = changes;
this.trigger( 'change:classes' );
},
save: function() {
if ( _.size( this.changes ) ) {
$.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_classes_save_changes', {
wc_shipping_classes_nonce : data.wc_shipping_classes_nonce,
changes : this.changes
}, this.onSaveResponse, 'json' );
} else {
shippingClass.trigger( 'saved:classes' );
}
},
discardChanges: function( id ) {
var changes = this.changes || {};
// Delete all changes
delete changes[ id ];
// No changes? Disable save button.
if ( 0 === _.size( this.changes ) ) {
shippingClassView.clearUnloadConfirmation();
}
},
onSaveResponse: function( response, textStatus ) {
if ( 'success' === textStatus ) {
if ( response.success ) {
shippingClass.set( 'classes', response.data.shipping_classes );
shippingClass.trigger( 'change:classes' );
shippingClass.changes = {};
shippingClass.trigger( 'saved:classes' );
} else if ( response.data ) {
window.alert( response.data );
} else {
window.alert( data.strings.save_failed );
}
}
shippingClassView.unblock();
}
} ),
// Backbone view
ShippingClassView = Backbone.View.extend({
rowTemplate: $row_template,
initialize: function() {
this.listenTo( this.model, 'change:classes', this.setUnloadConfirmation );
this.listenTo( this.model, 'saved:classes', this.clearUnloadConfirmation );
this.listenTo( this.model, 'saved:classes', this.render );
$tbody.on( 'change', { view: this }, this.updateModelOnChange );
$( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
$save_button.on( 'click', { view: this }, this.onSubmit );
$( document.body ).on( 'click', '.wc-shipping-class-add', { view: this }, this.onAddNewRow );
$( document.body ).on( 'click', '.wc-shipping-class-save-changes', { view: this }, this.onSubmit );
},
block: function() {
$( this.el ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
},
unblock: function() {
$( this.el ).unblock();
},
render: function() {
var classes = _.indexBy( this.model.get( 'classes' ), 'term_id' ),
view = this;
this.$el.empty();
this.unblock();
if ( _.size( classes ) ) {
// Sort classes
classes = _.sortBy( classes, function( shipping_class ) {
return shipping_class.name;
} );
// Populate $tbody with the current classes
$.each( classes, function( id, rowData ) {
view.renderRow( rowData );
} );
} else {
view.$el.append( $blank_template );
}
},
renderRow: function( rowData ) {
var view = this;
view.$el.append( view.rowTemplate( rowData ) );
view.initRow( rowData );
},
initRow: function( rowData ) {
var view = this;
var $tr = view.$el.find( 'tr[data-id="' + rowData.term_id + '"]');
// Support select boxes
$tr.find( 'select' ).each( function() {
var attribute = $( this ).data( 'attribute' );
$( this ).find( 'option[value="' + rowData[ attribute ] + '"]' ).prop( 'selected', true );
} );
// Make the rows function
$tr.find( '.view' ).show();
$tr.find( '.edit' ).hide();
$tr.find( '.wc-shipping-class-edit' ).on( 'click', { view: this }, this.onEditRow );
$tr.find( '.wc-shipping-class-delete' ).on( 'click', { view: this }, this.onDeleteRow );
$tr.find( '.editing .wc-shipping-class-edit' ).trigger('click');
$tr.find( '.wc-shipping-class-cancel-edit' ).on( 'click', { view: this }, this.onCancelEditRow );
// Editing?
if ( true === rowData.editing ) {
$tr.addClass( 'editing' );
$tr.find( '.wc-shipping-class-edit' ).trigger( 'click' );
}
},
onSubmit: function( event ) {
event.data.view.block();
event.data.view.model.save();
event.preventDefault();
},
onAddNewRow: function( event ) {
event.preventDefault();
var view = event.data.view,
model = view.model,
classes = _.indexBy( model.get( 'classes' ), 'term_id' ),
changes = {},
size = _.size( classes ),
newRow = _.extend( {}, data.default_shipping_class, {
term_id: 'new-' + size + '-' + Date.now(),
editing: true,
newRow : true
} );
changes[ newRow.term_id ] = newRow;
model.logChanges( changes );
view.renderRow( newRow );
$( '.wc-shipping-classes-blank-state' ).remove();
},
onEditRow: function( event ) {
event.preventDefault();
$( this ).closest('tr').addClass('editing');
$( this ).closest('tr').find('.view').hide();
$( this ).closest('tr').find('.edit').show();
event.data.view.model.trigger( 'change:classes' );
},
onDeleteRow: function( event ) {
var view = event.data.view,
model = view.model,
classes = _.indexBy( model.get( 'classes' ), 'term_id' ),
changes = {},
term_id = $( this ).closest('tr').data('id');
event.preventDefault();
if ( classes[ term_id ] ) {
delete classes[ term_id ];
changes[ term_id ] = _.extend( changes[ term_id ] || {}, { deleted : 'deleted' } );
model.set( 'classes', classes );
model.logChanges( changes );
}
view.render();
},
onCancelEditRow: function( event ) {
var view = event.data.view,
model = view.model,
row = $( this ).closest('tr'),
term_id = $( this ).closest('tr').data('id'),
classes = _.indexBy( model.get( 'classes' ), 'term_id' );
event.preventDefault();
model.discardChanges( term_id );
if ( classes[ term_id ] ) {
classes[ term_id ].editing = false;
row.after( view.rowTemplate( classes[ term_id ] ) );
view.initRow( classes[ term_id ] );
}
row.remove();
},
setUnloadConfirmation: function() {
this.needsUnloadConfirm = true;
$save_button.removeAttr( 'disabled' );
},
clearUnloadConfirmation: function() {
this.needsUnloadConfirm = false;
$save_button.attr( 'disabled', 'disabled' );
},
unloadConfirmation: function( event ) {
if ( event.data.view.needsUnloadConfirm ) {
event.returnValue = data.strings.unload_confirmation_msg;
window.event.returnValue = data.strings.unload_confirmation_msg;
return data.strings.unload_confirmation_msg;
}
},
updateModelOnChange: function( event ) {
var model = event.data.view.model,
$target = $( event.target ),
term_id = $target.closest( 'tr' ).data( 'id' ),
attribute = $target.data( 'attribute' ),
value = $target.val(),
classes = _.indexBy( model.get( 'classes' ), 'term_id' ),
changes = {};
if ( ! classes[ term_id ] || classes[ term_id ][ attribute ] !== value ) {
changes[ term_id ] = {};
changes[ term_id ][ attribute ] = value;
}
model.logChanges( changes );
}
} ),
shippingClass = new ShippingClass({
classes: data.classes
} ),
shippingClassView = new ShippingClassView({
model: shippingClass,
el: $tbody
} );
shippingClassView.render();
});
})( jQuery, shippingClassesLocalizeScript, wp, ajaxurl );

View File

@ -1,426 +0,0 @@
/* global shippingZoneMethodsLocalizeScript, ajaxurl */
( function( $, data, wp, ajaxurl ) {
$( function() {
var $table = $( '.wc-shipping-zone-methods' ),
$tbody = $( '.wc-shipping-zone-method-rows' ),
$save_button = $( '.wc-shipping-zone-method-save' ),
$row_template = wp.template( 'wc-shipping-zone-method-row' ),
$blank_template = wp.template( 'wc-shipping-zone-method-row-blank' ),
// Backbone model
ShippingMethod = Backbone.Model.extend({
changes: {},
logChanges: function( changedRows ) {
var changes = this.changes || {};
_.each( changedRows.methods, function( row, id ) {
changes.methods = changes.methods || { methods : {} };
changes.methods[ id ] = _.extend( changes.methods[ id ] || { instance_id : id }, row );
} );
if ( typeof changedRows.zone_name !== 'undefined' ) {
changes.zone_name = changedRows.zone_name;
}
if ( typeof changedRows.zone_locations !== 'undefined' ) {
changes.zone_locations = changedRows.zone_locations;
}
if ( typeof changedRows.zone_postcodes !== 'undefined' ) {
changes.zone_postcodes = changedRows.zone_postcodes;
}
this.changes = changes;
this.trigger( 'change:methods' );
},
save: function() {
$.post(
ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zone_methods_save_changes',
{
wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
changes : this.changes,
zone_id : data.zone_id
},
this.onSaveResponse,
'json'
);
},
onSaveResponse: function( response, textStatus ) {
if ( 'success' === textStatus ) {
if ( response.success ) {
if ( response.data.zone_id !== data.zone_id ) {
data.zone_id = response.data.zone_id;
if ( window.history.pushState ) {
window.history.pushState(
{},
'',
'admin.php?page=wc-settings&tab=shipping&zone_id=' + response.data.zone_id
);
}
}
shippingMethod.set( 'methods', response.data.methods );
shippingMethod.trigger( 'change:methods' );
shippingMethod.changes = {};
shippingMethod.trigger( 'saved:methods' );
} else {
window.alert( data.strings.save_failed );
}
}
}
} ),
// Backbone view
ShippingMethodView = Backbone.View.extend({
rowTemplate: $row_template,
initialize: function() {
this.listenTo( this.model, 'change:methods', this.setUnloadConfirmation );
this.listenTo( this.model, 'saved:methods', this.clearUnloadConfirmation );
this.listenTo( this.model, 'saved:methods', this.render );
$tbody.on( 'change', { view: this }, this.updateModelOnChange );
$tbody.on( 'sortupdate', { view: this }, this.updateModelOnSort );
$( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
$save_button.on( 'click', { view: this }, this.onSubmit );
$( document.body ).on(
'input change',
'#zone_name, #zone_locations, #zone_postcodes',
{ view: this },
this.onUpdateZone
);
$( document.body ).on( 'click', '.wc-shipping-zone-method-settings', { view: this }, this.onConfigureShippingMethod );
$( document.body ).on( 'click', '.wc-shipping-zone-add-method', { view: this }, this.onAddShippingMethod );
$( document.body ).on( 'wc_backbone_modal_response', this.onConfigureShippingMethodSubmitted );
$( document.body ).on( 'wc_backbone_modal_response', this.onAddShippingMethodSubmitted );
$( document.body ).on( 'change', '.wc-shipping-zone-method-selector select', this.onChangeShippingMethodSelector );
$( document.body ).on( 'click', '.wc-shipping-zone-postcodes-toggle', this.onTogglePostcodes );
},
onUpdateZone: function( event ) {
var view = event.data.view,
model = view.model,
value = $( this ).val(),
$target = $( event.target ),
attribute = $target.data( 'attribute' ),
changes = {};
event.preventDefault();
changes[ attribute ] = value;
model.set( attribute, value );
model.logChanges( changes );
view.render();
},
block: function() {
$( this.el ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
},
unblock: function() {
$( this.el ).unblock();
},
render: function() {
var methods = _.indexBy( this.model.get( 'methods' ), 'instance_id' ),
zone_name = this.model.get( 'zone_name' ),
view = this;
// Set name.
$('.wc-shipping-zone-name').text( zone_name ? zone_name : data.strings.default_zone_name );
// Blank out the contents.
this.$el.empty();
this.unblock();
if ( _.size( methods ) ) {
// Sort methods
methods = _.sortBy( methods, function( method ) {
return parseInt( method.method_order, 10 );
} );
// Populate $tbody with the current methods
$.each( methods, function( id, rowData ) {
if ( 'yes' === rowData.enabled ) {
rowData.enabled_icon = '<span class="woocommerce-input-toggle woocommerce-input-toggle--enabled">' +
data.strings.yes +
'</span>';
} else {
rowData.enabled_icon = '<span class="woocommerce-input-toggle woocommerce-input-toggle--disabled">' +
data.strings.no +
'</span>';
}
view.$el.append( view.rowTemplate( rowData ) );
var $tr = view.$el.find( 'tr[data-id="' + rowData.instance_id + '"]');
if ( ! rowData.has_settings ) {
$tr
.find( '.wc-shipping-zone-method-title > a' )
.replaceWith('<span>' + $tr.find( '.wc-shipping-zone-method-title > a' ).text() + '</span>' );
var $del = $tr.find( '.wc-shipping-zone-method-delete' );
$tr.find( '.wc-shipping-zone-method-title .row-actions' ).empty().html($del);
}
} );
// Make the rows function
this.$el.find( '.wc-shipping-zone-method-delete' ).on( 'click', { view: this }, this.onDeleteRow );
this.$el.find( '.wc-shipping-zone-method-enabled a').on( 'click', { view: this }, this.onToggleEnabled );
} else {
view.$el.append( $blank_template );
}
this.initTooltips();
},
initTooltips: function() {
$( '#tiptip_holder' ).removeAttr( 'style' );
$( '#tiptip_arrow' ).removeAttr( 'style' );
$( '.tips' ).tipTip({ 'attribute': 'data-tip', 'fadeIn': 50, 'fadeOut': 50, 'delay': 50 });
},
onSubmit: function( event ) {
event.data.view.block();
event.data.view.model.save();
event.preventDefault();
},
onDeleteRow: function( event ) {
var view = event.data.view,
model = view.model,
methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
changes = {},
instance_id = $( this ).closest('tr').data('id');
event.preventDefault();
delete methods[ instance_id ];
changes.methods = changes.methods || { methods : {} };
changes.methods[ instance_id ] = _.extend( changes.methods[ instance_id ] || {}, { deleted : 'deleted' } );
model.set( 'methods', methods );
model.logChanges( changes );
view.render();
},
onToggleEnabled: function( event ) {
var view = event.data.view,
$target = $( event.target ),
model = view.model,
methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
instance_id = $target.closest( 'tr' ).data( 'id' ),
enabled = $target.closest( 'tr' ).data( 'enabled' ) === 'yes' ? 'no' : 'yes',
changes = {};
event.preventDefault();
methods[ instance_id ].enabled = enabled;
changes.methods = changes.methods || { methods : {} };
changes.methods[ instance_id ] = _.extend( changes.methods[ instance_id ] || {}, { enabled : enabled } );
model.set( 'methods', methods );
model.logChanges( changes );
view.render();
},
setUnloadConfirmation: function() {
this.needsUnloadConfirm = true;
$save_button.removeAttr( 'disabled' );
},
clearUnloadConfirmation: function() {
this.needsUnloadConfirm = false;
$save_button.attr( 'disabled', 'disabled' );
},
unloadConfirmation: function( event ) {
if ( event.data.view.needsUnloadConfirm ) {
event.returnValue = data.strings.unload_confirmation_msg;
window.event.returnValue = data.strings.unload_confirmation_msg;
return data.strings.unload_confirmation_msg;
}
},
updateModelOnChange: function( event ) {
var model = event.data.view.model,
$target = $( event.target ),
instance_id = $target.closest( 'tr' ).data( 'id' ),
attribute = $target.data( 'attribute' ),
value = $target.val(),
methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
changes = {};
if ( methods[ instance_id ][ attribute ] !== value ) {
changes.methods[ instance_id ] = {};
changes.methods[ instance_id ][ attribute ] = value;
methods[ instance_id ][ attribute ] = value;
}
model.logChanges( changes );
},
updateModelOnSort: function( event ) {
var view = event.data.view,
model = view.model,
methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
changes = {};
_.each( methods, function( method ) {
var old_position = parseInt( method.method_order, 10 );
var new_position = parseInt( $table.find( 'tr[data-id="' + method.instance_id + '"]').index() + 1, 10 );
if ( old_position !== new_position ) {
methods[ method.instance_id ].method_order = new_position;
changes.methods = changes.methods || { methods : {} };
changes.methods[ method.instance_id ] = _.extend(
changes.methods[ method.instance_id ] || {}, { method_order : new_position }
);
}
} );
if ( _.size( changes ) ) {
model.logChanges( changes );
}
},
onConfigureShippingMethod: function( event ) {
var instance_id = $( this ).closest( 'tr' ).data( 'id' ),
model = event.data.view.model,
methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
method = methods[ instance_id ];
// Only load modal if supported
if ( ! method.settings_html ) {
return true;
}
event.preventDefault();
$( this ).WCBackboneModal({
template : 'wc-modal-shipping-method-settings',
variable : {
instance_id : instance_id,
method : method
},
data : {
instance_id : instance_id,
method : method
}
});
$( document.body ).trigger( 'init_tooltips' );
},
onConfigureShippingMethodSubmitted: function( event, target, posted_data ) {
if ( 'wc-modal-shipping-method-settings' === target ) {
shippingMethodView.block();
// Save method settings via ajax call
$.post(
ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zone_methods_save_settings',
{
wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
instance_id : posted_data.instance_id,
data : posted_data
},
function( response, textStatus ) {
if ( 'success' === textStatus && response.success ) {
$( 'table.wc-shipping-zone-methods' ).parent().find( '#woocommerce_errors' ).remove();
// If there were errors, prepend the form.
if ( response.data.errors.length > 0 ) {
shippingMethodView.showErrors( response.data.errors );
}
// Method was saved. Re-render.
if ( _.size( shippingMethodView.model.changes ) ) {
shippingMethodView.model.save();
} else {
shippingMethodView.model.onSaveResponse( response, textStatus );
}
} else {
window.alert( data.strings.save_failed );
shippingMethodView.unblock();
}
},
'json'
);
}
},
showErrors: function( errors ) {
var error_html = '<div id="woocommerce_errors" class="error notice is-dismissible">';
$( errors ).each( function( index, value ) {
error_html = error_html + '<p>' + value + '</p>';
} );
error_html = error_html + '</div>';
$( 'table.wc-shipping-zone-methods' ).before( error_html );
},
onAddShippingMethod: function( event ) {
event.preventDefault();
$( this ).WCBackboneModal({
template : 'wc-modal-add-shipping-method',
variable : {
zone_id : data.zone_id
}
});
$( '.wc-shipping-zone-method-selector select' ).trigger( 'change' );
},
onAddShippingMethodSubmitted: function( event, target, posted_data ) {
if ( 'wc-modal-add-shipping-method' === target ) {
shippingMethodView.block();
// Add method to zone via ajax call
$.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zone_add_method', {
wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
method_id : posted_data.add_method_id,
zone_id : data.zone_id
}, function( response, textStatus ) {
if ( 'success' === textStatus && response.success ) {
if ( response.data.zone_id !== data.zone_id ) {
data.zone_id = response.data.zone_id;
if ( window.history.pushState ) {
window.history.pushState(
{},
'',
'admin.php?page=wc-settings&tab=shipping&zone_id=' + response.data.zone_id
);
}
}
// Trigger save if there are changes, or just re-render
if ( _.size( shippingMethodView.model.changes ) ) {
shippingMethodView.model.save();
} else {
shippingMethodView.model.set( 'methods', response.data.methods );
shippingMethodView.model.trigger( 'change:methods' );
shippingMethodView.model.changes = {};
shippingMethodView.model.trigger( 'saved:methods' );
}
}
shippingMethodView.unblock();
}, 'json' );
}
},
onChangeShippingMethodSelector: function() {
var description = $( this ).find( 'option:selected' ).data( 'description' );
$( this ).parent().find( '.wc-shipping-zone-method-description' ).remove();
$( this ).after( '<div class="wc-shipping-zone-method-description">' + description + '</div>' );
$( this ).closest( 'article' ).height( $( this ).parent().height() );
},
onTogglePostcodes: function( event ) {
event.preventDefault();
var $tr = $( this ).closest( 'tr');
$tr.find( '.wc-shipping-zone-postcodes' ).show();
$tr.find( '.wc-shipping-zone-postcodes-toggle' ).hide();
}
} ),
shippingMethod = new ShippingMethod({
methods: data.methods,
zone_name: data.zone_name
} ),
shippingMethodView = new ShippingMethodView({
model: shippingMethod,
el: $tbody
} );
shippingMethodView.render();
$tbody.sortable({
items: 'tr',
cursor: 'move',
axis: 'y',
handle: 'td.wc-shipping-zone-method-sort',
scrollSensitivity: 40
});
});
})( jQuery, shippingZoneMethodsLocalizeScript, wp, ajaxurl );

View File

@ -1,271 +0,0 @@
/* global shippingZonesLocalizeScript, ajaxurl */
( function( $, data, wp, ajaxurl ) {
$( function() {
var $table = $( '.wc-shipping-zones' ),
$tbody = $( '.wc-shipping-zone-rows' ),
$save_button = $( '.wc-shipping-zone-save' ),
$row_template = wp.template( 'wc-shipping-zone-row' ),
$blank_template = wp.template( 'wc-shipping-zone-row-blank' ),
// Backbone model
ShippingZone = Backbone.Model.extend({
changes: {},
logChanges: function( changedRows ) {
var changes = this.changes || {};
_.each( changedRows, function( row, id ) {
changes[ id ] = _.extend( changes[ id ] || { zone_id : id }, row );
} );
this.changes = changes;
this.trigger( 'change:zones' );
},
discardChanges: function( id ) {
var changes = this.changes || {},
set_position = null,
zones = _.indexBy( this.get( 'zones' ), 'zone_id' );
// Find current set position if it has moved since last save
if ( changes[ id ] && changes[ id ].zone_order !== undefined ) {
set_position = changes[ id ].zone_order;
}
// Delete all changes
delete changes[ id ];
// If the position was set, and this zone does exist in DB, set the position again so the changes are not lost.
if ( set_position !== null && zones[ id ] && zones[ id ].zone_order !== set_position ) {
changes[ id ] = _.extend( changes[ id ] || {}, { zone_id : id, zone_order : set_position } );
}
this.changes = changes;
// No changes? Disable save button.
if ( 0 === _.size( this.changes ) ) {
shippingZoneView.clearUnloadConfirmation();
}
},
save: function() {
if ( _.size( this.changes ) ) {
$.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zones_save_changes', {
wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
changes : this.changes
}, this.onSaveResponse, 'json' );
} else {
shippingZone.trigger( 'saved:zones' );
}
},
onSaveResponse: function( response, textStatus ) {
if ( 'success' === textStatus ) {
if ( response.success ) {
shippingZone.set( 'zones', response.data.zones );
shippingZone.trigger( 'change:zones' );
shippingZone.changes = {};
shippingZone.trigger( 'saved:zones' );
} else {
window.alert( data.strings.save_failed );
}
}
}
} ),
// Backbone view
ShippingZoneView = Backbone.View.extend({
rowTemplate: $row_template,
initialize: function() {
this.listenTo( this.model, 'change:zones', this.setUnloadConfirmation );
this.listenTo( this.model, 'saved:zones', this.clearUnloadConfirmation );
this.listenTo( this.model, 'saved:zones', this.render );
$tbody.on( 'change', { view: this }, this.updateModelOnChange );
$tbody.on( 'sortupdate', { view: this }, this.updateModelOnSort );
$( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
$( document.body ).on( 'click', '.wc-shipping-zone-add', { view: this }, this.onAddNewRow );
},
block: function() {
$( this.el ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
},
unblock: function() {
$( this.el ).unblock();
},
render: function() {
var zones = _.indexBy( this.model.get( 'zones' ), 'zone_id' ),
view = this;
view.$el.empty();
view.unblock();
if ( _.size( zones ) ) {
// Sort zones
zones = _( zones )
.chain()
.sortBy( function ( zone ) { return parseInt( zone.zone_id, 10 ); } )
.sortBy( function ( zone ) { return parseInt( zone.zone_order, 10 ); } )
.value();
// Populate $tbody with the current zones
$.each( zones, function( id, rowData ) {
view.renderRow( rowData );
} );
} else {
view.$el.append( $blank_template );
}
view.initRows();
},
renderRow: function( rowData ) {
var view = this;
view.$el.append( view.rowTemplate( rowData ) );
view.initRow( rowData );
},
initRow: function( rowData ) {
var view = this;
var $tr = view.$el.find( 'tr[data-id="' + rowData.zone_id + '"]');
// List shipping methods
view.renderShippingMethods( rowData.zone_id, rowData.shipping_methods );
$tr.find( '.wc-shipping-zone-delete' ).on( 'click', { view: this }, this.onDeleteRow );
},
initRows: function() {
// Stripe
if ( 0 === ( $( 'tbody.wc-shipping-zone-rows tr' ).length % 2 ) ) {
$table.find( 'tbody.wc-shipping-zone-rows' ).next( 'tbody' ).find( 'tr' ).addClass( 'odd' );
} else {
$table.find( 'tbody.wc-shipping-zone-rows' ).next( 'tbody' ).find( 'tr' ).removeClass( 'odd' );
}
// Tooltips
$( '#tiptip_holder' ).removeAttr( 'style' );
$( '#tiptip_arrow' ).removeAttr( 'style' );
$( '.tips' ).tipTip({ 'attribute': 'data-tip', 'fadeIn': 50, 'fadeOut': 50, 'delay': 50 });
},
renderShippingMethods: function( zone_id, shipping_methods ) {
var $tr = $( '.wc-shipping-zones tr[data-id="' + zone_id + '"]');
var $method_list = $tr.find('.wc-shipping-zone-methods ul');
$method_list.find( '.wc-shipping-zone-method' ).remove();
if ( _.size( shipping_methods ) ) {
shipping_methods = _.sortBy( shipping_methods, function( method ) {
return parseInt( method.method_order, 10 );
} );
_.each( shipping_methods, function( shipping_method ) {
var class_name = 'method_disabled';
if ( 'yes' === shipping_method.enabled ) {
class_name = 'method_enabled';
}
$method_list.append(
'<li class="wc-shipping-zone-method ' + class_name + '">' + shipping_method.title + '</li>'
);
} );
} else {
$method_list.append( '<li class="wc-shipping-zone-method">' + data.strings.no_shipping_methods_offered + '</li>' );
}
},
onDeleteRow: function( event ) {
var view = event.data.view,
model = view.model,
zones = _.indexBy( model.get( 'zones' ), 'zone_id' ),
changes = {},
row = $( this ).closest('tr'),
zone_id = row.data('id');
event.preventDefault();
if ( window.confirm( data.strings.delete_confirmation_msg ) ) {
if ( zones[ zone_id ] ) {
delete zones[ zone_id ];
changes[ zone_id ] = _.extend( changes[ zone_id ] || {}, { deleted : 'deleted' } );
model.set( 'zones', zones );
model.logChanges( changes );
event.data.view.block();
event.data.view.model.save();
}
}
},
setUnloadConfirmation: function() {
this.needsUnloadConfirm = true;
$save_button.prop( 'disabled', false );
},
clearUnloadConfirmation: function() {
this.needsUnloadConfirm = false;
$save_button.prop( 'disabled', true );
},
unloadConfirmation: function( event ) {
if ( event.data.view.needsUnloadConfirm ) {
event.returnValue = data.strings.unload_confirmation_msg;
window.event.returnValue = data.strings.unload_confirmation_msg;
return data.strings.unload_confirmation_msg;
}
},
updateModelOnChange: function( event ) {
var model = event.data.view.model,
$target = $( event.target ),
zone_id = $target.closest( 'tr' ).data( 'id' ),
attribute = $target.data( 'attribute' ),
value = $target.val(),
zones = _.indexBy( model.get( 'zones' ), 'zone_id' ),
changes = {};
if ( ! zones[ zone_id ] || zones[ zone_id ][ attribute ] !== value ) {
changes[ zone_id ] = {};
changes[ zone_id ][ attribute ] = value;
}
model.logChanges( changes );
},
updateModelOnSort: function( event ) {
var view = event.data.view,
model = view.model,
zones = _.indexBy( model.get( 'zones' ), 'zone_id' ),
rows = $( 'tbody.wc-shipping-zone-rows tr' ),
changes = {};
// Update sorted row position
_.each( rows, function( row ) {
var zone_id = $( row ).data( 'id' ),
old_position = null,
new_position = parseInt( $( row ).index(), 10 );
if ( zones[ zone_id ] ) {
old_position = parseInt( zones[ zone_id ].zone_order, 10 );
}
if ( old_position !== new_position ) {
changes[ zone_id ] = _.extend( changes[ zone_id ] || {}, { zone_order : new_position } );
}
} );
if ( _.size( changes ) ) {
model.logChanges( changes );
event.data.view.block();
event.data.view.model.save();
}
}
} ),
shippingZone = new ShippingZone({
zones: data.zones
} ),
shippingZoneView = new ShippingZoneView({
model: shippingZone,
el: $tbody
} );
shippingZoneView.render();
$tbody.sortable({
items: 'tr',
cursor: 'move',
axis: 'y',
handle: 'td.wc-shipping-zone-sort',
scrollSensitivity: 40
});
});
})( jQuery, shippingZonesLocalizeScript, wp, ajaxurl );

View File

@ -1,32 +0,0 @@
/*global jQuery */
(function( $ ) {
var recordEvent = function( link ) {
window.wcTracks.recordEvent( 'status_widget_click', {
link: link
} );
};
$( '.sales-this-month a' ).on( 'click', function() {
recordEvent( 'net-sales' );
});
$( '.best-seller-this-month a' ).on( 'click', function() {
recordEvent( 'best-seller-this-month' );
});
$( '.processing-orders a' ).on( 'click', function() {
recordEvent( 'orders-processing' );
});
$( '.on-hold-orders a' ).on( 'click', function() {
recordEvent( 'orders-on-hold' );
});
$( '.low-in-stock a' ).on( 'click', function() {
recordEvent( 'low-stock' );
});
$( '.out-of-stock a' ).on( 'click', function() {
recordEvent( 'out-of-stock' );
});
})( jQuery );

View File

@ -1,411 +0,0 @@
/* global woocommerce_admin */
( function( $, woocommerce_admin ) {
$( function() {
if ( 'undefined' === typeof woocommerce_admin ) {
return;
}
// Add buttons to product screen.
var $product_screen = $( '.edit-php.post-type-product' ),
$title_action = $product_screen.find( '.page-title-action:first' ),
$blankslate = $product_screen.find( '.woocommerce-BlankState' );
if ( 0 === $blankslate.length ) {
if ( woocommerce_admin.urls.export_products ) {
$title_action.after(
'<a href="' +
woocommerce_admin.urls.export_products +
'" class="page-title-action">' +
woocommerce_admin.strings.export_products +
'</a>'
);
}
if ( woocommerce_admin.urls.import_products ) {
$title_action.after(
'<a href="' +
woocommerce_admin.urls.import_products +
'" class="page-title-action">' +
woocommerce_admin.strings.import_products +
'</a>'
);
}
} else {
$title_action.hide();
}
// Progress indicators when showing steps.
$( '.woocommerce-progress-form-wrapper .button-next' ).on( 'click', function() {
$('.wc-progress-form-content').block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
return true;
} );
// Field validation error tips
$( document.body )
.on( 'wc_add_error_tip', function( e, element, error_type ) {
var offset = element.position();
if ( element.parent().find( '.wc_error_tip' ).length === 0 ) {
element.after( '<div class="wc_error_tip ' + error_type + '">' + woocommerce_admin[error_type] + '</div>' );
element.parent().find( '.wc_error_tip' )
.css( 'left', offset.left + element.width() - ( element.width() / 2 ) - ( $( '.wc_error_tip' ).width() / 2 ) )
.css( 'top', offset.top + element.height() )
.fadeIn( '100' );
}
})
.on( 'wc_remove_error_tip', function( e, element, error_type ) {
element.parent().find( '.wc_error_tip.' + error_type ).fadeOut( '100', function() { $( this ).remove(); } );
})
.on( 'click', function() {
$( '.wc_error_tip' ).fadeOut( '100', function() { $( this ).remove(); } );
})
.on( 'blur', '.wc_input_decimal[type=text], .wc_input_price[type=text], .wc_input_country_iso[type=text]', function() {
$( '.wc_error_tip' ).fadeOut( '100', function() { $( this ).remove(); } );
})
.on(
'change',
'.wc_input_price[type=text], .wc_input_decimal[type=text], .wc-order-totals #refund_amount[type=text]',
function() {
var regex, decimalRegex,
decimailPoint = woocommerce_admin.decimal_point;
if ( $( this ).is( '.wc_input_price' ) || $( this ).is( '#refund_amount' ) ) {
decimailPoint = woocommerce_admin.mon_decimal_point;
}
regex = new RegExp( '[^\-0-9\%\\' + decimailPoint + ']+', 'gi' );
decimalRegex = new RegExp( '\\' + decimailPoint + '+', 'gi' );
var value = $( this ).val();
var newvalue = value.replace( regex, '' ).replace( decimalRegex, decimailPoint );
if ( value !== newvalue ) {
$( this ).val( newvalue );
}
}
)
.on(
'keyup',
// eslint-disable-next-line max-len
'.wc_input_price[type=text], .wc_input_decimal[type=text], .wc_input_country_iso[type=text], .wc-order-totals #refund_amount[type=text]',
function() {
var regex, error, decimalRegex;
var checkDecimalNumbers = false;
if ( $( this ).is( '.wc_input_price' ) || $( this ).is( '#refund_amount' ) ) {
checkDecimalNumbers = true;
regex = new RegExp( '[^\-0-9\%\\' + woocommerce_admin.mon_decimal_point + ']+', 'gi' );
decimalRegex = new RegExp( '[^\\' + woocommerce_admin.mon_decimal_point + ']', 'gi' );
error = 'i18n_mon_decimal_error';
} else if ( $( this ).is( '.wc_input_country_iso' ) ) {
regex = new RegExp( '([^A-Z])+|(.){3,}', 'im' );
error = 'i18n_country_iso_error';
} else {
checkDecimalNumbers = true;
regex = new RegExp( '[^\-0-9\%\\' + woocommerce_admin.decimal_point + ']+', 'gi' );
decimalRegex = new RegExp( '[^\\' + woocommerce_admin.decimal_point + ']', 'gi' );
error = 'i18n_decimal_error';
}
var value = $( this ).val();
var newvalue = value.replace( regex, '' );
// Check if newvalue have more than one decimal point.
if ( checkDecimalNumbers && 1 < newvalue.replace( decimalRegex, '' ).length ) {
newvalue = newvalue.replace( decimalRegex, '' );
}
if ( value !== newvalue ) {
$( document.body ).triggerHandler( 'wc_add_error_tip', [ $( this ), error ] );
} else {
$( document.body ).triggerHandler( 'wc_remove_error_tip', [ $( this ), error ] );
}
}
)
.on( 'change', '#_sale_price.wc_input_price[type=text], .wc_input_price[name^=variable_sale_price]', function() {
var sale_price_field = $( this ), regular_price_field;
if ( sale_price_field.attr( 'name' ).indexOf( 'variable' ) !== -1 ) {
regular_price_field = sale_price_field
.parents( '.variable_pricing' )
.find( '.wc_input_price[name^=variable_regular_price]' );
} else {
regular_price_field = $( '#_regular_price' );
}
var sale_price = parseFloat(
window.accounting.unformat( sale_price_field.val(), woocommerce_admin.mon_decimal_point )
);
var regular_price = parseFloat(
window.accounting.unformat( regular_price_field.val(), woocommerce_admin.mon_decimal_point )
);
if ( sale_price >= regular_price ) {
$( this ).val( '' );
}
})
.on( 'keyup', '#_sale_price.wc_input_price[type=text], .wc_input_price[name^=variable_sale_price]', function() {
var sale_price_field = $( this ), regular_price_field;
if ( sale_price_field.attr( 'name' ).indexOf( 'variable' ) !== -1 ) {
regular_price_field = sale_price_field
.parents( '.variable_pricing' )
.find( '.wc_input_price[name^=variable_regular_price]' );
} else {
regular_price_field = $( '#_regular_price' );
}
var sale_price = parseFloat(
window.accounting.unformat( sale_price_field.val(), woocommerce_admin.mon_decimal_point )
);
var regular_price = parseFloat(
window.accounting.unformat( regular_price_field.val(), woocommerce_admin.mon_decimal_point )
);
if ( sale_price >= regular_price ) {
$( document.body ).triggerHandler( 'wc_add_error_tip', [ $(this), 'i18n_sale_less_than_regular_error' ] );
} else {
$( document.body ).triggerHandler( 'wc_remove_error_tip', [ $(this), 'i18n_sale_less_than_regular_error' ] );
}
})
.on( 'init_tooltips', function() {
$( '.tips, .help_tip, .woocommerce-help-tip' ).tipTip( {
'attribute': 'data-tip',
'fadeIn': 50,
'fadeOut': 50,
'delay': 200,
'keepAlive': true
} );
$( '.column-wc_actions .wc-action-button' ).tipTip( {
'fadeIn': 50,
'fadeOut': 50,
'delay': 200
} );
// Add tiptip to parent element for widefat tables
$( '.parent-tips' ).each( function() {
$( this ).closest( 'a, th' ).attr( 'data-tip', $( this ).data( 'tip' ) ).tipTip( {
'attribute': 'data-tip',
'fadeIn': 50,
'fadeOut': 50,
'delay': 200,
'keepAlive': true
} ).css( 'cursor', 'help' );
});
});
// Tooltips
$( document.body ).trigger( 'init_tooltips' );
// wc_input_table tables
$( '.wc_input_table.sortable tbody' ).sortable({
items: 'tr',
cursor: 'move',
axis: 'y',
scrollSensitivity: 40,
forcePlaceholderSize: true,
helper: 'clone',
opacity: 0.65,
placeholder: 'wc-metabox-sortable-placeholder',
start: function( event, ui ) {
ui.item.css( 'background-color', '#f6f6f6' );
},
stop: function( event, ui ) {
ui.item.removeAttr( 'style' );
}
});
// Focus on inputs within the table if clicked instead of trying to sort.
$( '.wc_input_table.sortable tbody input' ).on( 'click', function() {
$( this ).focus();
} );
$( '.wc_input_table .remove_rows' ).on( 'click', function() {
var $tbody = $( this ).closest( '.wc_input_table' ).find( 'tbody' );
if ( $tbody.find( 'tr.current' ).length > 0 ) {
var $current = $tbody.find( 'tr.current' );
$current.each( function() {
$( this ).remove();
});
}
return false;
});
var controlled = false;
var shifted = false;
var hasFocus = false;
$( document.body ).on( 'keyup keydown', function( e ) {
shifted = e.shiftKey;
controlled = e.ctrlKey || e.metaKey;
});
$( '.wc_input_table' ).on( 'focus click', 'input', function( e ) {
var $this_table = $( this ).closest( 'table, tbody' );
var $this_row = $( this ).closest( 'tr' );
if ( ( e.type === 'focus' && hasFocus !== $this_row.index() ) || ( e.type === 'click' && $( this ).is( ':focus' ) ) ) {
hasFocus = $this_row.index();
if ( ! shifted && ! controlled ) {
$( 'tr', $this_table ).removeClass( 'current' ).removeClass( 'last_selected' );
$this_row.addClass( 'current' ).addClass( 'last_selected' );
} else if ( shifted ) {
$( 'tr', $this_table ).removeClass( 'current' );
$this_row.addClass( 'selected_now' ).addClass( 'current' );
if ( $( 'tr.last_selected', $this_table ).length > 0 ) {
if ( $this_row.index() > $( 'tr.last_selected', $this_table ).index() ) {
$( 'tr', $this_table )
.slice( $( 'tr.last_selected', $this_table ).index(), $this_row.index() )
.addClass( 'current' );
} else {
$( 'tr', $this_table )
.slice( $this_row.index(), $( 'tr.last_selected', $this_table ).index() + 1 )
.addClass( 'current' );
}
}
$( 'tr', $this_table ).removeClass( 'last_selected' );
$this_row.addClass( 'last_selected' );
} else {
$( 'tr', $this_table ).removeClass( 'last_selected' );
if ( controlled && $( this ).closest( 'tr' ).is( '.current' ) ) {
$this_row.removeClass( 'current' );
} else {
$this_row.addClass( 'current' ).addClass( 'last_selected' );
}
}
$( 'tr', $this_table ).removeClass( 'selected_now' );
}
}).on( 'blur', 'input', function() {
hasFocus = false;
});
// Additional cost and Attribute term tables
$( '.woocommerce_page_wc-settings .shippingrows tbody tr:even, table.attributes-table tbody tr:nth-child(odd)' )
.addClass( 'alternate' );
// Show order items on orders page
$( document.body ).on( 'click', '.show_order_items', function() {
$( this ).closest( 'td' ).find( 'table' ).toggle();
return false;
});
// Select availability
$( 'select.availability' ).change( function() {
if ( $( this ).val() === 'all' ) {
$( this ).closest( 'tr' ).next( 'tr' ).hide();
} else {
$( this ).closest( 'tr' ).next( 'tr' ).show();
}
}).trigger( 'change' );
// Hidden options
$( '.hide_options_if_checked' ).each( function() {
$( this ).find( 'input:eq(0)' ).change( function() {
if ( $( this ).is( ':checked' ) ) {
$( this )
.closest( 'fieldset, tr' )
.nextUntil( '.hide_options_if_checked, .show_options_if_checked', '.hidden_option' )
.hide();
} else {
$( this )
.closest( 'fieldset, tr' )
.nextUntil( '.hide_options_if_checked, .show_options_if_checked', '.hidden_option' )
.show();
}
}).trigger( 'change' );
});
$( '.show_options_if_checked' ).each( function() {
$( this ).find( 'input:eq(0)' ).change( function() {
if ( $( this ).is( ':checked' ) ) {
$( this )
.closest( 'fieldset, tr' )
.nextUntil( '.hide_options_if_checked, .show_options_if_checked', '.hidden_option' )
.show();
} else {
$( this )
.closest( 'fieldset, tr' )
.nextUntil( '.hide_options_if_checked, .show_options_if_checked', '.hidden_option' )
.hide();
}
}).trigger( 'change' );
});
// Reviews.
$( 'input#woocommerce_enable_reviews' ).change(function() {
if ( $( this ).is( ':checked' ) ) {
$( '#woocommerce_enable_review_rating' ).closest( 'tr' ).show();
} else {
$( '#woocommerce_enable_review_rating' ).closest( 'tr' ).hide();
}
}).trigger( 'change' );
// Attribute term table
$( 'table.attributes-table tbody tr:nth-child(odd)' ).addClass( 'alternate' );
// Toggle gateway on/off.
$( '.wc_gateways' ).on( 'click', '.wc-payment-gateway-method-toggle-enabled', function() {
var $link = $( this ),
$row = $link.closest( 'tr' ),
$toggle = $link.find( '.woocommerce-input-toggle' );
var data = {
action: 'woocommerce_toggle_gateway_enabled',
security: woocommerce_admin.nonces.gateway_toggle,
gateway_id: $row.data( 'gateway_id' )
};
$toggle.addClass( 'woocommerce-input-toggle--loading' );
$.ajax( {
url: woocommerce_admin.ajax_url,
data: data,
dataType : 'json',
type : 'POST',
success: function( response ) {
if ( true === response.data ) {
$toggle.removeClass( 'woocommerce-input-toggle--enabled, woocommerce-input-toggle--disabled' );
$toggle.addClass( 'woocommerce-input-toggle--enabled' );
$toggle.removeClass( 'woocommerce-input-toggle--loading' );
} else if ( false === response.data ) {
$toggle.removeClass( 'woocommerce-input-toggle--enabled, woocommerce-input-toggle--disabled' );
$toggle.addClass( 'woocommerce-input-toggle--disabled' );
$toggle.removeClass( 'woocommerce-input-toggle--loading' );
} else if ( 'needs_setup' === response.data ) {
window.location.href = $link.attr( 'href' );
}
}
} );
return false;
});
$( '#wpbody' ).on( 'click', '#doaction, #doaction2', function() {
var action = $( this ).is( '#doaction' ) ? $( '#bulk-action-selector-top' ).val() : $( '#bulk-action-selector-bottom' ).val();
if ( 'remove_personal_data' === action ) {
return window.confirm( woocommerce_admin.i18n_remove_personal_data_notice );
}
});
});
})( jQuery, woocommerce_admin );

Some files were not shown because too many files have changed in this diff Show More