diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..296fdf8 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +# These are supported funding model platforms +custom: paypal.me/mvelis diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e2b8498 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "" +labels: "bug" +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..a22484a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,10 @@ +--- +name: Question +about: More information is needed or I have questions +title: "" +labels: "question" +assignees: "" +--- + +**Describe the question** +A clear and concise description of what the question is. diff --git a/.github/ISSUE_TEMPLATE/request.md b/.github/ISSUE_TEMPLATE/request.md new file mode 100644 index 0000000..029fa38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/request.md @@ -0,0 +1,41 @@ +--- +name: Feature Request +about: New features and enhacements +title: "" +labels: "enhancement, feature request" +assignees: "" +--- + +### Is your proposal related to a problem? + + + +(Write your answer here.) + +### Describe the solution you'd like + + + +(Describe your proposed solution here.) + +### Describe alternatives you've considered + + + +(Write your answer here.) + +### Additional context + + + +(Write your answer here.) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a3a7e52 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,76 @@ +name: Build and Upload Release Asset + +on: + push: + tags: + - '*' # Runs on any tag push + release: + types: + - published # Runs when a release is published + workflow_dispatch: # Allows manual triggering + +permissions: + contents: write # Required to upload assets + actions: write # Required for actions to work correctly + id-token: write # Required for authentication if necessary + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # Step 1: Checkout code + - name: Checkout code + uses: actions/checkout@v4 + + # Step 2: Set up Node.js (if needed) + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '23' + + # Step 3: Create a build directory and copy necessary files + - name: Prepare build directory + run: | + mkdir build + rsync -av --progress . build/slm-plus --exclude-from=<(echo " + .gitignore + .changelog.md + security.md + _config.yml + composer.json + build.xml + package.json + package-lock.json + .github + .vscode + samples + ") + + # Step 4: Create the ZIP file + - name: Create ZIP archive + run: | + cd build + zip -r ../slm-plus.zip slm-plus + shell: bash + + # Step 5: Create a Release (if push event) + - name: Create a Release (if push) + if: github.event_name == 'push' + run: | + TAG_NAME=$(echo $GITHUB_REF | sed 's/refs\/tags\///') + curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -d '{"tag_name": "'$TAG_NAME'", "name": "'$TAG_NAME'"}' \ + https://api.github.com/repos/${{ github.repository }}/releases + + # Step 6: Upload ZIP to Release (only if release is published) + - name: Upload ZIP to Release + if: github.event_name == 'release' && github.event.action == 'published' + uses: actions/upload-release-asset@v1 + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: slm-plus.zip + asset_name: slm-plus.zip + asset_content_type: application/zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Ensure token is passed correctly diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 14bc68c..dda102d --- a/.gitignore +++ b/.gitignore @@ -1 +1,25 @@ -/nbproject/private/ \ No newline at end of file +/nbproject/private/ +sftp-config_.json +sftp-config.json +sftp-config-alt.json +package-lock.json +.Icon +/node_modules/ +/node_modules/* +.vscode +/samples/ +samples/LicenseAPI.php +samples/LicenseAPI.php +samples/GetLicenseInfo.php +samples/DeactivateLicense.php +samples/CreateLicense.php +samples/CoreConfig.php +samples/CheckLicense.php +samples/LicenseAPI.php +samples/GetLicenseInfo.php +samples/DeactivateLicense.php +samples/CreateLicense.php +samples/CoreConfig.php +samples/CheckLicense.php +samples/GetLicenseInfo.php +samples/DeactivateLicense.php diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..612eaac --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b9d33c6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,430 @@ +# Changelog + +## [6.1.7](https://github.com/michelve/software-license-manager/tree/6.1.7) (2024-11-06) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/6.1.6...6.1.7) + +**Implemented enhancements:** + +- Dev 6.1.7 Release [\#102](https://github.com/michelve/software-license-manager/pull/102) ([michelve](https://github.com/michelve)) + +**Fixed bugs:** + +- Issue with add to cart button [\#99](https://github.com/michelve/software-license-manager/issues/99) + +## [6.1.6](https://github.com/michelve/software-license-manager/tree/6.1.6) (2024-11-03) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/6.1.4...6.1.6) + +## [6.1.4](https://github.com/michelve/software-license-manager/tree/6.1.4) (2024-11-01) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.6.2...6.1.4) + +**Implemented enhancements:** + +- Automatic generation of license code for existing customer orders \(for orders before installing the plugin\) [\#96](https://github.com/michelve/software-license-manager/issues/96) + +**Fixed bugs:** + +- WooCommerce 7.x not creating Keys on order completion. [\#93](https://github.com/michelve/software-license-manager/issues/93) +- Error on Activation [\#89](https://github.com/michelve/software-license-manager/issues/89) +- Double Plugin entries [\#88](https://github.com/michelve/software-license-manager/issues/88) + +**Closed issues:** + +- \[Question\] Smart way to integrate license product type with WC subscriptions? [\#86](https://github.com/michelve/software-license-manager/issues/86) + +**Merged pull requests:** + +- There are 6 TD tags and not 5 Update wc\_licenses\_class.php [\#98](https://github.com/michelve/software-license-manager/pull/98) ([ricardo777](https://github.com/ricardo777)) +- Remove unnecessary metadata [\#95](https://github.com/michelve/software-license-manager/pull/95) ([fabolivark](https://github.com/fabolivark)) +- Small fixes to license info view [\#94](https://github.com/michelve/software-license-manager/pull/94) ([MechComp](https://github.com/MechComp)) +- Correct error on activate that trys to load a non-existent option. [\#90](https://github.com/michelve/software-license-manager/pull/90) ([Narimm](https://github.com/Narimm)) +- Bump minimist from 1.2.5 to 1.2.6 [\#85](https://github.com/michelve/software-license-manager/pull/85) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Bump node-fetch from 2.6.1 to 2.6.7 [\#80](https://github.com/michelve/software-license-manager/pull/80) ([dependabot[bot]](https://github.com/apps/dependabot)) + +## [5.6.2](https://github.com/michelve/software-license-manager/tree/5.6.2) (2022-06-23) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.6.1...5.6.2) + +**Implemented enhancements:** + +- License status check [\#79](https://github.com/michelve/software-license-manager/issues/79) +- Would you mind using escape functions. [\#40](https://github.com/michelve/software-license-manager/issues/40) +- Translation issues [\#22](https://github.com/michelve/software-license-manager/issues/22) + +## [5.6.1](https://github.com/michelve/software-license-manager/tree/5.6.1) (2022-01-06) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.12...5.6.1) + +**Implemented enhancements:** + +- Would you ming adding toggle button to open license infor panel in wc\_licenses\_class.php [\#44](https://github.com/michelve/software-license-manager/issues/44) +- Features Request / Enhancement: Let users give their licenses to someone else [\#35](https://github.com/michelve/software-license-manager/issues/35) + +**Closed issues:** + +- missing key [\#58](https://github.com/michelve/software-license-manager/issues/58) + +**Merged pull requests:** + +- Update slm-add-licenses.php [\#71](https://github.com/michelve/software-license-manager/pull/71) ([ghost](https://github.com/ghost)) +- Update slm-list-licenses-class.php [\#70](https://github.com/michelve/software-license-manager/pull/70) ([ghost](https://github.com/ghost)) +- Update slm\_about\_menu.php [\#69](https://github.com/michelve/software-license-manager/pull/69) ([ghost](https://github.com/ghost)) +- Update stats.php [\#68](https://github.com/michelve/software-license-manager/pull/68) ([ghost](https://github.com/ghost)) +- Update slm-utility.php [\#67](https://github.com/michelve/software-license-manager/pull/67) ([ghost](https://github.com/ghost)) +- Update purchase.php [\#66](https://github.com/michelve/software-license-manager/pull/66) ([ghost](https://github.com/ghost)) +- Update slm-lic-settings.php [\#65](https://github.com/michelve/software-license-manager/pull/65) ([ghost](https://github.com/ghost)) +- Update slm-admin-functions.php [\#64](https://github.com/michelve/software-license-manager/pull/64) ([ghost](https://github.com/ghost)) +- Update slm-add-licenses.php [\#63](https://github.com/michelve/software-license-manager/pull/63) ([ghost](https://github.com/ghost)) +- Update slm-subscribers.php [\#62](https://github.com/michelve/software-license-manager/pull/62) ([ghost](https://github.com/ghost)) +- Bump handlebars from 4.5.3 to 4.7.7 [\#59](https://github.com/michelve/software-license-manager/pull/59) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Front-end licence removal doesn't reflect setting allow\_user\_activation\_removal [\#57](https://github.com/michelve/software-license-manager/pull/57) ([MechComp](https://github.com/MechComp)) + +## [5.5.12](https://github.com/michelve/software-license-manager/tree/5.5.12) (2020-10-29) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.11...5.5.12) + +## [5.5.11](https://github.com/michelve/software-license-manager/tree/5.5.11) (2020-10-28) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.10...5.5.11) + +**Implemented enhancements:** + +- Clear Logs saved [\#51](https://github.com/michelve/software-license-manager/issues/51) + +## [5.5.10](https://github.com/michelve/software-license-manager/tree/5.5.10) (2020-10-27) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.9...5.5.10) + +**Implemented enhancements:** + +- Would you mind using escape functions. [\#40](https://github.com/michelve/software-license-manager/issues/40) + +**Merged pull requests:** + +- Bug-fix - multiple licences created when order item qty changed [\#50](https://github.com/michelve/software-license-manager/pull/50) ([MechComp](https://github.com/MechComp)) + +## [5.5.9](https://github.com/michelve/software-license-manager/tree/5.5.9) (2020-09-14) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.8...5.5.9) + +**Implemented enhancements:** + +- Would you ming using wp\_enqueue\_script & wp\_enqueue\_style in wc\_licenses\_class.php? [\#43](https://github.com/michelve/software-license-manager/issues/43) +- Would you mind using home\_url instead of site\_url [\#39](https://github.com/michelve/software-license-manager/issues/39) +- date\(\) is used in this plugin. Would you mind using wp\_date\(\) introduced in WP 5.3? [\#29](https://github.com/michelve/software-license-manager/issues/29) + +**Fixed bugs:** + +- Translation issue due to no text domain in wc\_licenses\_class.php [\#45](https://github.com/michelve/software-license-manager/issues/45) +- It seems that "slm-front-end.css" does not load on slm 5.5.8 \(in wc\_licenses\_class.php\) [\#42](https://github.com/michelve/software-license-manager/issues/42) +- There is no link string for "slm-view" in wc\_licenses\_class.php [\#41](https://github.com/michelve/software-license-manager/issues/41) +- HTML markup is incorrect in wc\_licenses\_class.php [\#38](https://github.com/michelve/software-license-manager/issues/38) + +**Merged pull requests:** + +- I created bug fix for wc\_licenses\_class.php [\#46](https://github.com/michelve/software-license-manager/pull/46) ([k-kikuchi-waverworks](https://github.com/k-kikuchi-waverworks)) +- Bump node-fetch from 2.6.0 to 2.6.1 [\#37](https://github.com/michelve/software-license-manager/pull/37) ([dependabot[bot]](https://github.com/apps/dependabot)) + +## [5.5.8](https://github.com/michelve/software-license-manager/tree/5.5.8) (2020-09-08) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.7...5.5.8) + +**Implemented enhancements:** + +- Software License Manager changes theme [\#30](https://github.com/michelve/software-license-manager/issues/30) + +**Closed issues:** + +- my license [\#32](https://github.com/michelve/software-license-manager/issues/32) + +## [5.5.7](https://github.com/michelve/software-license-manager/tree/5.5.7) (2020-09-02) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/v5.7.0...5.5.7) + +**Closed issues:** + +- On License management page\(for WooCommerce\), table heading datas are not displayed properly on SP. [\#33](https://github.com/michelve/software-license-manager/issues/33) +- what is difference [\#31](https://github.com/michelve/software-license-manager/issues/31) + +**Merged pull requests:** + +- implemneted data-title support [\#34](https://github.com/michelve/software-license-manager/pull/34) ([michelve](https://github.com/michelve)) + +## [v5.7.0](https://github.com/michelve/software-license-manager/tree/v5.7.0) (2020-08-10) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.6...v5.7.0) + +## [5.5.6](https://github.com/michelve/software-license-manager/tree/5.5.6) (2020-08-10) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.5...5.5.6) + +**Merged pull requests:** + +- 5.5.5 - Order mail fix [\#28](https://github.com/michelve/software-license-manager/pull/28) ([MechComp](https://github.com/MechComp)) + +## [5.5.5](https://github.com/michelve/software-license-manager/tree/5.5.5) (2020-07-11) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.4...5.5.5) + +**Merged pull requests:** + +- 5.5.4 - bugfix - validation of licence fields - product editor [\#26](https://github.com/michelve/software-license-manager/pull/26) ([MechComp](https://github.com/MechComp)) +- 5.5.4 - Orders backward compatibility view; fixed icon [\#25](https://github.com/michelve/software-license-manager/pull/25) ([MechComp](https://github.com/MechComp)) + +## [5.5.4](https://github.com/michelve/software-license-manager/tree/5.5.4) (2020-06-24) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.3...5.5.4) + +**Merged pull requests:** + +- MechComp [\#24](https://github.com/michelve/software-license-manager/pull/24) ([MechComp](https://github.com/MechComp)) + +## [5.5.3](https://github.com/michelve/software-license-manager/tree/5.5.3) (2020-05-18) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.2...5.5.3) + +## [5.5.2](https://github.com/michelve/software-license-manager/tree/5.5.2) (2020-05-03) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.1...5.5.2) + +## [5.5.1](https://github.com/michelve/software-license-manager/tree/5.5.1) (2020-04-30) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.5.0...5.5.1) + +## [5.5.0](https://github.com/michelve/software-license-manager/tree/5.5.0) (2020-04-28) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.4.2...5.5.0) + +## [5.4.2](https://github.com/michelve/software-license-manager/tree/5.4.2) (2020-04-28) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.4.1...5.4.2) + +**Implemented enhancements:** + +- License Manager Add or Edit Tabs not working [\#20](https://github.com/michelve/software-license-manager/issues/20) + +## [5.4.1](https://github.com/michelve/software-license-manager/tree/5.4.1) (2020-04-27) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.3.3...5.4.1) + +## [5.3.3](https://github.com/michelve/software-license-manager/tree/5.3.3) (2020-03-31) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.3.2...5.3.3) + +## [5.3.2](https://github.com/michelve/software-license-manager/tree/5.3.2) (2020-03-31) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.3.1...5.3.2) + +## [5.3.1](https://github.com/michelve/software-license-manager/tree/5.3.1) (2020-03-31) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.2.1...5.3.1) + +## [5.2.1](https://github.com/michelve/software-license-manager/tree/5.2.1) (2020-03-25) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.2.0...5.2.1) + +**Fixed bugs:** + +- devices and domains menu [\#16](https://github.com/michelve/software-license-manager/issues/16) + +**Merged pull requests:** + +- Sorting by ID not working properly [\#19](https://github.com/michelve/software-license-manager/pull/19) ([MechComp](https://github.com/MechComp)) +- Sorting by ID not working properly [\#18](https://github.com/michelve/software-license-manager/pull/18) ([MechComp](https://github.com/MechComp)) +- Error - sets all licences as Expired if they don't have Expiration da… [\#17](https://github.com/michelve/software-license-manager/pull/17) ([MechComp](https://github.com/MechComp)) + +## [5.2.0](https://github.com/michelve/software-license-manager/tree/5.2.0) (2020-03-17) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.9...5.2.0) + +**Implemented enhancements:** + +- Integrate with WooCommerce Subscriptions [\#10](https://github.com/michelve/software-license-manager/issues/10) + +**Closed issues:** + +- Users with blank $wc\_billing\_email can see all other licenses [\#15](https://github.com/michelve/software-license-manager/issues/15) + +## [5.1.9](https://github.com/michelve/software-license-manager/tree/5.1.9) (2020-02-27) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.8...5.1.9) + +**Implemented enhancements:** + +- Log and export option [\#13](https://github.com/michelve/software-license-manager/issues/13) + +**Closed issues:** + +- Complete order email displays incorrect expiration date info [\#14](https://github.com/michelve/software-license-manager/issues/14) + +## [5.1.8](https://github.com/michelve/software-license-manager/tree/5.1.8) (2020-01-20) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.7...5.1.8) + +**Implemented enhancements:** + +- Log and export option [\#13](https://github.com/michelve/software-license-manager/issues/13) + +**Closed issues:** + +- Complete order email displays incorrect expiration date info [\#14](https://github.com/michelve/software-license-manager/issues/14) + +**Merged pull requests:** + +- Bump handlebars from 4.1.2 to 4.5.3 [\#12](https://github.com/michelve/software-license-manager/pull/12) ([dependabot[bot]](https://github.com/apps/dependabot)) + +## [5.1.7](https://github.com/michelve/software-license-manager/tree/5.1.7) (2019-12-19) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.6...5.1.7) + +**Implemented enhancements:** + +- Integrate with WooCommerce Subscriptions [\#10](https://github.com/michelve/software-license-manager/issues/10) + +**Fixed bugs:** + +- Integration with newest WooCommerce not working [\#11](https://github.com/michelve/software-license-manager/issues/11) + +## [5.1.6](https://github.com/michelve/software-license-manager/tree/5.1.6) (2019-11-16) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.5...5.1.6) + +**Implemented enhancements:** + +- Getting Fatal error when install without WooCpmmerce [\#9](https://github.com/michelve/software-license-manager/issues/9) + +**Fixed bugs:** + +- Integration with newest WooCommerce not working [\#11](https://github.com/michelve/software-license-manager/issues/11) + +## [5.1.5](https://github.com/michelve/software-license-manager/tree/5.1.5) (2019-11-07) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.4...5.1.5) + +**Implemented enhancements:** + +- Product type "License product" broken again [\#8](https://github.com/michelve/software-license-manager/issues/8) +- Getting Fatal error when install without WooCpmmerce [\#9](https://github.com/michelve/software-license-manager/issues/9) + +## [5.1.4](https://github.com/michelve/software-license-manager/tree/5.1.4) (2019-09-04) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.3...5.1.4) + +**Implemented enhancements:** + +- License does not set itself to expired automatically [\#7](https://github.com/michelve/software-license-manager/issues/7) +- Product type "License product" broken again [\#8](https://github.com/michelve/software-license-manager/issues/8) + +## [5.1.3](https://github.com/michelve/software-license-manager/tree/5.1.3) (2019-08-31) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.2...5.1.3) + +**Implemented enhancements:** + +- License does not set itself to expired automatically [\#7](https://github.com/michelve/software-license-manager/issues/7) + +## [5.1.2](https://github.com/michelve/software-license-manager/tree/5.1.2) (2019-08-30) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.1.0...5.1.2) + +**Closed issues:** + +- Licensed Product type not actually Purchasable in WooCommerce [\#5](https://github.com/michelve/software-license-manager/issues/5) + +## [5.1.0](https://github.com/michelve/software-license-manager/tree/5.1.0) (2019-08-28) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.0.5...5.1.0) + +**Closed issues:** + +- Unable to add Product of type "License Manager" [\#4](https://github.com/michelve/software-license-manager/issues/4) + +## [5.0.5](https://github.com/michelve/software-license-manager/tree/5.0.5) (2019-08-24) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/v.5.0.4...5.0.5) + +**Closed issues:** + +- Unable to add Product of type "License Manager" [\#4](https://github.com/michelve/software-license-manager/issues/4) + +## [v.5.0.4](https://github.com/michelve/software-license-manager/tree/v.5.0.4) (2019-07-30) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/v.5.0.3...v.5.0.4) + +## [v.5.0.3](https://github.com/michelve/software-license-manager/tree/v.5.0.3) (2019-07-30) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.0.3...v.5.0.3) + +## [5.0.3](https://github.com/michelve/software-license-manager/tree/5.0.3) (2019-07-27) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.0.2-patched...5.0.3) + +## [5.0.2-patched](https://github.com/michelve/software-license-manager/tree/5.0.2-patched) (2019-07-17) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/5.0.2...5.0.2-patched) + +## [5.0.2](https://github.com/michelve/software-license-manager/tree/5.0.2) (2019-07-01) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.12.1...5.0.2) + +## [4.12.1](https://github.com/michelve/software-license-manager/tree/4.12.1) (2019-06-17) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.11.2...4.12.1) + +## [4.11.2](https://github.com/michelve/software-license-manager/tree/4.11.2) (2019-06-12) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.10.3...4.11.2) + +## [4.10.3](https://github.com/michelve/software-license-manager/tree/4.10.3) (2019-05-14) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.8...4.10.3) + +## [4.8](https://github.com/michelve/software-license-manager/tree/4.8) (2019-04-01) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.7...4.8) + +## [4.7](https://github.com/michelve/software-license-manager/tree/4.7) (2019-03-07) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.6...4.7) + +## [4.6](https://github.com/michelve/software-license-manager/tree/4.6) (2018-12-31) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.06...4.6) + +## [4.06](https://github.com/michelve/software-license-manager/tree/4.06) (2018-12-31) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.5...4.06) + +## [4.5](https://github.com/michelve/software-license-manager/tree/4.5) (2018-12-04) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.4...4.5) + +## [4.4](https://github.com/michelve/software-license-manager/tree/4.4) (2018-12-02) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.3...4.4) + +**Closed issues:** + +- Make Expiry and Renewal dates optional? [\#1](https://github.com/michelve/software-license-manager/issues/1) + +## [4.3](https://github.com/michelve/software-license-manager/tree/4.3) (2018-11-30) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.2...4.3) + +## [4.2](https://github.com/michelve/software-license-manager/tree/4.2) (2018-08-08) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/4.1...4.2) + +## [4.1](https://github.com/michelve/software-license-manager/tree/4.1) (2018-07-13) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/3.1.0...4.1) + +## [3.1.0](https://github.com/michelve/software-license-manager/tree/3.1.0) (2017-07-19) + +[Full Changelog](https://github.com/michelve/software-license-manager/compare/27811e4c2cb6f458b3fc89b8aee637965e49c538...3.1.0) + + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/LICENSE.md b/LICENSE.md new file mode 100755 index 0000000..26c64ef --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +### Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +1. Source Code. + +The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + +The Corresponding Source for a work in source code form is that +same work. + +2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +## END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 2019 Michel Velis + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + SLM Plus Copyright (C) 2020 Michel Velis + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + +The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 970fd41..34106cd --- a/README.md +++ b/README.md @@ -1,2 +1,164 @@ -software-license-manager -======================== +# Welcome to SLM Plus 👋 + +![Version](https://img.shields.io/github/v/release/michelve/software-license-manager?color=blue) +[![Documentation](https://img.shields.io/badge/documentation-yes-brightgreen.svg)](https://documenter.getpostman.com/view/307939/6tjU1FL?version=latest) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/michelve/software-license-manager/blob/master/LICENSE.md) + + +🔐 SLM Plus - Enhanced SLM Plus for WordPress + +SLM Plus is a robust and customizable license management plugin for WordPress, built to integrate seamlessly with WooCommerce and WP eStore. Designed to provide comprehensive license generation, validation, and tracking capabilities, SLM Plus simplifies software licensing workflows, ensuring secure, efficient distribution and control of your digital products. + +## Key Features +- WooCommerce & WP eStore Compatibility: Fully integrates with both platforms, enabling automated license key generation and management upon product purchase. +- Flexible License Types: Supports varied license models, including subscription-based and lifetime licenses, with adjustable terms and expiration settings. +- Secure API: Offers a secure API for license creation and validation, providing reliable protection for digital goods and software products. +- Advanced Configuration Options: Customize license settings, including device limits, domain constraints, and renewal reminders, all from a centralized admin interface. +- Bulk License Generation: Efficiently issue licenses for past WooCommerce orders with the "Generate Licenses" tool, ensuring complete licensing coverage across all sales. + +SLM Plus is the ideal solution for developers, digital product vendors, and businesses seeking a powerful, easy-to-manage license manager that scales with growth. + +### 🏠 [Homepage](https://github.com/michelve/software-license-manager#readme) + +## 🔧 Install + +```text +1. Go to the Add New Plugins screen in your WordPress admin area +2. Click the upload tab +3. Browse for the plugin file (slm-plus.zip) +4. Click Install Now and then activate the plugin +``` + +### Sample Files Overview +- **CoreConfig.php**: Sets up global constants and utility methods for API requests and responses. +- **LicenseAPI.php**: Provides core methods for each license management action, using `CoreConfig.php` for secure requests. +- **Action Files**: + - `CreateLicense.php`: Handles license creation. + - `ActivateLicense.php`: Activates a license for a specific domain or device. + - `DeactivateLicense.php`: Manages deactivation of a license. + - `CheckLicense.php`: Checks the current status of a license. + - `GetLicenseInfo.php`: Retrieves detailed information about a license. + +Refer to each [wiki page](https://github.com/michelve/software-license-manager/wiki) for in-depth guides on using these files. + + +## Author + +👤 **Michel Velis and Tips and Tricks HQ** + +- Github: [@michelve](https://github.com/michelve) + +## 🤝 Contributing + +Contributions, issues, and feature requests are welcome! + +Feel free to check [issues page](https://github.com/michelve/software-license-manager/issues). + +## Show your support + +Give a ⭐️ if this project helped you! + +## 📓 Postman samples: + +[API Demo and Samples:](https://documenter.getpostman.com/view/307939/6tjU1FL?version=latest) + +## 📦SLM Plus Features + +- **Create License Keys**: Easily generate unique license keys for applications. +- **Remote License Management**: + - Remotely **check**, **activate**, **deactivate**, **update**, and **delete** license keys from within your application. + - **Track status**, **activation dates**, and **usage locations** for each license key. +- **License Activity Monitoring**: + - View detailed **usage logs** and **activation history** for each license key. + - Monitor **requests** and **activities** associated with each license. +- **Manual and Bulk License Creation**: + - Manually create licenses from the admin dashboard. + - **Bulk license generation** for WooCommerce orders, including orders placed before plugin activation. +- **WooCommerce Integration**: + - **Attach license data** directly to WooCommerce orders and display details within each order. + - Support for **custom WooCommerce product types** related to license management. +- **User and Admin Features**: + - **Admin widgets** for license stats and key metrics. + - **Export licenses** for both admins and users. + - Allow users to **view**, **activate**, and **manage licenses** from their WooCommerce “My Account” page. +- **Enhanced License Management**: + - **Bulk actions** support for efficient license handling. + - **View licenses by subscriber** and access detailed activity logs per license. +- **Notification and Expiration Management**: + - **Email notifications** for expiration, activation, and renewal reminders. + - Configure **custom expiration terms** and automate reminders for users. +- **Multilingual Support**: Available in **English** and **Spanish** with additional language support planned. +- **Admin Tools and Security**: + - Flexible **API endpoints** for integration. + - Enhanced **security measures** and **data verification** on each action for safe data handling. + +This feature set offers complete license management for WordPress and WooCommerce environments, providing enhanced control, security, and visibility for admins and end-users. + + +### How to Use the New Shortcodes and Blocks + +#### Shortcodes: +1. **Forgot License**: `[slm_forgot_license]` + - Displays a form where users can enter their email to retrieve license information. + - Can be added to any page or post manually. + +2. **List Licenses**: `[slm_list_licenses]` + - Dynamically displays a table of licenses associated with the logged-in user. + - Useful for pages where users manage their licenses. + +#### Blocks: +1. **Forgot License Block**: + - Found under the "SLM Plus" category in the block editor. + - Provides an interactive form preview in the editor. + - On the frontend, renders the `[slm_forgot_license]` shortcode. + + +## ✅ Compatibility + +- [-] Woocommerce +- [-] WP eStore +- [-] WP Download Manager + +## 🕘 Changelog and history + +Changelog: [View changelog](https://github.com/michelve/software-license-manager/blob/master/CHANGELOG.md) + +## 📄 Documentation and Wiki + +For a detailed guide on each action, refer to the new wiki pages. + +## 🎑 Screenshots + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +SLM Plus + +## 📝 License + +Copyright © 2024 [Michel Velis and Tips and Tricks HQ](https://github.com/michelve). + +This project is [MIT](https://github.com/michelve/software-license-manager/blob/master/LICENSE.md) licensed. \ No newline at end of file diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/admin/includes/cronjobs/slm-tasks.php b/admin/includes/cronjobs/slm-tasks.php new file mode 100644 index 0000000..2311e90 --- /dev/null +++ b/admin/includes/cronjobs/slm-tasks.php @@ -0,0 +1,138 @@ + 86400, + 'display' => __('Every 24 Hours', 'slm-plus'), + ); + return $schedules; +} + +// Schedule the event if not already scheduled +if (!wp_next_scheduled('slm_expired_send_email_reminder')) { + wp_schedule_event(time(), 'slm_daily', 'slm_expired_send_email_reminder'); +} + +// Run license check and send email reminders +add_action('slm_expired_send_email_reminder', 'slm_run_license_check'); + +function slm_run_license_check() { + try { + // Run the expiration check, ensuring it returns an array with both expired and reinstated licenses + $result = SLM_Utility::check_for_expired_lic(); + + // Validate the structure of the returned result + if (!is_array($result) || !isset($result['expired_licenses'], $result['reinstated_licenses'])) { + SLM_Helper_Class::write_log('Unexpected result format from check_for_expired_lic.'); + return []; // Return empty array if result format is unexpected + } + + // Process and log expired licenses if any + if (!empty($result['expired_licenses'])) { + $expired_license_keys = []; + foreach ($result['expired_licenses'] as $license) { + // Assuming each license in expired_licenses is a license key + if (is_string($license)) { + $expired_license_keys[] = $license; + } elseif (is_array($license) && isset($license['license_key'])) { + $expired_license_keys[] = $license['license_key']; + } + } + SLM_Helper_Class::write_log('Expired licenses: ' . implode(', ', $expired_license_keys)); + } else { + SLM_Helper_Class::write_log('No expired licenses found.'); + } + + // Process and log reinstated licenses if any + if (!empty($result['reinstated_licenses'])) { + $reinstated_license_keys = []; + foreach ($result['reinstated_licenses'] as $license) { + // Assuming each license in reinstated_licenses is a license key + if (is_string($license)) { + $reinstated_license_keys[] = $license; + } elseif (is_array($license) && isset($license['license_key'])) { + $reinstated_license_keys[] = $license['license_key']; + } + } + SLM_Helper_Class::write_log('Reinstated licenses: ' . implode(', ', $reinstated_license_keys)); + } else { + SLM_Helper_Class::write_log('No licenses were reinstated.'); + } + + // Return the full result array + return $result; + } catch (Exception $e) { + // Log error if the check fails + SLM_Helper_Class::write_log('Error in slm_run_license_check: ' . $e->getMessage()); + return []; // Return empty array if an error occurred + } +} + + +// Clear the scheduled event on plugin deactivation to avoid duplicate schedules +register_deactivation_hook(__FILE__, 'slm_clear_scheduled_events'); +function slm_clear_scheduled_events() { + $timestamp = wp_next_scheduled('slm_expired_send_email_reminder'); + if ($timestamp) { + wp_unschedule_event($timestamp, 'slm_expired_send_email_reminder'); + } +} + +// Add a custom admin page button to run the license check manually +add_action('admin_menu', 'slm_add_manual_license_check_page'); +function slm_add_manual_license_check_page() { + add_submenu_page( + 'tools.php', // Parent slug, 'Tools' menu + __('Run License Check', 'slm-plus'), // Page title + __('Run License Check', 'slm-plus'), // Menu title + 'manage_options', // Capability required + 'slm-manual-license-check', // Menu slug + 'slm_manual_license_check_page' // Callback function + ); +} + +// Display the button, handle the manual check, and show results +function slm_manual_license_check_page() { + // Check user capability + if (!current_user_can('manage_options')) { + return; + } + + // Variable to store expired licenses + $expired_licenses = array(); + + // Security check with nonce + if (isset($_POST['slm_manual_check']) && check_admin_referer('slm_manual_check_action', 'slm_manual_check_nonce')) { + // Run the license check and get any expired licenses + $expired_licenses = slm_run_license_check(); + + if (!empty($expired_licenses)) { + echo '

' . esc_html__('License check completed. The following licenses have expired:', 'slm-plus') . '

'; + } else { + echo '

' . esc_html__('License check completed. No expired licenses found.', 'slm-plus') . '

'; + } + } + + // Display the button in the admin area + echo '
'; + echo '

' . esc_html__('Run License Check Manually', 'slm-plus') . '

'; + echo '
'; + wp_nonce_field('slm_manual_check_action', 'slm_manual_check_nonce'); + echo ''; + echo '
'; + + // Output expired licenses if available + if (!empty($expired_licenses)) { + echo '

' . esc_html__('Expired Licenses:', 'slm-plus') . '

'; + echo '
    '; + foreach ($expired_licenses as $license) { + echo '
  • ' . esc_html($license) . '
  • '; + } + echo '
'; + } + + echo '
'; +} diff --git a/admin/includes/partials/stats.php b/admin/includes/partials/stats.php new file mode 100644 index 0000000..9c02efc --- /dev/null +++ b/admin/includes/partials/stats.php @@ -0,0 +1,103 @@ +
    +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • + +
  • +
    +
    +
    +
    +
    + +
    +
  • +
+
+
diff --git a/software-license-manager/client-side-examples/index.html b/admin/index.html old mode 100644 new mode 100755 similarity index 100% rename from software-license-manager/client-side-examples/index.html rename to admin/index.html diff --git a/admin/slm-about-menu.php b/admin/slm-about-menu.php new file mode 100644 index 0000000..81d7fa7 --- /dev/null +++ b/admin/slm-about-menu.php @@ -0,0 +1,71 @@ + +
+

+ +

+ +
+
+
+
+

+
+

+ + + + + + + + + + + + + + + + + + + + + +
+ + Michel Velis + + + + tipsandtricks + +
+ + + +
+ + + +
+
+
+
+
+
+
+ isset($_POST['license_key']) ? sanitize_text_field(wp_unslash($_POST['license_key'])) : '', + 'max_allowed_domains' => isset($_POST['max_allowed_domains']) ? intval(wp_unslash($_POST['max_allowed_domains'])) : 0, + 'max_allowed_devices' => isset($_POST['max_allowed_devices']) ? intval(wp_unslash($_POST['max_allowed_devices'])) : 0, + 'lic_status' => isset($_POST['lic_status']) ? sanitize_text_field(wp_unslash($_POST['lic_status'])) : '', + 'first_name' => isset($_POST['first_name']) ? sanitize_text_field(wp_unslash($_POST['first_name'])) : '', + 'last_name' => isset($_POST['last_name']) ? sanitize_text_field(wp_unslash($_POST['last_name'])) : '', + 'email' => isset($_POST['email']) && is_email(wp_unslash($_POST['email'])) ? sanitize_email(wp_unslash($_POST['email'])) : '', // Ensure unslash before sanitization + 'company_name' => isset($_POST['company_name']) ? sanitize_text_field(wp_unslash($_POST['company_name'])) : '', + 'txn_id' => isset($_POST['txn_id']) ? sanitize_text_field(wp_unslash($_POST['txn_id'])) : '', + 'manual_reset_count' => isset($_POST['manual_reset_count']) ? intval(wp_unslash($_POST['manual_reset_count'])) : 0, + 'purchase_id_' => isset($_POST['purchase_id_']) ? sanitize_text_field(wp_unslash($_POST['purchase_id_'])) : '', + 'date_created' => isset($_POST['date_created']) ? SLM_API_Utility::slm_validate_date(sanitize_text_field(wp_unslash($_POST['date_created']))) : date_i18n('Y-m-d'), // Default to today's date if not set + 'date_renewed' => isset($_POST['date_renewed']) ? SLM_API_Utility::slm_validate_date(sanitize_text_field(wp_unslash($_POST['date_renewed']))) : '', + 'date_activated' => isset($_POST['date_activated']) ? SLM_API_Utility::slm_validate_date(sanitize_text_field(wp_unslash($_POST['date_activated']))) : '', + 'product_ref' => isset($_POST['product_ref']) ? sanitize_text_field(wp_unslash($_POST['product_ref'])) : '', + 'until' => isset($_POST['until']) ? sanitize_text_field(wp_unslash($_POST['until'])) : '', + 'current_ver' => isset($_POST['current_ver']) ? sanitize_text_field(wp_unslash($_POST['current_ver'])) : '', + 'subscr_id' => isset($_POST['subscr_id']) ? sanitize_text_field(wp_unslash($_POST['subscr_id'])) : '', + 'lic_type' => isset($_POST['lic_type']) ? sanitize_text_field(wp_unslash($_POST['lic_type'])) : '', + 'date_expiry' => isset($_POST['lic_type']) && $_POST['lic_type'] === 'lifetime' ? gmdate('Y-m-d', strtotime('+200 years')) : (isset($_POST['date_expiry']) ? SLM_API_Utility::slm_validate_date(sanitize_text_field(wp_unslash($_POST['date_expiry']))) : ''), + 'item_reference' => isset($_POST['item_reference']) ? sanitize_text_field(wp_unslash($_POST['item_reference'])) : '', + 'slm_billing_length' => isset($_POST['slm_billing_length']) ? sanitize_text_field(wp_unslash($_POST['slm_billing_length'])) : '', + 'slm_billing_interval' => isset($_POST['slm_billing_interval']) ? sanitize_text_field(wp_unslash($_POST['slm_billing_interval'])) : '', + 'reminder_sent' => isset($_POST['reminder_sent']) ? intval(wp_unslash($_POST['reminder_sent'])) : 0, + 'reminder_sent_date' => isset($_POST['reminder_sent_date']) ? SLM_API_Utility::slm_validate_date(sanitize_text_field(wp_unslash($_POST['reminder_sent_date']))) : '', + ]; + + // Check for required fields + if (empty($data['email']) || empty($data['date_created']) || ($data['lic_type'] !== 'lifetime' && empty($data['date_expiry'])) || empty($data['lic_type'])) { + echo '

' . esc_html__('Required fields are missing.', 'slm-plus') . '

'; + } else { + // Insert or update the data in the database + if ($id) { + $wpdb->update(SLM_TBL_LICENSE_KEYS, $data, ['id' => $id]); + echo '

' . esc_html__('License updated successfully.', 'slm-plus') . '

'; + } else { + $wpdb->insert(SLM_TBL_LICENSE_KEYS, $data); + echo '

' . esc_html__('License created successfully.', 'slm-plus') . '

'; + echo '' . esc_html__('View License', 'slm-plus') . '

'; + } + } + } else { + // If editing, load existing data + if ($id) { + // Sanitize the $id to make sure it's an integer + $id = intval($id); + + // Define the table name as a constant + $table_name = SLM_TBL_LICENSE_KEYS; + + // Use $wpdb->prepare() for the actual query with a placeholder for the ID + $query = $wpdb->prepare( + "SELECT * FROM {$table_name} WHERE id = %d", // Use %d for the integer placeholder + $id + ); + + // Get the result using the prepared query + $license = $wpdb->get_row($query); + + if ($license) { + $data = (array) $license; + } else { + // If the license is not found, reset to create a new record + $data = [ + 'license_key' => '', + 'max_allowed_domains' => SLM_DEFAULT_MAX_DOMAINS, + 'max_allowed_devices' => SLM_DEFAULT_MAX_DEVICES, + 'lic_status' => 'pending', + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'company_name' => '', + 'txn_id' => '', + 'purchase_id_' => '', + 'date_created' => date_i18n($slm_wp_date_format, strtotime('now')), + 'date_renewed' => '', + 'date_activated' => '', + 'product_ref' => '', + 'until' => '', + 'current_ver' => '', + 'subscr_id' => '', + 'lic_type' => 'subscription', + 'date_expiry' => $date_expiry, // Set a default expiry + 'item_reference' => '', + 'slm_billing_length' => $slm_billing_length, + 'slm_billing_interval' => $slm_billing_interval, + 'reminder_sent' => '0', + 'manual_reset_count' => '', + 'reminder_sent_date' => '0000-00-00' + ]; + + // Add error message that license key wasn't found + echo '

' . esc_html__('License key not found. Please create a new license.', 'slm-plus') . '

'; + } + } else { + // Prepare empty data for a new record + $data = [ + 'license_key' => '', + 'max_allowed_domains' => SLM_DEFAULT_MAX_DOMAINS, + 'max_allowed_devices' => SLM_DEFAULT_MAX_DEVICES, + 'lic_status' => 'pending', + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'company_name' => '', + 'txn_id' => '', + 'purchase_id_' => '', + 'date_created' => date_i18n($slm_wp_date_format, strtotime('now')), + 'date_renewed' => '', + 'date_activated' => '', + 'product_ref' => '', + 'until' => '', + 'current_ver' => '', + 'subscr_id' => '', + 'lic_type' => 'subscription', + 'date_expiry' => $date_expiry, + 'item_reference' => '', + 'slm_billing_length' => $slm_billing_length, + 'slm_billing_interval' => $slm_billing_interval, + 'reminder_sent' => '0', + 'manual_reset_count' => '', + 'reminder_sent_date' => '0000-00-00' + ]; + + // Generate a license key for new records + $data['license_key'] = slm_get_license(KEY_API_PREFIX); + } + } + +?> +
+

+ +
+ + + + + +
+ + + + +prepare( + "SELECT * FROM $table_name WHERE license_key = %s ORDER BY $orderby $order LIMIT 10 OFFSET %d", + esc_sql($data['license_key']), + (isset($_GET['paged']) ? intval($_GET['paged'] - 1) * 10 : 0) // Calculate offset for pagination +); +$log_entries = $wpdb->get_results($query, ARRAY_A); +?> + +
+

+ + + + + __('ID', 'slm-plus'), + 'slm_action' => __('Action', 'slm-plus'), + 'time' => __('Date & Time', 'slm-plus'), + 'source' => __('Source', 'slm-plus'), + ]; + + // Render table headers with sorting links + foreach ($columns as $column_key => $column_name): + $next_order = ($orderby === $column_key && $order === 'ASC') ? 'DESC' : 'ASC'; + ?> + + + + + + + + + + + + + + +
+ + + + + + +
+ +

+ +
+ + + + + + + get_row($wpdb->prepare( + "SELECT max_allowed_domains, max_allowed_devices FROM " . SLM_TBL_LICENSE_KEYS . " WHERE license_key = %s", + $license_key + )); + + // Ensure the max values are retrieved and set them to default values if not found + $max_domains = isset($license_info->max_allowed_domains) ? intval($license_info->max_allowed_domains) : 0; + $max_devices = isset($license_info->max_allowed_devices) ? intval($license_info->max_allowed_devices) : 0; + + // Fetch the current number of registered domains for this license key + $registered_domains = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM " . SLM_TBL_LIC_DOMAIN . " WHERE lic_key = %s", + $license_key + )); + + // Fetch the current number of registered devices for this license key + $registered_devices = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM " . SLM_TBL_LIC_DEVICES . " WHERE lic_key = %s", + $license_key + )); + + // Ensure the count values are integers + $registered_domains = isset($registered_domains) ? intval($registered_domains) : 0; + $registered_devices = isset($registered_devices) ? intval($registered_devices) : 0; + + // Calculate how many domains and devices are left + $domains_left = $max_domains - $registered_domains; + $devices_left = $max_devices - $registered_devices; + + // Ensure the result is not negative (to handle edge cases) + $domains_left = max(0, $domains_left); + $devices_left = max(0, $devices_left); + + // Fetch all registered domains for this license key + $registered_domains_data = $wpdb->get_results($wpdb->prepare( + "SELECT id, registered_domain FROM " . SLM_TBL_LIC_DOMAIN . " WHERE lic_key = %s", + $license_key + )); + + // Fetch all registered devices for this license key + $registered_devices_data = $wpdb->get_results($wpdb->prepare( + "SELECT id, registered_devices FROM " . SLM_TBL_LIC_DEVICES . " WHERE lic_key = %s", + $license_key + )); + + + $slm_ajax_uri = ''; + $slm_deactivate_nonce = wp_create_nonce('slmplus_delete_activation_nonce'); + + // Render the table + ?> + +
+

+

+ :
+ : +

+ + + + + + + + + + + + + + + registered_domain); + ?> + + + + + + + + + + + + + registered_devices); + ?> + + + + + + + + + + + + + + + + + +
id); ?>registered_domain); ?> + +
id); ?>registered_devices); ?> + + +
+
+ + + + 'error', + 'message' => __('Nonce verification failed.', 'slm-plus'), + 'error_code' => 401 + ]); + } + + global $wpdb; + + // Check if the activation ID and type are provided and valid + if (!isset($_POST['activation_id']) || !is_numeric(wp_unslash($_POST['activation_id'])) || !isset($_POST['activation_type']) || empty($_POST['activation_type'])) { + wp_send_json_error([ + 'result' => 'error', + 'message' => __('Invalid activation data.', 'slm-plus'), + 'error_code' => 400 + ]); + } + $activation_id = isset($_POST['activation_id']) ? intval(wp_unslash($_POST['activation_id'])) : 0; + $activation_type = isset($_POST['activation_type']) ? sanitize_text_field(wp_unslash($_POST['activation_type'])) : ''; + + // Delete the activation from the correct table + if ($activation_type === 'domain') { + $result = $wpdb->delete(SLM_TBL_LIC_DOMAIN, ['id' => $activation_id]); + } elseif ($activation_type === 'device') { + $result = $wpdb->delete(SLM_TBL_LIC_DEVICES, ['id' => $activation_id]); + } else { + wp_send_json_error([ + 'result' => 'error', + 'message' => __('Invalid activation type.', 'slm-plus'), + 'error_code' => 400 + ]); + } + + // Handle result + if ($result !== false) { + wp_send_json_success([ + 'result' => 'success', + 'message' => __('The license key has been deactivated for this domain.', 'slm-plus'), + 'error_code' => 360 + ]); + } else { + wp_send_json_error([ + 'result' => 'error', + 'message' => __('Error deleting activation.', 'slm-plus'), + 'error_code' => 500 + ]); + } + }); + ?> + + +
+ + + + + + +

+
+
+ + + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + + + + +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + readonly /> +
+ get_results($query, ARRAY_A); + + // Create the '; + foreach ($statuses as $status) { + // Set the selected attribute if the current status matches + $selected = selected($data['lic_status'], $status['status_key'], false); + echo ''; + } + echo ''; + ?> +
readonly class="regular-text datepicker" required /> +

+ +

+
+ +
+

+ Choose this date to set when the license should renew or expire.
Format: %s (input: YYYY-MM-DD).', 'slm-plus'), esc_html($slm_wp_date_format)); + ?> +

+
+
+
+ + +

+ +

+
+ +
+ + +

+ +

+
+
+
+ +

+ +

+
+ +

+ +

+
+
+
+ +

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +

+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ +

+ +

+
+
+
+ + + + + + +
+ +
+
+'; + echo '

' . esc_html__('SLM Plus - Tools', 'slm-plus') . '

'; + echo '
'; + + if (isset($_POST['send_deactivation_request'])) { + // Verify the nonce + if (isset($_POST['slm_deactivation_nonce_field']) && wp_verify_nonce($_POST['slm_deactivation_nonce_field'], 'slm_deactivation_nonce_action')) { + // Nonce is valid, proceed with the deactivation request + + $postURL = esc_url_raw($_POST['slm_deactivation_req_url']); + $secretKeyForVerification = slm_get_option('lic_verification_secret'); + $data = array('secret_key' => $secretKeyForVerification); + + // Make the POST request using wp_remote_post + $response = wp_remote_post($postURL, array( + 'method' => 'POST', + 'body' => $data, + 'timeout' => 15, // Optional timeout value + 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded') + )); + + // Check for errors in the response + if (is_wp_error($response)) { + $error_message = $response->get_error_message(); + $msg = esc_html__('Request failed: ', 'slm-plus') . esc_html($error_message); + } else { + $body = wp_remote_retrieve_body($response); + + // Check for success message in response + if ($body == "Success") { + $msg = esc_html__('Success message returned from the remote host.', 'slm-plus'); + } else { + $msg = esc_html__('Unexpected response: ', 'slm-plus') . esc_html($body); + } + } + + // Display message + echo '

'; + echo esc_html__('Request sent to the specified URL!', 'slm-plus'); + echo '
' . esc_html($msg); + echo '

'; + } else { + // Nonce is invalid or missing + echo '

' . esc_html__('Security check failed. Invalid nonce.', 'slm-plus') . '

'; + } + } + + + if (isset($_POST['slm_clear_log'])) { + // Verify the nonce + if (isset($_POST['slm_clear_log_nonce_field']) && wp_verify_nonce($_POST['slm_clear_log_nonce_field'], 'slm_clear_log_nonce_action')) { + // Nonce is valid, proceed with clearing the log + + global $wpdb, $slm_debug_logger; + + // Define the table name using the constant (already assumed to be done securely) + $table = SLM_TBL_LIC_LOG; + + // Sanitize the table name if it's dynamically passed (for security) + $table = sanitize_key($table); // sanitize_key ensures a safe table name, although here it's already defined + + // Sanitize and validate other variables if used dynamically + + // Direct query execution for truncating the table + if ($wpdb->get_var("SHOW TABLES LIKE '$table'")) { // Check if table exists + $query = "TRUNCATE TABLE `$table`"; // Backticks are used to prevent issues with reserved SQL keywords + $wpdb->query($query); // Direct query execution + } else { + // Handle the case where the table doesn't exist + error_log('Table not found: ' . $table); + } + + // Reset log files + $slm_debug_logger->reset_log_file("log.txt"); + $slm_debug_logger->reset_log_file("log-cron-job.txt"); + + echo '

' . esc_html__('Log was cleared successfully!', 'slm-plus') . '

'; + } else { + // Nonce is invalid or missing + echo '

' . esc_html__('Security check failed. Invalid nonce.', 'slm-plus') . '

'; + } + } + +?> +
+
+

+
+
+ + +
+ +
+
+
+
+ + +
+

+
+

+ +

+
+ + + + + + + +
+ +

' . esc_html__('New Creation Secret Key:', 'slm-plus') . ' ' . esc_html($_GET['new_creation_secret']) . '

'; + } + if (isset($_GET['new_verification_secret'])) { + echo '

' . esc_html__('New Verification Secret Key:', 'slm-plus') . ' ' . esc_html($_GET['new_verification_secret']) . '

'; + } + ?> +
+
+ + $new_creation_secret])); + wp_redirect(add_query_arg(['slm_notice' => 'creation_key_reset', 'new_creation_secret' => $new_creation_secret], $_SERVER['REQUEST_URI'])); + exit; + } + + if (isset($_POST['reset_verification_secret']) && check_admin_referer('slm_reset_secret_keys_nonce_action', 'slm_reset_secret_keys_nonce_field')) { + $new_verification_secret = SLM_Utility::create_secret_keys(); // Generate new verification secret + update_option('slm_plugin_options', array_merge(get_option('slm_plugin_options', []), ['lic_verification_secret' => $new_verification_secret])); + wp_redirect(add_query_arg(['slm_notice' => 'verification_key_reset', 'new_verification_secret' => $new_verification_secret], $_SERVER['REQUEST_URI'])); + exit; + } + } + ?> + + +
+

+
+

+
+
+ + +
+
+
+
+ + +
+

+
+

+
+ +
+ +
+
+ + ' . esc_html__('Last backup created on: ', 'slm-plus') . esc_html($backup_date) . ' - ' . esc_html__('Download Backup', 'slm-plus') . '

'; + } + ?> +
+
+ +
+

+
+

+ + + +

+ +
+ +
+ + + + + + + + + + + + + + + + +
+ +

+
+ +

+
+ + /> + + +

+ +

+ +
+
+
+ +
+

+
    +
    +
    +
    + + + + +
    '; +} + +/** + * Generates or retrieves a unique hash for the backup directory. + */ +function slm_get_unique_hash() +{ + $hash = slm_get_option('slm_backup_dir_hash'); + if (!$hash) { + $hash = wp_generate_password(8, false, false); // Generate random 8-character hash + slm_update_option('slm_backup_dir_hash', $hash); + } + return $hash; +} + + +/** + * Retrieves an option from the slm_plugin_options. + */ +function slm_get_option($key) +{ + $options = get_option('slm_plugin_options', []); + return $options[$key] ?? null; +} + +/** + * Updates or adds an option to the slm_plugin_options. + */ +function slm_update_option($key, $value) +{ + $options = get_option('slm_plugin_options', []); + $options[$key] = $value; + update_option('slm_plugin_options', $options); +} diff --git a/admin/slm-admin-init.php b/admin/slm-admin-init.php new file mode 100755 index 0000000..f253590 --- /dev/null +++ b/admin/slm-admin-init.php @@ -0,0 +1,15 @@ +add_menu(array( + 'id' => 'slm-menu', + 'title' => '' . __('SLM Plus', 'slm-plus'), // Added text domain + 'href' => admin_url('admin.php?page=slm_overview'), + 'meta' => array( + 'title' => __('slm-plus', 'slm-plus'), // Added text domain + ), + )); + $admin_bar->add_menu(array( + 'id' => 'slm-manage-licenses-overview', + 'parent' => 'slm-menu', + 'title' => __('Overview', 'slm-plus'), // Added text domain + 'href' => admin_url('admin.php?page=slm_overview'), + 'meta' => array( + 'title' => __('Overview', 'slm-plus'), // Added text domain + 'class' => 'slm_overview_menu' + ), + )); + $admin_bar->add_menu(array( + 'id' => 'slm-manage-licenses-addnew', + 'parent' => 'slm-menu', + 'title' => __('Add new license', 'slm-plus'), // Added text domain + 'href' => admin_url('admin.php?page=slm_manage_license'), + 'meta' => array( + 'title' => __('Add new license', 'slm-plus'), // Added text domain + 'class' => 'slm_addlicense_menu' + ), + )); + $admin_bar->add_menu(array( + 'id' => 'slm-manage-licenses-settings', + 'parent' => 'slm-menu', + 'title' => __('Settings', 'slm-plus'), // Added text domain + 'href' => admin_url( 'admin.php?page=slm_settings'), + 'meta' => array( + 'title' => __('Settings', 'slm-plus'), // Added text domain + 'class' => 'slm_settings_menu' + ), + )); +} + + +/** + * Create the function to output the contents of our Dashboard Widget. + */ +function slm_dashboard_widget_function() +{ ?> + + + +
    +
    + + + + + + + + + +
    + +  –  +
    +
    + + + +
    +

    + + +
    +
    +
    +
    +

    +
    + +

    + + +

    + + +

    + +
    +
    + + +
    +

    +
    +

    + + + +

    +
    +
    + +
    +

    +
    + + + + + + + + + + '; + foreach ($code as $value) { + echo ''; + } + echo ''; + } + ?> + +
    ' . esc_html($value) . '
    +
    +
    +
    +
    +
    +
    + + trim($_POST['lic_creation_secret']), + 'lic_prefix' => trim($_POST['lic_prefix']), + 'default_max_domains' => $default_max_domains, + 'default_max_devices' => $default_max_devices, + 'lic_verification_secret' => trim($_POST['lic_verification_secret']), + 'enable_auto_key_expiration' => isset($_POST['enable_auto_key_expiration']), + 'enable_debug' => isset($_POST['enable_debug']), + 'slm_woo' => isset($_POST['slm_woo']), + 'slm_wc_lic_generator' => isset($_POST['slm_wc_lic_generator']), + 'slm_woo_downloads' => isset($_POST['slm_woo_downloads']), + 'slm_woo_affect_downloads' => isset($_POST['slm_woo_affect_downloads']), + 'slm_stats' => isset($_POST['slm_stats']), + 'slm_adminbar' => isset($_POST['slm_adminbar']), + // 'slm_conflictmode' => isset($_POST['slm_conflictmode']), + // 'slm_front_conflictmode' => isset($_POST['slm_front_conflictmode']), + 'slm_wpestores' => isset($_POST['slm_wpestores']), + 'slm_dl_manager' => isset($_POST['slm_dl_manager']), + 'slm_multiple_items' => isset($_POST['slm_multiple_items']), + 'allow_user_activation_removal' => isset($_POST['allow_user_activation_removal']), + 'expiration_reminder_text' => sanitize_text_field($_POST['expiration_reminder_text']), + 'license_until_version' => $license_until_version, + 'license_current_version' => $license_current_version, + 'slm_billing_length' => $slm_billing_length, + 'slm_billing_interval' => $slm_billing_interval, + ); + + // Update the options in the database + update_option('slm_plugin_options', $options); + echo '

    ' . esc_html__('Options updated!', 'slm-plus') . '

    '; + } + + $secret_key = !empty($options['lic_creation_secret']) ? $options['lic_creation_secret'] : SLM_Utility::create_secret_keys(); + $secret_verification_key = !empty($options['lic_verification_secret']) ? $options['lic_verification_secret'] : SLM_Utility::create_secret_keys(); + $tab = isset($_REQUEST['tab']) ? $_REQUEST['tab'] : 'general_settings'; + + ?> +
    +
    +
    + + +

    + +
    + + + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    +
    +

    +
    +

    +
    +

    +
    +

    +
    +

    +
    +

    +
    +

    + +

    +

    +
    value="1" /> + +

    +
    + value="1" /> + +
    + value="1" /> + +
    + value="1" /> + +

    +
    + value="1" /> + +

    +
    +
    + +
    +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + value="1" /> + +
    + value="1" /> + +

    + + + +

    +
    + value="1" /> + +
    + value="1" /> + +
    + + + +

    + +
    +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    +

    value="1" /> +

    +
    +

    +
    +

    +
    +

    +
    +
    +
    + +
    +
    + + + + + +
    + +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    + 'page', + 'name' => 'license-cart', // Check for the slug + 'post_status' => 'publish', + 'posts_per_page' => 1, + )); + + if ($query->have_posts()) { + add_settings_error( + 'slm_license_cart_error', + 'slm_license_cart_exists', + __('The License Cart page already exists.', 'slm-plus'), + 'error' + ); + return; + } + + // Create the License Cart page + $page_id = wp_insert_post(array( + 'post_title' => 'License Cart', + 'post_content' => '', + 'post_status' => 'publish', + 'post_name' => 'license-cart', // Set the slug + 'post_type' => 'page', + 'meta_input' => array('_wp_page_template' => 'page-license-cart.php'), // Assign the custom template + )); + + // Check if the page was created successfully + if ($page_id && !is_wp_error($page_id)) { + // Hide the page from menus and navigation + update_post_meta($page_id, '_menu_item_visibility', 'hidden'); + add_settings_error( + 'slm_license_cart_success', + 'slm_license_cart_created', + __('The License Cart page was successfully created.', 'slm-plus'), + 'updated' + ); + } else { + add_settings_error( + 'slm_license_cart_error', + 'slm_license_cart_failed', + __('Failed to create the License Cart page. Please try again.', 'slm-plus'), + 'error' + ); + } +} + + +add_action('admin_notices', 'slm_license_cart_admin_notices'); + +function slm_license_cart_admin_notices() { + settings_errors('slm_license_cart_error'); + settings_errors('slm_license_cart_success'); +} diff --git a/admin/slm-list-licenses-class.php b/admin/slm-list-licenses-class.php new file mode 100755 index 0000000..88c2477 --- /dev/null +++ b/admin/slm-list-licenses-class.php @@ -0,0 +1,551 @@ + 'item', //singular name of the listed records + 'plural' => 'items', //plural name of the listed records + 'ajax' => false //does this table support ajax? + )); + } + + public function no_items() + { + esc_html_e('No licenses avaliable.', 'slm-plus'); + } + + function get_views() + { + + $base = admin_url('admin.php?page=slm_overview'); + $current = isset($_GET['view']) ? $_GET['view'] : ''; + + $link_html = '%s(%s)'; + + $views = array( + 'all' => sprintf( + $link_html, + esc_url(remove_query_arg('view', $base)), + $current === 'all' || $current == '' ? ' class="current"' : '', + esc_html__('All', 'slm-plus'), + SLM_Utility::get_total_licenses() + ), + 'active' => sprintf( + $link_html, + esc_url(add_query_arg('view', 'active', $base . '&s=active')), + $current === 'active' ? ' class="current"' : '', + esc_html__('active', 'slm-plus'), + SLM_Utility::count_licenses('active') + ), + 'pending' => sprintf( + $link_html, + esc_url(add_query_arg('view', 'pending', $base . '&s=pending')), + $current === 'pending' ? ' class="current"' : '', + esc_html__('pending', 'slm-plus'), + SLM_Utility::count_licenses('pending') + ), + 'expired' => sprintf( + $link_html, + esc_url(add_query_arg('view', 'expired', $base . '&s=expired')), + $current === 'expired' ? ' class="current"' : '', + esc_html__('expired', 'slm-plus'), + SLM_Utility::count_licenses('expired') + ), + 'blocked' => sprintf( + $link_html, + esc_url(add_query_arg('view', 'blocked', $base . '&s=blocked')), + $current === 'blocked' ? ' class="current"' : '', + esc_html__('blocked', 'slm-plus'), + SLM_Utility::count_licenses('blocked') + ) + ); + + return $views; + } + + + function get_columns() + { + $columns = array( + 'cb' => '', //Render a checkbox + 'id' => __('ID', 'slm-plus'), + 'lic_status' => __('Status', 'slm-plus'), + 'license_key' => __('Key', 'slm-plus'), + 'item_reference' => __('Item reference', 'slm-plus'), + 'lic_type' => __('License type', 'slm-plus'), + 'email' => __('Email', 'slm-plus'), + 'max_allowed_domains' => __('Domains', 'slm-plus'), + 'max_allowed_devices' => __('Devices', 'slm-plus'), + 'purchase_id_' => __('Order #', 'slm-plus'), + 'date_created' => __('Created on', 'slm-plus'), + 'date_renewed' => __('Renewed on', 'slm-plus'), + 'date_activated' => __('Activated on', 'slm-plus'), + 'date_expiry' => __('Expiration', 'slm-plus'), + 'until' => __('Until Ver.', 'slm-plus'), + 'current_ver' => __('Current Ver.', 'slm-plus') + ); + return $columns; + } + + function column_default($item, $column_name) + { + switch ($column_name) { + + case 'lic_status': + return ' ' . $item[$column_name] . ''; + break; + + case 'email': + return '' . $item[$column_name] . ' '; + break; + + case 'date_expiry': + $expiration = $item[$column_name]; + $date_today = time(); + + if ($expiration == '0000-00-00') { + return '' . __(' Lifetime ', 'slm-plus') . '' . ' '; + } + + + if ($expiration != '0000-00-00') { + if (strtotime($expiration) < time()) { + return ' ' . $expiration . ' ' . ' ' . SLM_Utility::get_days_remaining($expiration) . ' day(s) due'; + } else { + return '' . $item[$column_name] . '' . ' ' . SLM_Utility::get_days_remaining($expiration) . ' day(s) left'; + } + } else { + //return $item[$column_name]; + return 'not set'; + } + break; + + default: + return $item[$column_name]; + } + } + + function column_id($item) + { + $row_id = $item['id']; + $actions = array( + 'edit' => sprintf('Edit', $row_id), + 'delete' => sprintf('Delete', $row_id), + ); + return sprintf( + ' %1$s %2$s', + /*$1%s*/ + $item['id'], + /*$2%s*/ + $this->row_actions($actions) + ); + } + + function column_active($item) + { + if ($item['active'] == 1) { + return 'active'; + } else { + return 'inactive'; + } + } + + function column_cb($item) + { + + return sprintf( + '', + /*$1%s*/ + $this->_args['singular'], //Let's simply repurpose the table's singular label + /*$2%s*/ + $item['id'] //The value of the checkbox should be the record's id + ); + } + + function get_sortable_columns() + { + $sortable_columns = array( + 'id' => array('id', true), + 'email' => array('email', true), + 'lic_type' => array('lic_type', true), + 'until' => array('until', true), + 'current_ver' => array('current_ver', true), + 'lic_status' => array('lic_status', true), + 'item_reference' => array('item_reference', true), + ); + + return $sortable_columns; + } + + function get_bulk_actions() + { + $actions = array( + 'delete' => 'Delete', + 'blocked' => 'Block', + 'expired' => 'Expire', + 'active' => 'Activate', + // 'reminder' => 'Send Reminder', + 'export' => 'Export', + ); + return $actions; + } + + function process_bulk_action() + { + if ('delete' === $this->current_action()) { + //Process delete bulk actions + if (!isset($_REQUEST['item'])) { + $error_msg = '

    ' . __('Error - Please select some records using the checkboxes', 'slm-plus') . '

    '; + echo '
    ' . esc_html($error_msg) . '
    '; + return; + } else { + $nvp_key = $this->_args['singular']; + $records_to_delete = $_GET[$nvp_key]; + + foreach ($records_to_delete as $row) { + SLM_Utility::delete_license_key_by_row_id($row); + } + + echo '

    Selected records deleted successfully!

    '; + } + } + + if ('blocked' === $this->current_action()) { + //Process blocked bulk actions + if (!isset($_REQUEST['item'])) { + $error_msg = '

    ' . __('Error - Please select some records using the checkboxes', 'slm-plus') . '

    '; + echo '
    ' . esc_html($error_msg) . '
    '; + return; + } else { + $nvp_key = $this->_args['singular']; + $licenses_to_block = $_GET[$nvp_key]; + + foreach ($licenses_to_block as $row) { + SLM_Utility::block_license_key_by_row_id($row); + } + + echo '

    ' . esc_html($row) . ' ' . esc_html__('Selected records blocked successfully!', 'slm-plus') . '

    '; + } + } + + if ('expired' === $this->current_action()) { + //Process expired bulk actions + if (!isset($_REQUEST['item'])) { + $error_msg = '

    ' . __('Error - Please select some records using the checkboxes', 'slm-plus') . '

    '; + echo '
    ' . esc_html($error_msg) . '
    '; + return; + } else { + $nvp_key = $this->_args['singular']; + $licenses_to_expire = $_GET[$nvp_key]; + + foreach ($licenses_to_expire as $row) { + SLM_Utility::expire_license_key_by_row_id($row); + } + + echo '

    ' . esc_html($row) . ' ' . esc_html__('Selected records expired successfully!', 'slm-plus') . '

    '; + } + } + + if ('active' === $this->current_action()) { + //Process activate bulk actions + if (!isset($_REQUEST['item'])) { + $error_msg = '

    ' . __('Error - Please select some records using the checkboxes', 'slm-plus') . '

    '; + echo '
    ' . esc_html($error_msg) . '
    '; + return; + } else { + $nvp_key = $this->_args['singular']; + $liceses_to_activate = $_GET[$nvp_key]; + + foreach ($liceses_to_activate as $row) { + SLM_Utility::active_license_key_by_row_id($row); + } + + echo '

    ' . esc_html($row) . ' ' . esc_html__('Selected records activated successfully!', 'slm-plus') . '

    '; + } + } + + // Export license data + if ('export' === $this->current_action()) { + if (!isset($_REQUEST['item'])) { + $error_msg = '

    ' . __('Error - Please select some records using the checkboxes', 'slm-plus') . '

    '; + echo '
    ' . esc_html($error_msg) . '
    '; + return; + } else { + $nvp_key = $this->_args['singular']; + $licenses_to_export = $_GET[$nvp_key]; + + // Call the export function + $file_urls = self::export_license_data($licenses_to_export); + + // Display success message with download links for each license + echo '
    '; + echo '

    Export successful! Download the CSV files:

    '; + foreach ($file_urls as $file_url) { + echo '

    Download CSV File

    '; + } + echo '
    '; + } + } + } + + public static function export_license_data($license_ids) + { + global $wpdb; + + // Fetch the custom directory path from options (saved with hash) + $slm_options = get_option('slm_plugin_options'); + $custom_dir_hash = isset($slm_options['slm_backup_dir_hash']) ? $slm_options['slm_backup_dir_hash'] : ''; + + // Prepare file URLs array + $file_urls = []; + + // Get the WordPress upload directory + $upload_dir = wp_upload_dir(); + $custom_dir = $upload_dir['basedir'] . '/' . $custom_dir_hash; + + // Initialize WP_Filesystem for safe file handling + if (empty($GLOBALS['wp_filesystem'])) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + WP_Filesystem(); + } + + // Ensure the directory exists using WP_Filesystem methods + if (!is_dir($custom_dir)) { + $created = $GLOBALS['wp_filesystem']->mkdir($custom_dir, 0755); // Create the directory if it doesn't exist + if (!$created) { + return new WP_Error('directory_creation_failed', 'Unable to create the directory.'); + } + } + + // Fetch license data for each selected ID + foreach ($license_ids as $license_id) { + $data = $wpdb->get_row($wpdb->prepare( + "SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE id = %d", + $license_id + ), ARRAY_A); + + if ($data) { + $license_key = $data['license_key']; + + // Prepare file name as "license_key.csv" + $file_name = sanitize_file_name($license_key) . '.csv'; + $file_path = $custom_dir . '/' . $file_name; + + // Open file handle using WP_Filesystem methods + $file_handle = $GLOBALS['wp_filesystem']->open($file_path, 'w'); + + if (!$file_handle) { + return new WP_Error('file_creation_failed', 'Unable to open the file for writing.'); + } + + // Write CSV headers and license data to the file + fputcsv($file_handle, array_keys($data)); + fputcsv($file_handle, $data); + + // Close the file handle + $GLOBALS['wp_filesystem']->close($file_handle); + + // Store the file URL for download + $file_urls[] = $upload_dir['baseurl'] . '/' . $custom_dir_hash . '/' . $file_name; + } + } + + // Return the array of file URLs + return $file_urls; + } + + + + /* + * This function will delete the selected license key entries from the DB. + */ + function delete_license_key($key_row_id) + { + SLM_Utility::delete_license_key_by_row_id($key_row_id); + $success_msg = '

    '; + $success_msg .= 'The selected entry was deleted successfully!'; + $success_msg .= '

    '; + echo esc_html($success_msg); + } + + function block_license_key($key_row_id) + { + SLM_Utility::block_license_key_by_row_id($key_row_id); + $success_msg = '

    '; + $success_msg .= 'The selected entry was blocked successfully!'; + $success_msg .= '

    '; + echo esc_html($success_msg); + } + + private function sort_data($a, $b) + { + // Set defaults + $orderby = 'id'; + $order = 'desc'; + + // Sanitize and unslash input for 'orderby' and 'order' + if (!empty($_GET['orderby'])) { + $orderby = wp_unslash($_GET['orderby']); // wp_unslash before sanitization + $orderby = sanitize_key($orderby); // sanitize for key-based data + } + + if (!empty($_GET['order'])) { + $order = wp_unslash($_GET['order']); // wp_unslash before sanitization + $order = in_array(strtolower($order), ['asc', 'desc']) ? strtolower($order) : 'desc'; // Ensure 'asc' or 'desc' only + } + + // Sorting logic + if ($orderby == 'id') { + if ($a[$orderby] == $b[$orderby]) { + $result = 0; + } else { + $result = ($a[$orderby] < $b[$orderby]) ? -1 : 1; + } + } else { + $result = strcmp($a[$orderby], $b[$orderby]); + } + + // Return based on the order (asc or desc) + if ($order === 'asc') { + return $result; + } + + return -$result; + } + + function prepare_items() + { + global $wpdb; + $user = get_current_user_id(); + $screen = get_current_screen(); + $option = $screen->get_option('per_page', 'option'); + $per_page = get_user_meta($user, $option, true); + + if (empty($per_page) || $per_page < 1) { + $per_page = $screen->get_option('per_page', 'default'); + } + + $columns = $this->get_columns(); + $hidden = get_hidden_columns($screen); + $sortable = $this->get_sortable_columns(); + $this->_column_headers = array($columns, $hidden, $sortable); + + $this->process_bulk_action(); + + // Ensure the license table constant is used safely + $license_table = esc_sql(SLM_TBL_LICENSE_KEYS); // Sanitize the table name + + // Search handling with esc_like for wildcard search + $search = isset($_REQUEST['s']) ? sanitize_text_field(wp_unslash($_REQUEST['s'])) : ''; // Use wp_unslash to handle slashes + $search_term = wp_strip_all_tags($search); // Using wp_strip_all_tags instead of strip_tags + $search_term_esc = addcslashes($search_term, '_%'); // Escapes underscore and percent characters + + // Prepared query with placeholders and escaped search term + $do_search = $wpdb->prepare( + "SELECT * FROM $license_table + WHERE license_key LIKE %s OR email LIKE %s OR lic_status LIKE %s OR first_name LIKE %s OR last_name LIKE %s", + '%' . $search_term_esc . '%', // Apply wildcard to the escaped search term + '%' . $search_term_esc . '%', + '%' . $search_term_esc . '%', + '%' . $search_term_esc . '%', + '%' . $search_term_esc . '%' + ); + + // Execute the query and get the results + $data = $wpdb->get_results($do_search, ARRAY_A); + + // Sort data + usort($data, array(&$this, 'sort_data')); + + // Pagination + $current_page = $this->get_pagenum(); + $total_items = count($data); + $data = array_slice($data, (($current_page - 1) * $per_page), $per_page); + + $this->items = $data; + + // Set pagination arguments + $this->set_pagination_args(array( + 'total_items' => $total_items, + 'per_page' => $per_page, + 'total_pages' => ceil($total_items / $per_page) + )); + } +} + +class SLM_Plugin +{ + // class instance + static $instance; + + // customer WP_List_Table object + public $licenses_obj; + + // class constructor + public function __construct() + { + add_filter('set-screen-option', [__CLASS__, 'set_screen'], 10, 3); + add_action('admin_menu', [$this, 'slm_add_admin_menu']); + } + + public static function set_screen($status, $option, $value) + { + return $value; + } + + public function slm_add_admin_menu() + { + $icon_svg = SLM_ASSETS_URL . 'images/slm_logo_small.svg'; + add_menu_page(__('SLM Plus', 'slm-plus'), __('SLM Plus', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, SLM_MAIN_MENU_SLUG, "slm_manage_licenses_menu", $icon_svg); + $hook = add_submenu_page(SLM_MAIN_MENU_SLUG, __('Manage Licenses', 'slm-plus'), __('Manage Licenses', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, SLM_MAIN_MENU_SLUG, "slm_manage_licenses_menu"); + add_submenu_page(SLM_MAIN_MENU_SLUG, __('Create license', 'slm-plus'), __('Create license', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, 'slm_manage_license', "slm_add_licenses_menu"); + add_submenu_page(SLM_MAIN_MENU_SLUG, __('Subscribers', 'slm-plus'), __('Subscribers', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, 'slm_subscribers', "slm_subscribers_menu"); + add_submenu_page(SLM_MAIN_MENU_SLUG, __('Tools', 'slm-plus'), __('Tools', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, 'slm_admin_tools', "slm_admin_tools_menu"); + add_submenu_page(SLM_MAIN_MENU_SLUG, __('Settings', 'slm-plus'), __('Settings', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, 'slm_settings', "slm_settings_menu"); + add_submenu_page(SLM_MAIN_MENU_SLUG, __('Help', 'slm-plus'), __('Help', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, 'slm_help', "slm_integration_help_menu"); + add_submenu_page(SLM_MAIN_MENU_SLUG, __('About', 'slm-plus'), __('About', 'slm-plus'), SLM_MANAGEMENT_PERMISSION, 'slm_about', "slm_about_menu"); + add_action("load-" . $hook, [$this, 'screen_option']); + } + + /** + * Screen options + */ + public function screen_option() + { + $option = 'per_page'; + $args = [ + 'label' => 'Pagination', + 'default' => 16, + 'option' => 'licenses_per_page' + ]; + add_screen_option($option, $args); + $this->licenses_obj = new SLM_List_Licenses(); + } + + /** Singleton instance */ + public static function get_instance() + { + if (!isset(self::$instance)) { + self::$instance = new self(); + } + + return self::$instance; + } +} + +add_action('plugins_loaded', function () { + SLM_Plugin::get_instance(); +}); diff --git a/admin/slm-manage-licenses.php b/admin/slm-manage-licenses.php new file mode 100755 index 0000000..83b8a6e --- /dev/null +++ b/admin/slm-manage-licenses.php @@ -0,0 +1,76 @@ + false, + 'message' => esc_html__('You do not have permission to manage this license.', 'slm-plus'), + ); + echo json_encode($response); + die(); + } +} + +function slm_manage_licenses_menu() +{ + //include_once('slm-list-licenses-class.php'); + $license_list = new SLM_List_Licenses(); + + if (isset($_REQUEST['action'])) { //Do list table form row action tasks + if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'delete_license') { //Delete link was clicked for a row in list table + $license_list->delete_license_key(sanitize_text_field($_REQUEST['id'])); + } + } +?> +
    +
    +
    +

    +
    + + +
    + +
    +
    +
    + +
    + +
    +
    +
    + + prepare_items(); + $license_list->search_box(__('Search', 'slm-plus'), 'search-box-id'); + $license_list->views(); + $license_list->display(); ?> +
    +
    +
    +
    +
    +
    + +
    +
    +
    + 'slm_subscriber', + 'plural' => 'slm_subscribers', + 'ajax' => false + )); + } + + function column_default($item, $column_name) + { + switch ($column_name) { + + case 'lic_status': + return ' ' . $item[$column_name] . ''; + break; + + default: + return $item[$column_name]; + } + } + + + + function column_id($item) + { + $row_id = $item['id'] . '&email=' . $item['email']; + $actions = array( + 'edit' => sprintf('View', $row_id), + ); + return sprintf( + ' %1$s %2$s', + /*$1%s*/ + $item['id'], + /*$2%s*/ + $this->row_actions($actions) + ); + } + + + function column_active($item) + { + if ($item['active'] == 1) { + return 'active'; + } else { + return 'inactive'; + } + } + + + + + function column_cb($item) + { + + return sprintf( + '', + /*$1%s*/ + $this->_args['singular'], //Let's simply repurpose the table's singular label + /*$2%s*/ + $item['id'] //The value of the checkbox should be the record's id + ); + } + + function get_columns() + { + $columns = array( + 'cb' => '', //Render a checkbox + 'id' => __('ID', 'slm-plus'), + 'first_name' => __('First Name', 'slm-plus'), + 'last_name' => __('Last Name', 'slm-plus'), + 'email' => __('Email Address', 'slm-plus') + ); + return $columns; + } + + + function get_sortable_columns() + { + $sortable_columns = array( + 'id' => array('id', true), + 'email' => array('email', true), + 'first_name' => array('first_name', true), + 'last_name' => array('last_name', true) + ); + + return $sortable_columns; + } + + private function sort_data($a, $b) + { + // Set defaults + $orderby = 'id'; + $order = 'desc'; + // If orderby is set, use this as the sort column + if (!empty($_GET['orderby'])) { + $orderby = $_GET['orderby']; + } + // If order is set use this as the order + if (!empty($_GET['order'])) { + $order = $_GET['order']; + } + if ($orderby == 'id') { + if ($a[$orderby] == $b[$orderby]) { + $result = 0; + } else { + $result = ($a[$orderby] < $b[$orderby]) ? -1 : 1; + } + } else { + $result = strcmp($a[$orderby], $b[$orderby]); + } + if ($order === 'asc') { + return $result; + } + return -$result; + } + + function prepare_items() + { + $per_page = 24; + $columns = $this->get_columns(); + $hidden = array(); + $sortable = $this->get_sortable_columns(); + + $this->_column_headers = array($columns, $hidden, $sortable); + $this->process_bulk_action(); + + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Sanitize the search term and strip all tags + $search = isset($_REQUEST['s']) ? sanitize_text_field(wp_unslash($_REQUEST['s'])) : false; + $search_term = trim(wp_strip_all_tags($search)); // Using wp_strip_all_tags for better sanitization + + // Escape the search term for SQL and add wildcards manually + $escaped_search_term = '%' . $wpdb->esc_like($search_term) . '%'; // esc_like handles escaping the term + + // Prepare the query with placeholders to prevent SQL injection + $do_search = $wpdb->prepare( + "SELECT * FROM {$license_table} + WHERE `email` LIKE %s + OR `first_name` LIKE %s + OR `last_name` LIKE %s + GROUP BY `email`", + $escaped_search_term, // Use the escaped search term with wildcards + $escaped_search_term, + $escaped_search_term + ); + + // Execute the query safely + $data = $wpdb->get_results($do_search, ARRAY_A); + + usort($data, array(&$this, 'sort_data')); + + // Pagination logic + $current_page = $this->get_pagenum(); + $total_items = count($data); + $data = array_slice($data, (($current_page - 1) * $per_page), $per_page); + $this->items = $data; + + // Set pagination arguments + $this->set_pagination_args(array( + 'total_items' => $total_items, + 'per_page' => $per_page, + 'total_pages' => ceil($total_items / $per_page) + )); + } +} + +function slm_subscribers_menu() +{ + $subscribers_list = new Subscribers_List_Table(); + $slm_subscriber_edit = isset($_REQUEST['slm_subscriber_edit']) ? sanitize_text_field($_REQUEST['slm_subscriber_edit']) : ''; + if ($slm_subscriber_edit === 'true') : ?> + +
    +

    +
    + +
    + +
    +
    +
    +
    + + + + + + + + +
    +
    +
    +
    +
    +
    + + + +
    +

    +
    +
    +
    +
    +
    +
    +
    + + prepare_items(); + $subscribers_list->search_box(__('Search', 'slm-plus'), 'search-box-id'); + $subscribers_list->views(); + $subscribers_list->display(); ?> +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + +Choose this date to set when the license should renew or expire. " +"
    Format: %s (input: YYYY-MM-DD)." +msgstr "" + +#: admin/slm-add-licenses.php:538 +msgid "Renewal" +msgstr "" + +#: admin/slm-add-licenses.php:542 admin/slm-lic-settings.php:167 +msgid "Billing Length" +msgstr "" + +#: admin/slm-add-licenses.php:545 +msgid "" +"Sets how often the license renews. E.g., a length of 2 with a term of years " +"means the license renews every 2 years." +msgstr "" + +#: admin/slm-add-licenses.php:550 admin/slm-lic-settings.php:173 +#: woocommerce/includes/slm-meta-boxes.php:207 +msgid "Expiration Term" +msgstr "" + +#: admin/slm-add-licenses.php:552 admin/slm-lic-settings.php:176 +#: woocommerce/includes/slm-meta-boxes.php:211 +msgid "Day(s)" +msgstr "" + +#: admin/slm-add-licenses.php:553 admin/slm-lic-settings.php:177 +#: woocommerce/includes/slm-meta-boxes.php:212 +msgid "Month(s)" +msgstr "" + +#: admin/slm-add-licenses.php:554 admin/slm-lic-settings.php:178 +#: woocommerce/includes/slm-meta-boxes.php:213 +msgid "Year(s)" +msgstr "" + +#: admin/slm-add-licenses.php:557 +msgid "Choose the renewal period: days, months, or years." +msgstr "" + +#: admin/slm-add-licenses.php:564 +msgid "Subscriber ID" +msgstr "" + +#: admin/slm-add-licenses.php:568 +#: woocommerce/includes/wc_licenses_class.php:205 +msgid "Date Renewed" +msgstr "" + +#: admin/slm-add-licenses.php:579 +#: woocommerce/includes/wc_licenses_class.php:204 +msgid "Date Activated" +msgstr "" + +#: admin/slm-add-licenses.php:593 +msgid "Transaction Information" +msgstr "" + +#: admin/slm-add-licenses.php:599 woocommerce/includes/slm-meta-boxes.php:174 +msgid "Item Reference" +msgstr "" + +#: admin/slm-add-licenses.php:604 +msgid "Transaction ID" +msgstr "" + +#: admin/slm-add-licenses.php:609 +msgid "Purchase ID" +msgstr "" + +#: admin/slm-add-licenses.php:614 +#: woocommerce/includes/wc_licenses_class.php:207 +msgid "Product Reference" +msgstr "" + +#: admin/slm-add-licenses.php:622 +msgid "Other" +msgstr "" + +#: admin/slm-add-licenses.php:627 +msgid "Until" +msgstr "" + +#: admin/slm-add-licenses.php:631 admin/slm-lic-settings.php:155 +#: woocommerce/includes/slm-meta-boxes.php:224 +msgid "Current Version" +msgstr "" + +#: admin/slm-add-licenses.php:635 +msgid "Reminder Sent" +msgstr "" + +#: admin/slm-add-licenses.php:639 +msgid "Reminder Sent Date" +msgstr "" + +#: admin/slm-add-licenses.php:651 +msgid "Manual Reset Count" +msgstr "" + +#: admin/slm-add-licenses.php:659 +msgid "Update License" +msgstr "" + +#: admin/slm-add-licenses.php:661 woocommerce/includes/slm-meta-boxes.php:395 +#: woocommerce/includes/slm-meta-boxes.php:399 +msgid "Create License" +msgstr "" + +#: admin/slm-admin-functions.php:14 +msgid "SLM Plus - Tools" +msgstr "" + +#: admin/slm-admin-functions.php:30 +msgid "Success message returned from the remote host." +msgstr "" + +#: admin/slm-admin-functions.php:33 +msgid "Request sent to the specified URL!" +msgstr "" + +#: admin/slm-admin-functions.php:45 +msgid "Log was cleared successfully!" +msgstr "" + +#: admin/slm-admin-functions.php:51 +msgid "Send Deactivation Message for a License" +msgstr "" + +#: admin/slm-admin-functions.php:56 +msgid "Send Request" +msgstr "" + +#: admin/slm-admin-functions.php:63 +msgid "Clean Activity Log" +msgstr "" + +#: admin/slm-admin-functions.php:65 +msgid "This will clear/reset license keys activities" +msgstr "" + +#: admin/slm-admin-functions.php:68 +msgid "Clear Log" +msgstr "" + +#: admin/slm-admin-functions.php:75 +msgid "Backup Database" +msgstr "" + +#: admin/slm-admin-functions.php:77 +msgid "" +"This will create a backup of the database tables related to this plugin and " +"save it to the uploads directory." +msgstr "" + +#: admin/slm-admin-functions.php:80 +msgid "Create Backup" +msgstr "" + +#: admin/slm-admin-functions.php:90 +msgid "Last backup created on: " +msgstr "" + +#: admin/slm-admin-functions.php:90 +msgid "Download Backup" +msgstr "" + +#: admin/slm-admin-functions.php:97 +msgid "Generate License for WooCommerce Orders" +msgstr "" + +#: admin/slm-admin-functions.php:100 admin/slm-lic-settings.php:249 +msgid "" +"This tool generates bulk licenses for WooCommerce orders placed before the " +"plugin was activated or for orders that lack existing licenses." +msgstr "" + +#: admin/slm-admin-functions.php:101 admin/slm-lic-settings.php:250 +msgid "Warning:" +msgstr "" + +#: admin/slm-admin-functions.php:102 admin/slm-lic-settings.php:251 +msgid "" +"This action cannot be undone. Please back up your database before proceeding." +msgstr "" + +#: admin/slm-admin-functions.php:112 +msgid "Product ID" +msgstr "" + +#: admin/slm-admin-functions.php:114 +msgid "Enter Product ID" +msgstr "" + +#: admin/slm-admin-functions.php:115 +msgid "Specify the default product ID for license generation." +msgstr "" + +#: admin/slm-admin-functions.php:120 +msgid "Subscription Type" +msgstr "" + +#: admin/slm-admin-functions.php:126 +msgid "Select the type of license for the order." +msgstr "" + +#: admin/slm-admin-functions.php:133 +msgid "Generate Licenses" +msgstr "" + +#: admin/slm-admin-functions.php:137 +msgid "" +"Please enable the WooCommerce License Generator option to activate the " +"Generate Licenses tool." +msgstr "" + +#: admin/slm-admin-functions.php:147 +msgid "License Generation Results:" +msgstr "" + +#: admin/slm-admin-functions.php:165 +msgid "Product ID cannot be empty." +msgstr "" + +#: admin/slm-admin-functions.php:166 +msgid "Product ID is required." +msgstr "" + +#: admin/slm-admin-functions.php:170 +msgid "Subscription Type cannot be empty." +msgstr "" + +#: admin/slm-admin-functions.php:171 +msgid "Subscription Type is required." +msgstr "" + +#: admin/slm-admin-functions.php:193 +msgid "Licenses generated successfully!" +msgstr "" + +#: admin/slm-admin-functions.php:196 +msgid "Some licenses failed to generate. Check the response for details." +msgstr "" + +#: admin/slm-admin-functions.php:200 +msgid "There was an error processing the request. Please try again." +msgstr "" + +#: admin/slm-admin-functions.php:201 +msgid "There was an error processing the request." +msgstr "" + +#: admin/slm-admin-functions.php:283 +msgid "Backup created successfully! Download from: " +msgstr "" + +#: admin/slm-admin-functions.php:285 +msgid "Error: Failed to create the backup file." +msgstr "" + +#. Plugin Name of the plugin/theme +#: admin/slm-dashboard-widgets.php:30 admin/slm-list-licenses-class.php:466 +msgid "SLM Plus" +msgstr "" + +#: admin/slm-dashboard-widgets.php:33 +msgid "slm-plus" +msgstr "" + +#: admin/slm-dashboard-widgets.php:42 +msgid "Overview" +msgstr "" + +#: admin/slm-dashboard-widgets.php:52 +msgid "Add new license" +msgstr "" + +#: admin/slm-dashboard-widgets.php:62 admin/slm-list-licenses-class.php:471 +#: slm-plus.php:69 +msgid "Settings" +msgstr "" + +#: admin/slm-dashboard-widgets.php:79 +msgid "Manage licenses" +msgstr "" + +#: admin/slm-dashboard-widgets.php:80 +msgid "Total active licenses" +msgstr "" + +#: admin/slm-dashboard-widgets.php:120 +msgid "Recent Licenses" +msgstr "" + +#: admin/slm-dashboard-widgets.php:121 +msgid "View All" +msgstr "" + +#: admin/slm-integration-help-page.php:14 +msgid "SLM Plus - Integration Help" +msgstr "" + +#: admin/slm-integration-help-page.php:15 +msgid "Version:" +msgstr "" + +#: admin/slm-integration-help-page.php:21 +msgid "API Settings" +msgstr "" + +#: admin/slm-integration-help-page.php:29 +msgid "License API Query POST URL for Your Installation" +msgstr "" + +#: admin/slm-integration-help-page.php:32 +msgid "License Activation/Deactivation API Secret Key" +msgstr "" + +#: admin/slm-integration-help-page.php:35 +msgid "License Creation API Secret Key" +msgstr "" + +#: admin/slm-integration-help-page.php:41 +msgid "Documentation and Guides" +msgstr "" + +#: admin/slm-integration-help-page.php:43 +msgid "Need more help? Check out the documentation:" +msgstr "" + +#: admin/slm-integration-help-page.php:45 +msgid "Postman API Demos" +msgstr "" + +#: admin/slm-integration-help-page.php:52 +msgid "Error Codes and Descriptions" +msgstr "" + +#: admin/slm-integration-help-page.php:57 +msgid "Constant" +msgstr "" + +#: admin/slm-integration-help-page.php:58 +msgid "Error Code" +msgstr "" + +#: admin/slm-integration-help-page.php:59 +msgid "Description" +msgstr "" + +#: admin/slm-integration-help-page.php:65 +msgid "The license creation failed due to an unknown error." +msgstr "" + +#: admin/slm-integration-help-page.php:66 +msgid "The license key provided during creation is invalid." +msgstr "" + +#: admin/slm-integration-help-page.php:67 +msgid "The domain associated with this license is already inactive." +msgstr "" + +#: admin/slm-integration-help-page.php:68 +msgid "The domain information is missing in the request." +msgstr "" + +#: admin/slm-integration-help-page.php:69 +msgid "The license key has been canceled." +msgstr "" + +#: admin/slm-integration-help-page.php:70 +msgid "Failed to cancel the license key." +msgstr "" + +#: admin/slm-integration-help-page.php:71 +msgid "Successfully deactivated the license key for the specified domain." +msgstr "" + +#: admin/slm-integration-help-page.php:72 +msgid "The license key was successfully deactivated." +msgstr "" + +#: admin/slm-integration-help-page.php:73 +msgid "Failed to delete the license key." +msgstr "" + +#: admin/slm-integration-help-page.php:74 +msgid "The license key was successfully deleted." +msgstr "" + +#: admin/slm-integration-help-page.php:75 +msgid "The license key has been deleted." +msgstr "" + +#: admin/slm-integration-help-page.php:76 +msgid "Failed to update the license key details." +msgstr "" + +#: admin/slm-integration-help-page.php:77 +msgid "The license key was successfully updated." +msgstr "" + +#: admin/slm-integration-help-page.php:78 +msgid "The license key was successfully activated." +msgstr "" + +#: admin/slm-integration-help-page.php:79 +msgid "The license key has been blocked from further use." +msgstr "" + +#: admin/slm-integration-help-page.php:80 +msgid "The license key was successfully created." +msgstr "" + +#: admin/slm-integration-help-page.php:81 +msgid "The license key already exists in the system." +msgstr "" + +#: admin/slm-integration-help-page.php:82 +msgid "The license key has expired." +msgstr "" + +#: admin/slm-integration-help-page.php:83 +msgid "The license key is already in use on another domain or device." +msgstr "" + +#: admin/slm-integration-help-page.php:84 +msgid "The license key is invalid." +msgstr "" + +#: admin/slm-integration-help-page.php:85 +msgid "Failed to delete the license key because it was not found." +msgstr "" + +#: admin/slm-integration-help-page.php:86 +msgid "Failed to update the license key because it was not found." +msgstr "" + +#: admin/slm-integration-help-page.php:87 +msgid "The license key has reached its maximum allowable devices." +msgstr "" + +#: admin/slm-integration-help-page.php:88 +msgid "The license key has reached its maximum allowable domains." +msgstr "" + +#: admin/slm-integration-help-page.php:89 +msgid "The key verification failed due to an invalid key." +msgstr "" + +#: admin/slm-lic-settings.php:72 +msgid "Options updated!" +msgstr "" + +#: admin/slm-lic-settings.php:85 +msgid "SLM Plus - Settings" +msgstr "" + +#: admin/slm-lic-settings.php:91 +msgid "General" +msgstr "" + +#: admin/slm-lic-settings.php:95 +msgid "Integrations" +msgstr "" + +#: admin/slm-lic-settings.php:99 +msgid "Debugging" +msgstr "" + +#: admin/slm-lic-settings.php:103 +msgid "Emails" +msgstr "" + +#: admin/slm-lic-settings.php:115 +msgid "Secret Key for License Creation" +msgstr "" + +#: admin/slm-lic-settings.php:118 +msgid "" +"This secret key will be used to authenticate any license creation request. " +"You can change it with something random." +msgstr "" + +#: admin/slm-lic-settings.php:122 +msgid "Secret Key for License Verification Requests" +msgstr "" + +#: admin/slm-lic-settings.php:124 +msgid "" +"This secret key will be used to authenticate any license verification " +"request from customer's site. Important! Do not change this value once your " +"customers start to use your product(s)!" +msgstr "" + +#: admin/slm-lic-settings.php:128 +msgid "License Key Prefix" +msgstr "" + +#: admin/slm-lic-settings.php:130 +msgid "" +"You can optionaly specify a prefix for the license keys. This prefix will be " +"added to the uniquely generated license keys." +msgstr "" + +#: admin/slm-lic-settings.php:134 +msgid "Maximum Allowed Devices" +msgstr "" + +#: admin/slm-lic-settings.php:136 +msgid "" +"Maximum number of devices which each license is valid for (default value)." +msgstr "" + +#: admin/slm-lic-settings.php:141 +msgid "Maximum Allowed Domains" +msgstr "" + +#: admin/slm-lic-settings.php:143 +msgid "" +"Maximum number of domains which each license is valid for (default value)." +msgstr "" + +#: admin/slm-lic-settings.php:148 +msgid "Support Until Ver." +msgstr "" + +#: admin/slm-lic-settings.php:150 +msgid "" +"This is used to enable bulk license generation for WooCommerce orders placed " +"before the plugin was active or for orders that do not already contain " +"licenses (default setting)." +msgstr "" + +#: admin/slm-lic-settings.php:157 admin/slm-lic-settings.php:169 +#: admin/slm-lic-settings.php:181 +msgid "" +"This is used to enable bulk license generation for WooCommerce orders placed " +"before the plugin was active or for orders that do not already contain " +"licenses (default setting:)." +msgstr "" + +#: admin/slm-lic-settings.php:180 +msgid "Frequency period: in days, months, or years" +msgstr "" + +#: admin/slm-lic-settings.php:187 +msgid "Auto Expire License Keys" +msgstr "" + +#: admin/slm-lic-settings.php:189 +msgid "Enable auto expiration " +msgstr "" + +#: admin/slm-lic-settings.php:190 +msgid "" +" When enabled, it will automatically set the status of a license key to " +"\"Expired\" when the expiry date value of the key is reached. It doesn't " +"remotely deactivate a key. It simply changes the status of the key in your " +"database to expired." +msgstr "" + +#: admin/slm-lic-settings.php:194 +msgid "General settings" +msgstr "" + +#: admin/slm-lic-settings.php:197 +msgid "Enable stats in licenses overview page." +msgstr "" + +#: admin/slm-lic-settings.php:205 +msgid "Enable admin bar shortcut link" +msgstr "" + +#: admin/slm-lic-settings.php:210 +msgid "Multiple items validation" +msgstr "" + +#: admin/slm-lic-settings.php:213 +msgid "Enable verification of Item reference." +msgstr "" + +#: admin/slm-lic-settings.php:214 +msgid "" +"When enabled, there will be another field in Licenced product - Item " +"reference. This field should correspond to the API parameter item_reference " +"of your software." +msgstr "" + +#: admin/slm-lic-settings.php:219 +msgid "User permissions" +msgstr "" + +#: admin/slm-lic-settings.php:222 +msgid "Allow users to remove domains/devices in My account." +msgstr "" + +#: admin/slm-lic-settings.php:223 +msgid "" +"When enabled, users will be able to remove registered domains or devices in " +"their account." +msgstr "" + +#: admin/slm-lic-settings.php:232 +msgid "WooCommerce Settings" +msgstr "" + +#: admin/slm-lic-settings.php:236 +msgid "WooCommerce" +msgstr "" + +#: admin/slm-lic-settings.php:239 +msgid "" +"Enable WooCommerce Support (A fully customizable, open source eCommerce " +"platform built for WordPress.)" +msgstr "" + +#: admin/slm-lic-settings.php:247 +msgid "Enable WooCommerce Order License Generator" +msgstr "" + +#: admin/slm-lic-settings.php:260 +msgid "" +"Disable WooCommerce download page. Process downloads though license order " +"info page." +msgstr "" + +#: admin/slm-lic-settings.php:267 +msgid "" +"Enable WooCommerce downloads expiration. Downloads will expire together with " +"corresponding license." +msgstr "" + +#: admin/slm-lic-settings.php:272 admin/slm-lic-settings.php:275 +msgid "WP eStores" +msgstr "" + +#: admin/slm-lic-settings.php:278 +msgid "Enable WordPress eStore Plugin Support." +msgstr "" + +#: admin/slm-lic-settings.php:289 +msgid "Enable Debug Logging" +msgstr "" + +#: admin/slm-lic-settings.php:292 +msgid "If checked, debug output will be written to log files." +msgstr "" + +#: admin/slm-lic-settings.php:297 +msgid "SLM Plus Version" +msgstr "" + +#: admin/slm-lic-settings.php:304 +msgid "SLM Databse Version" +msgstr "" + +#: admin/slm-lic-settings.php:311 +msgid "SLM Rewrite Version" +msgstr "" + +#: admin/slm-lic-settings.php:326 +msgid "Expiration reminder" +msgstr "" + +#: admin/slm-lic-settings.php:336 +msgid "Update Options" +msgstr "" + +#: admin/slm-list-licenses-class.php:27 +msgid "No licenses avaliable." +msgstr "" + +#: admin/slm-list-licenses-class.php:43 +msgid "All" +msgstr "" + +#: admin/slm-list-licenses-class.php:50 +msgid "active" +msgstr "" + +#: admin/slm-list-licenses-class.php:57 +msgid "pending" +msgstr "" + +#: admin/slm-list-licenses-class.php:64 +msgid "expired" +msgstr "" + +#: admin/slm-list-licenses-class.php:71 +msgid "blocked" +msgstr "" + +#: admin/slm-list-licenses-class.php:85 admin/slm-subscribers.php:189 +#: woocommerce/includes/wc_licenses_class.php:114 +#: woocommerce/includes/wc_licenses_class.php:200 +msgid "Status" +msgstr "" + +#: admin/slm-list-licenses-class.php:86 +msgid "Key" +msgstr "" + +#: admin/slm-list-licenses-class.php:87 +msgid "Item reference" +msgstr "" + +#: admin/slm-list-licenses-class.php:88 +msgid "License type" +msgstr "" + +#: admin/slm-list-licenses-class.php:90 +msgid "Domains" +msgstr "" + +#: admin/slm-list-licenses-class.php:91 +msgid "Devices" +msgstr "" + +#: admin/slm-list-licenses-class.php:92 +msgid "Order #" +msgstr "" + +#: admin/slm-list-licenses-class.php:93 +msgid "Created on" +msgstr "" + +#: admin/slm-list-licenses-class.php:94 +msgid "Renewed on" +msgstr "" + +#: admin/slm-list-licenses-class.php:95 +msgid "Activated on" +msgstr "" + +#: admin/slm-list-licenses-class.php:96 +msgid "Expiration" +msgstr "" + +#: admin/slm-list-licenses-class.php:97 +msgid "Until Ver." +msgstr "" + +#: admin/slm-list-licenses-class.php:98 +msgid "Current Ver." +msgstr "" + +#: admin/slm-list-licenses-class.php:120 +msgid " Lifetime " +msgstr "" + +#: admin/slm-list-licenses-class.php:213 admin/slm-list-licenses-class.php:231 +#: admin/slm-list-licenses-class.php:249 admin/slm-list-licenses-class.php:267 +#: admin/slm-list-licenses-class.php:285 +msgid "Error - Please select some records using the checkboxes" +msgstr "" + +#: admin/slm-list-licenses-class.php:467 +msgid "Manage Licenses" +msgstr "" + +#: admin/slm-list-licenses-class.php:468 +msgid "Create license" +msgstr "" + +#: admin/slm-list-licenses-class.php:469 +msgid "Subscribers" +msgstr "" + +#: admin/slm-list-licenses-class.php:470 +msgid "Tools" +msgstr "" + +#: admin/slm-list-licenses-class.php:472 +msgid "Help" +msgstr "" + +#: admin/slm-list-licenses-class.php:473 +msgid "About" +msgstr "" + +#: admin/slm-manage-licenses.php:15 +msgid "You do not have permission to manage this license." +msgstr "" + +#: admin/slm-manage-licenses.php:36 +msgid "SLM Plus - Manage Licenses" +msgstr "" + +#: admin/slm-manage-licenses.php:38 +msgid "Add New" +msgstr "" + +#: admin/slm-manage-licenses.php:62 admin/slm-subscribers.php:217 +msgid "Search" +msgstr "" + +#: admin/slm-subscribers.php:88 +msgid "Email Address" +msgstr "" + +#: admin/slm-subscribers.php:176 +msgid "Overview - Manage Subscribers" +msgstr "" + +#: admin/slm-subscribers.php:178 +msgid "View all" +msgstr "" + +#: admin/slm-subscribers.php:188 +msgid "License key" +msgstr "" + +#: admin/slm-subscribers.php:206 +msgid "Overview - All Subscribers" +msgstr "" + +#: includes/class-slm-installer.php:41 +msgid "Pending" +msgstr "" + +#: includes/class-slm-installer.php:42 +msgid "Active" +msgstr "" + +#: includes/class-slm-installer.php:43 +msgid "Blocked" +msgstr "" + +#: includes/class-slm-installer.php:44 +msgid "Expired" +msgstr "" + +#: includes/slm-utility.php:421 includes/slm-utility.php:437 +msgid "0 days remaining" +msgstr "" + +#: includes/slm-utility.php:430 +#, php-format +msgid "%s days remaining until %s" +msgstr "" + +#: includes/slm-utility.php:482 +msgid "License key not found or invalid email." +msgstr "" + +#: includes/slm-utility.php:882 +msgid " view" +msgstr "" + +#: includes/slm-utility.php:894 +msgid "Request" +msgstr "" + +#: includes/slm-utility.php:902 +msgid "Source:" +msgstr "" + +#: includes/slm-utility.php:903 +msgid "Time:" +msgstr "" + +#: includes/slm-utility.php:949 +msgid "Not registered yet" +msgstr "" + +#: includes/slm-utility.php:967 includes/slm-utility.php:979 +msgid "License information" +msgstr "" + +#: includes/slm-utility.php:980 +msgid "License type: " +msgstr "" + +#: includes/slm-utility.php:981 +msgid "Domains allowed: " +msgstr "" + +#: includes/slm-utility.php:982 +msgid "Devices allowed: " +msgstr "" + +#: includes/slm-utility.php:983 +msgid "Renews every " +msgstr "" + +#: woocommerce/includes/create-license-orders.php:42 +msgid "Product ID is missing in the request." +msgstr "" + +#: woocommerce/includes/create-license-orders.php:58 +msgid "" +"The provided Product ID does not correspond to a valid WooCommerce product. " +"Please check the ID and try again." +msgstr "" + +#: woocommerce/includes/create-license-orders.php:190 +#: woocommerce/includes/create-license-orders.php:239 +#: woocommerce/includes/slm-meta-boxes.php:530 +#, php-format +msgid "License Key generated: %s" +msgstr "" + +#: woocommerce/includes/create-license-orders.php:260 +#, php-format +msgid "%d orders were skipped:" +msgstr "" + +#: woocommerce/includes/create-license-orders.php:264 +#, php-format +msgid "" +"Order ID %d was skipped due to: %s. View " +"Order" +msgstr "" + +#: woocommerce/includes/create-license-orders.php:273 +#, php-format +msgid "%d licenses generated successfully:" +msgstr "" + +#: woocommerce/includes/create-license-orders.php:276 +#, php-format +msgid "" +"License Key: %s for Order ID %d - View " +"Order" +msgstr "" + +#: woocommerce/includes/create-license-orders.php:282 +#, php-format +msgid "%d licenses failed to generate." +msgstr "" + +#: woocommerce/includes/purchase.php:90 +msgid "License no longer exists" +msgstr "" + +#: woocommerce/includes/purchase.php:95 +#: woocommerce/includes/slm-meta-boxes.php:381 +msgid "License Key:" +msgstr "" + +#: woocommerce/includes/purchase.php:193 +msgid "License could not be created: Invalid sites allowed number." +msgstr "" + +#: woocommerce/includes/purchase.php:344 +msgid "License Key(s) generated:" +msgstr "" + +#: woocommerce/includes/purchase.php:373 +msgid "License Key(s) could not be created." +msgstr "" + +#: woocommerce/includes/purchase.php:627 +#, php-format +msgid "Order confirmation email sent to: %s" +msgstr "" + +#: woocommerce/includes/purchase.php:675 +#: woocommerce/includes/wc_licenses_class.php:215 +msgid "License Details" +msgstr "" + +#: woocommerce/includes/purchase.php:690 +msgid "View My Licenses" +msgstr "" + +#: woocommerce/includes/purchase.php:748 +msgid "License Keys" +msgstr "" + +#: woocommerce/includes/purchase.php:753 +msgid "Product" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:51 +msgid "License product" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:68 +msgid "License Manager" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:70 +msgid "Enables the license creation API." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:120 +msgid "License Info" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:146 +msgid "Domain Licenses" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:152 +msgid "Enter the allowed number of domains this license can have (websites)." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:161 +msgid "Devices Licenses" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:167 +msgid "" +"Enter the allowed number of devices this license can have (computers, " +"mobile, etc)." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:175 +msgid "Software's item reference" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:177 +msgid "" +"Enter the item reference of your application, theme, or plug-in. The license " +"will be then bound to this exact software." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:186 +msgid "Type of license: subscription base or lifetime" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:188 +msgid "Select one" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:197 +msgid "Renewal Period Length" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:198 +msgid "XX Amount of days, months, or years." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:209 +msgid "Choose between days, months, or years" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:214 +msgid "One Time" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:227 +msgid "Enter the current version of your application, theme, or plug-in" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:233 +msgid "Until Version" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:236 +msgid "Enter the version until support expires." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:361 +msgid "SLM Properties" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:382 +#: woocommerce/includes/slm-meta-boxes.php:389 +msgid "License Type:" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:383 +msgid "A license key is already assigned to this order." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:398 +msgid "Order must be completed or processing to create a license." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:421 +msgid "" +"Warning: The order lacks user information like last name or email. Do you " +"still wish to create the license?" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:425 +msgid "Are you sure you want to create a license for this order?" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:435 +msgid "License creation failed. Please check the logs." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:441 +msgid "Unable to verify order details. Please try again." +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:460 +#: woocommerce/includes/slm-meta-boxes.php:545 +msgid "Invalid order ID" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:465 +msgid "Order must be completed or processing to create a license" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:518 +msgid "API request failed" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:533 +msgid "License created successfully" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:535 +msgid "License creation failed" +msgstr "" + +#: woocommerce/includes/slm-meta-boxes.php:554 +msgid "Order not found" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:59 +msgid "My Licenses" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:84 +#: woocommerce/includes/wc_licenses_class.php:188 +msgid "Invalid license or access denied." +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:112 +#: woocommerce/includes/wc_licenses_class.php:202 +msgid "Order ID" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:132 +msgid "Manual" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:140 +msgid "View" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:195 +msgid "Back to My Licenses" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:206 +msgid "Date Expiry" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:208 +msgid "Subscription ID" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:231 +msgid "No Order Information Available" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:247 +msgid "Product Not Found" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:250 +msgid "No Product Information Available" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:256 +msgid "Not activated yet" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:256 +msgid "Not renewed yet" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:289 +msgid "No activations found." +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:305 +msgid "Delete" +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:327 +msgid "Activation successfully deleted. Reload Page." +msgstr "" + +#: woocommerce/includes/wc_licenses_class.php:329 +msgid "Failed to delete activation. Please try again." +msgstr "" + +#. Plugin URI of the plugin/theme +msgid "https://github.com/michelve/software-license-manager/" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"A comprehensive software license management solution for web applications " +"including WordPress plugins, themes, and PHP-based software. Seamlessly " +"integrates with WooCommerce to offer license key generation, management, and " +"validation. Ideal for developers managing software licenses across multiple " +"platforms with built-in multilingual support and performance optimization." +msgstr "" + +#. Author of the plugin/theme +msgid "Michel Velis" +msgstr "" + +#. Author URI of the plugin/theme +msgid "https://github.com/michelve/" +msgstr "" diff --git a/includes/class-slm-activator.php b/includes/class-slm-activator.php new file mode 100755 index 0000000..4989f11 --- /dev/null +++ b/includes/class-slm-activator.php @@ -0,0 +1,48 @@ +get_charset_collate(); + +$used_db_version = get_option('slm_db_version', '5.0.0'); + +// Check the current database version +$new_db_version = SLM_DB_VERSION; + +// Table definitions +$lic_key_table = SLM_TBL_LICENSE_KEYS; + +// Ensure backward compatibility updates are applied +if (version_compare($used_db_version, $new_db_version, '<')) { + error_log("SLM: Starting database updates from version $used_db_version to $new_db_version."); + + // Check if the 'associated_orders' column exists + $column_exists = $wpdb->get_results("SHOW COLUMNS FROM $lic_key_table LIKE 'associated_orders'"); + + // Ensure the 'time' column is DATETIME + $check_time_column = $wpdb->get_results("SHOW COLUMNS FROM $lic_log_tbl LIKE 'time'"); + if (!empty($check_time_column)) { + $time_column_info = $wpdb->get_row("SHOW COLUMNS FROM $lic_log_tbl WHERE Field = 'time'"); + if ($time_column_info->Type !== 'datetime') { + error_log("SLM: Updating 'time' column to DATETIME in $lic_log_tbl."); + $update_time_column_query = " + ALTER TABLE $lic_log_tbl + MODIFY COLUMN time DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'; + "; + $update_time_column_result = $wpdb->query($update_time_column_query); + if ($update_time_column_result === false) { + error_log("SLM: Error updating 'time' column - " . $wpdb->last_error); + } else { + error_log("SLM: 'time' column updated successfully to DATETIME."); + } + } else { + error_log("SLM: 'time' column is already DATETIME."); + } + } else { + error_log("SLM: 'time' column does not exist in $lic_log_tbl. Skipping update."); + } + + // Add the 'time_only' column if it doesn't exist + $check_time_only_column = $wpdb->get_results("SHOW COLUMNS FROM $lic_log_tbl LIKE 'time_only'"); + if (empty($check_time_only_column)) { + error_log("SLM: Adding missing column 'time_only' to $lic_log_tbl."); + $add_time_only_column_query = " + ALTER TABLE $lic_log_tbl + ADD COLUMN time_only TIME NOT NULL DEFAULT '00:00:00'; + "; + $add_time_only_column_result = $wpdb->query($add_time_only_column_query); + if ($add_time_only_column_result === false) { + error_log("SLM: Error adding 'time_only' column - " . $wpdb->last_error); + } else { + error_log("SLM: 'time_only' column added successfully."); + } + } else { + error_log("SLM: Column 'time_only' already exists in $lic_log_tbl."); + } + + + if (empty($column_exists)) { + error_log("SLM: Adding missing column 'associated_orders' to $lic_key_table."); + + // Add missing columns to the license keys table + $lk_tbl_sql_mod = " + ALTER TABLE $lic_key_table + ADD COLUMN associated_orders TEXT DEFAULT NULL; + "; + + $result = $wpdb->query($lk_tbl_sql_mod); + + if ($result === false) { + error_log("SLM: Error adding 'associated_orders' column - " . $wpdb->last_error); + } else { + error_log("SLM: 'associated_orders' column added successfully."); + } + } else { + error_log("SLM: Column 'associated_orders' already exists in $lic_key_table."); + } + + // Add other missing columns (if required) + $other_columns = array( + 'item_reference' => "VARCHAR(255) NOT NULL", + 'slm_billing_length' => "VARCHAR(255) NOT NULL", + 'slm_billing_interval' => "ENUM('days', 'months', 'years', 'onetime') NOT NULL DEFAULT 'days'", + 'wc_order_id' => "INT DEFAULT NULL", + 'payment_status' => "ENUM('pending', 'completed', 'failed') DEFAULT 'pending'", + 'renewal_attempts' => "INT DEFAULT 0", + 'lic_status' => "ENUM('pending', 'active', 'expired', 'suspended', 'blocked', 'trial') NOT NULL DEFAULT 'pending'" + ); + + foreach ($other_columns as $column => $definition) { + $column_exists = $wpdb->get_results("SHOW COLUMNS FROM $lic_key_table LIKE '$column'"); + if (empty($column_exists)) { + // Add missing column + $alter_query = "ALTER TABLE $lic_key_table ADD COLUMN $column $definition;"; + error_log("SLM: Adding missing column '$column' to $lic_key_table."); + $result = $wpdb->query($alter_query); + + if ($result === false) { + error_log("SLM: Error adding column '$column' - " . $wpdb->last_error); + } else { + error_log("SLM: Column '$column' added successfully."); + } + } else { + // Check if the column definition needs to be updated (for example, updating lic_status ENUM values) + $column_info = $wpdb->get_row("SHOW COLUMNS FROM $lic_key_table LIKE '$column'"); + if ($column === 'lic_status' && strpos($column_info->Type, "'trial'") === false) { + // Update lic_status to include the new ENUM values + $alter_query = "ALTER TABLE $lic_key_table MODIFY COLUMN $column $definition;"; + error_log("SLM: Updating column '$column' in $lic_key_table."); + $result = $wpdb->query($alter_query); + + if ($result === false) { + error_log("SLM: Error updating column '$column' - " . $wpdb->last_error); + } else { + error_log("SLM: Column '$column' updated successfully."); + } + } else { + error_log("SLM: Column '$column' already exists and does not need updates."); + } + } + } + + // Update the database version + update_option("slm_db_version", $new_db_version); + error_log("SLM database updated from version $used_db_version to $new_db_version."); +} else { + error_log("SLM: No updates needed for backward compatibility. Current version: $used_db_version."); +} + + +// Create license statuses table if it doesn't exist +$status_table_sql = "CREATE TABLE IF NOT EXISTS " . $lic_status_table . " ( + id INT(11) NOT NULL AUTO_INCREMENT, + status_key VARCHAR(255) NOT NULL, + status_label VARCHAR(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY unique_status_key (status_key) +) " . $charset_collate . ";"; +dbDelta($status_table_sql); + + +// Insert default statuses if table is empty +$status_count = $wpdb->get_var("SELECT COUNT(*) FROM $lic_status_table"); +if ($status_count == 0) { + $default_statuses = array( + array('status_key' => 'pending', 'status_label' => __('Pending', 'slm-plus')), + array('status_key' => 'active', 'status_label' => __('Active', 'slm-plus')), + array('status_key' => 'blocked', 'status_label' => __('Blocked', 'slm-plus')), + array('status_key' => 'trial', 'status_label' => __('Trial', 'slm-plus')), + array('status_key' => 'expired', 'status_label' => __('Expired', 'slm-plus')) + ); + + foreach ($default_statuses as $status) { + $wpdb->insert($lic_status_table, $status); + } +} + +// Create or update the license keys table structure +$lk_tbl_sql = "CREATE TABLE IF NOT EXISTS " . $lic_key_table . " ( + id INT(12) NOT NULL AUTO_INCREMENT, + license_key VARCHAR(255) NOT NULL, + item_reference VARCHAR(255) NOT NULL, + product_ref VARCHAR(255) NOT NULL DEFAULT '', + subscr_id VARCHAR(128) NOT NULL DEFAULT '', + txn_id VARCHAR(64) NOT NULL DEFAULT '', + purchase_id_ VARCHAR(255) NOT NULL DEFAULT '', + wc_order_id INT DEFAULT NULL, + associated_orders TEXT DEFAULT NULL, + first_name VARCHAR(32) NOT NULL DEFAULT '', + last_name VARCHAR(32) NOT NULL DEFAULT '', + email VARCHAR(64) NOT NULL, + company_name VARCHAR(100) NOT NULL DEFAULT '', + lic_status ENUM('pending', 'active', 'expired', 'suspended', 'blocked', 'trial') NOT NULL DEFAULT 'pending', + lic_type ENUM('none', 'subscription', 'lifetime') NOT NULL DEFAULT 'subscription', + max_allowed_domains INT NOT NULL, + max_allowed_devices INT NOT NULL, + manual_reset_count VARCHAR(128) NOT NULL DEFAULT '', + current_ver VARCHAR(255) NOT NULL DEFAULT '', + until VARCHAR(255) NOT NULL DEFAULT '', + slm_billing_length VARCHAR(255) NOT NULL, + slm_billing_interval ENUM('days', 'months', 'years', 'onetime') NOT NULL DEFAULT 'days', + payment_status ENUM('pending', 'completed', 'failed') DEFAULT 'pending', + renewal_attempts INT DEFAULT 0, + date_created DATE NOT NULL DEFAULT '0000-00-00', + date_activated DATE NOT NULL DEFAULT '0000-00-00', + date_renewed DATE NOT NULL DEFAULT '0000-00-00', + date_expiry DATE NOT NULL DEFAULT '0000-00-00', + reminder_sent_date DATE NOT NULL DEFAULT '0000-00-00', + reminder_sent VARCHAR(255) NOT NULL DEFAULT '0', + PRIMARY KEY (id) +) " . $charset_collate . ";"; + + +dbDelta($lk_tbl_sql); + +// Create domains table if not exists +$ld_tbl_sql = "CREATE TABLE IF NOT EXISTS " . $lic_domain_table . " ( + id INT NOT NULL AUTO_INCREMENT, + lic_key_id INT NOT NULL, + lic_key varchar(255) NOT NULL, + registered_domain text NOT NULL, + registered_devices text NOT NULL, + item_reference varchar(255) NOT NULL, + PRIMARY KEY (id) +)" . $charset_collate . ";"; +dbDelta($ld_tbl_sql); + +// Create emails table if not exists +$slm_emails_tbl = "CREATE TABLE IF NOT EXISTS " . $lic_emails_table . " ( + id INT NOT NULL AUTO_INCREMENT, + lic_key varchar(255) NOT NULL, + sent_to varchar(255) NOT NULL, + status varchar(255) NOT NULL, + sent text NOT NULL, + date_sent date NOT NULL DEFAULT '0000-00-00', + disable_notifications text NOT NULL, + PRIMARY KEY (id) +)" . $charset_collate . ";"; +dbDelta($slm_emails_tbl); + +// Create log table if not exists +$log_tbl_sql = "CREATE TABLE IF NOT EXISTS" . $lic_log_tbl . " ( + id INT NOT NULL AUTO_INCREMENT, + license_key VARCHAR(255) NOT NULL, + slm_action VARCHAR(255) NOT NULL, + time DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', -- Store combined date and time + time_only TIME NOT NULL DEFAULT '00:00:00', -- Store time only + source VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +) $charset_collate;"; +dbDelta($log_tbl_sql); + + +// Create devices table if not exists +$ldv_tbl_sql = "CREATE TABLE IF NOT EXISTS " . $lic_devices_table . " ( + id INT NOT NULL AUTO_INCREMENT, + lic_key_id INT NOT NULL, + lic_key varchar(255) NOT NULL, + registered_devices text NOT NULL, + registered_domain text NOT NULL, + item_reference varchar(255) NOT NULL, + PRIMARY KEY (id) +)" . $charset_collate . ";"; +dbDelta($ldv_tbl_sql); + +// Add new options if they don't exist and preserve old settings +$new_options = array( + 'lic_creation_secret' => SLM_Utility::create_secret_keys(), + 'lic_verification_secret' => SLM_Utility::create_secret_keys(), + 'lic_prefix' => 'SLM-', + 'default_max_domains' => '2', + 'default_max_devices' => '2', + 'enable_debug' => '', + 'slm_woo' => '1', + 'license_current_version' => '1.0.0', + 'license_until_version' => '2.1.0', + 'slm_wc_lic_generator' => '1', + 'slm_woo_downloads' => '', + 'slm_woo_affect_downloads' => '1', + 'slm_wpestores' => '', + 'slm_stats' => '1', + 'slm_billing_length' => '1', + 'slm_billing_interval' => 'years', + 'slm_adminbar' => '1', + 'slm_multiple_items' => '', + 'slm_conflictmode' => '1', + 'slm_front_conflictmode' => '1', + 'enable_auto_key_expiration' => '1', + 'slm_backup_dir_hash' => '/slm-plus-' . wp_generate_password(8, false, false), + 'slm_dl_manager' => '', + 'allow_user_activation_removal' => '1', + 'expiration_reminder_text' => 'Your account has reverted to Basic with limited functionality. You can renew today to keep using it on all your devices and enjoy the valuable features. It is a smart investment.' +); + +// Retrieve existing options to merge them with the new ones +$existing_options = get_option('slm_plugin_options', []); +$merged_options = array_merge($new_options, $existing_options); + +// Update options with merged settings +update_option('slm_plugin_options', $merged_options); + +// Create the backup directory if it doesn't already exist +$upload_dir = wp_upload_dir(); // Get the WordPress upload directory +$backup_dir_path = $upload_dir['basedir'] . $merged_options['slm_backup_dir_hash']; + +// Check if the directory exists, and if not, create it +if (!file_exists($backup_dir_path)) { + wp_mkdir_p($backup_dir_path); +} + diff --git a/software-license-manager/includes/index.html b/includes/index.php old mode 100644 new mode 100755 similarity index 100% rename from software-license-manager/includes/index.html rename to includes/index.php diff --git a/includes/mails/expired.php b/includes/mails/expired.php new file mode 100644 index 0000000..5a2fec3 --- /dev/null +++ b/includes/mails/expired.php @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +'; \ No newline at end of file diff --git a/includes/slm-api-listener.php b/includes/slm-api-listener.php new file mode 100755 index 0000000..b7c868b --- /dev/null +++ b/includes/slm-api-listener.php @@ -0,0 +1,859 @@ +renew_api_listener(); + break; + case 'slm_create_new': + $this->creation_api_listener(); + break; + case 'slm_activate': + $this->activation_api_listener(); + break; + case 'slm_deactivate': + $this->deactivation_api_listener(); + break; + case 'slm_update': + $this->update_api_listener(); + break; + case 'slm_check': + $this->check_api_listener(); + break; + case 'slm_info': + $this->check_api_info(); + break; + } + } + } + + + function creation_api_listener() + { + if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) === 'slm_create_new') { + global $slm_debug_logger, $wpdb; + $slm_lic_table = SLM_TBL_LICENSE_KEYS; + $options = get_option('slm_plugin_options'); + $lic_key_prefix = $options['lic_prefix'] ?? 'SLM-'; // Use default prefix if missing + + // Security check: Verify secret key + SLM_API_Utility::verify_secret_key_for_creation(); + + // Logging for debugging + $slm_debug_logger->log_debug("API - license creation (slm_create_new) request received."); + + // Trigger action hook for external integrations + do_action('slm_api_listener_slm_create_new'); + + // Initialize fields array + $fields = []; + + // License key handling + if (!empty($_REQUEST['license_key'])) { + $fields['license_key'] = sanitize_text_field($_REQUEST['license_key']); + } else { + $fields['license_key'] = slm_get_license($lic_key_prefix); // Generate if not provided + } + + // Sanitize and prepare other fields + $fields['lic_status'] = !empty($_REQUEST['lic_status']) ? sanitize_text_field($_REQUEST['lic_status']) : 'pending'; + $fields['first_name'] = sanitize_text_field($_REQUEST['first_name']); + $fields['last_name'] = sanitize_text_field($_REQUEST['last_name']); + $fields['purchase_id_'] = sanitize_text_field($_REQUEST['purchase_id_']); + $fields['email'] = sanitize_email($_REQUEST['email']); + $fields['company_name'] = !empty($_REQUEST['company_name']) ? sanitize_text_field($_REQUEST['company_name']) : ''; + $fields['txn_id'] = sanitize_text_field($_REQUEST['txn_id']); + $fields['max_allowed_domains'] = !empty($_REQUEST['max_allowed_domains']) ? intval($_REQUEST['max_allowed_domains']) : intval($options['default_max_domains']); + $fields['max_allowed_devices'] = !empty($_REQUEST['max_allowed_devices']) ? intval($_REQUEST['max_allowed_devices']) : intval($options['default_max_devices']); + $fields['date_created'] = isset($_REQUEST['date_created']) ? sanitize_text_field($_REQUEST['date_created']) : wp_date('Y-m-d'); + $fields['date_expiry'] = !empty($_REQUEST['date_expiry']) ? sanitize_text_field($_REQUEST['date_expiry']) : null; + $fields['product_ref'] = !empty($_REQUEST['product_ref']) ? sanitize_text_field($_REQUEST['product_ref']) : ''; + $fields['until'] = !empty($_REQUEST['until']) ? sanitize_text_field($_REQUEST['until']) : ''; + $fields['current_ver'] = !empty($_REQUEST['current_ver']) ? sanitize_text_field($_REQUEST['current_ver']) : ''; + $fields['subscr_id'] = !empty($_REQUEST['subscr_id']) ? sanitize_text_field($_REQUEST['subscr_id']) : ''; + $fields['item_reference'] = !empty($_REQUEST['item_reference']) ? sanitize_text_field($_REQUEST['item_reference']) : ''; + $fields['lic_type'] = !empty($_REQUEST['lic_type']) ? sanitize_text_field($_REQUEST['lic_type']) : ''; + $fields['slm_billing_length'] = !empty($_REQUEST['slm_billing_length']) ? sanitize_text_field($_REQUEST['slm_billing_length']) : ''; + $fields['slm_billing_interval'] = !empty($_REQUEST['slm_billing_interval']) ? sanitize_text_field($_REQUEST['slm_billing_interval']) : ''; + + // Validation for subscription-type licenses + if ($fields['lic_type'] === 'subscription') { + if (empty($fields['slm_billing_length'])) { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'License creation failed. Specify "slm_billing_length".', + 'error_code' => SLM_Error_Codes::CREATE_FAILED + ]); + } + if (empty($fields['slm_billing_interval'])) { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'License creation failed. Specify "slm_billing_interval".', + 'error_code' => SLM_Error_Codes::CREATE_FAILED + ]); + } + } + + // Insert the license into the database + $result = $wpdb->insert($slm_lic_table, $fields); + + // Error handling for database insertion + if ($result === false) { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'License creation failed', + 'error_code' => SLM_Error_Codes::CREATE_FAILED + ]); + } else { + // Success: License created + $response_args = [ + 'result' => 'success', + 'message' => 'License successfully created', + 'key' => $fields['license_key'], + 'code' => SLM_Error_Codes::LICENSE_CREATED + ]; + + // Log license creation + SLM_Utility::create_log($fields['license_key'], 'slm_create_new'); + + // Output API response + SLM_API_Utility::output_api_response($response_args); + } + } + } + + function activation_api_listener() + { + if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) === 'slm_activate') { + global $slm_debug_logger, $wpdb; + $sql_prep1 = ""; + $options = get_option('slm_plugin_options'); + + // Verify secret key first for security + SLM_API_Utility::verify_secret_key(); + + // Trigger action hook for external integrations + do_action('slm_api_listener_slm_activate'); + + // Initialize fields + $fields = []; + $fields['lic_key'] = sanitize_text_field($_REQUEST['license_key']); + $registered_domain = isset($_REQUEST['registered_domain']) ? sanitize_text_field($_REQUEST['registered_domain']) : ''; + $registered_devices = isset($_REQUEST['registered_devices']) ? sanitize_text_field($_REQUEST['registered_devices']) : ''; + $item_reference = isset($_REQUEST['item_reference']) ? sanitize_text_field($_REQUEST['item_reference']) : ''; + + + // Table names + $slm_lic_table = SLM_TBL_LICENSE_KEYS; + $reg_domain_table = SLM_TBL_LIC_DOMAIN; + $reg_table_devices = SLM_TBL_LIC_DEVICES; + + // Check if multiple items need verification + if (!empty($item_reference) && $options['slm_multiple_items'] == 1) { + $sql_prep1 = $wpdb->prepare("SELECT * FROM $slm_lic_table WHERE license_key = %s AND item_reference = %s", $fields['lic_key'], $item_reference); + } else { + $sql_prep1 = $wpdb->prepare("SELECT * FROM $slm_lic_table WHERE license_key = %s", $fields['lic_key']); + } + + // Get the license details from the database + $retLic = $wpdb->get_row($sql_prep1, OBJECT); + + SLM_Helper_Class::write_log('User ID (subscr_id): ' . $sql_prep1); + + + if (!$retLic) { + $args = ['result' => 'error', 'message' => 'Invalid license key, key was not found.', 'error_code' => SLM_Error_Codes::LICENSE_INVALID]; + SLM_API_Utility::output_api_response($args); + } + + // Check if the license is blocked or expired + if ($retLic->lic_status === 'blocked') { + do_action('slm_api_listener_slm_activate_key_blocked', $fields['lic_key']); + $args = ['result' => 'error', 'message' => 'Your license key is blocked', 'error_code' => SLM_Error_Codes::LICENSE_BLOCKED]; + SLM_API_Utility::output_api_response($args); + } + + if ($retLic->lic_status === 'expired') { + do_action('slm_api_listener_slm_activate_key_expired', $fields['lic_key']); + $args = ['result' => 'error', 'message' => 'Your license key has expired', 'error_code' => SLM_Error_Codes::LICENSE_EXPIRED]; + SLM_API_Utility::output_api_response($args); + } + + // Handling registered domains + if (!empty($registered_domain)) { + $sql_prep2 = $wpdb->prepare("SELECT * FROM $reg_domain_table WHERE lic_key = %s", $fields['lic_key']); + $reg_domains = $wpdb->get_results($sql_prep2, OBJECT); + + if (count($reg_domains) < intval($retLic->max_allowed_domains)) { + foreach ($reg_domains as $reg_domain) { + + // Handle domain migration + if (!empty($_REQUEST['migrate_from']) && $reg_domain->registered_domain === sanitize_text_field($_REQUEST['migrate_from'])) { + $wpdb->update($reg_domain_table, ['registered_domain' => $registered_domain], ['registered_domain' => sanitize_text_field($_REQUEST['migrate_from'])]); + $args = ['result' => 'success', 'message' => 'Registered domain has been updated']; + SLM_API_Utility::output_api_response($args); + } + + // Check if the domain is already in use + if ($reg_domain->registered_domain === $registered_domain) { + $args = [ + 'result' => 'error', + 'icon_url' => SLM_Utility::slm_get_icon_url('1x', 'f-remove.png'), + 'message' => 'License key already in use on ' . $registered_domain, + 'error_code' => SLM_Error_Codes::LICENSE_IN_USE, + 'registered_domain' => $reg_domain->registered_domain, + 'item_reference' => $item_reference + ]; + SLM_API_Utility::output_api_response($args); + } + } + + // Register new domain + // If the registered domain is provided, add it to the fields + $fields['registered_domain'] = $registered_domain; + + // Assuming $retLic->id contains the license key ID, add it to the fields + $fields['lic_key_id'] = $retLic->id; + + // Insert into the registered domain table + $wpdb->insert($reg_domain_table, $fields); + + // Update license status to active + $current_date = wp_date('Y-m-d'); + $wpdb->update($slm_lic_table, ['lic_status' => 'active', 'date_activated' => $current_date], ['id' => $retLic->id]); + + //SLM_Helper_Class::write_log('LOG: ' . $url); + + // Send activation email + $lic_email = SLM_Utility::slm_get_lic_email($fields['lic_key']); + $subject = 'Your license key was activated'; + $message = 'Your license key: ' . $fields['lic_key'] . ' was activated successfully on ' . wp_date("F j, Y, g:i a") . '.'; + SLM_Utility::slm_send_mail($lic_email, $subject, $message, 'success'); + + // Return success response + $args = [ + 'result' => 'success', + 'icon_url' => SLM_Utility::slm_get_icon_url('1x', 'verified.png'), + 'message' => 'License key activated.', + 'registered_domain' => $registered_domain, + 'code' => SLM_Error_Codes::LICENSE_VALID, + 'item_reference' => $item_reference + ]; + SLM_Utility::create_log($fields['lic_key'], 'License key activated for domain ' . $registered_domain); + SLM_API_Utility::output_api_response($args); + } else { + $args = [ + 'result' => 'error', + 'message' => 'Reached maximum allowable domains', + 'error_code' => SLM_Error_Codes::REACHED_MAX_DOMAINS + ]; + SLM_Utility::create_log($fields['lic_key'], 'Reached maximum allowable domains'); + SLM_API_Utility::output_api_response($args); + } + } + + // Handling registered devices + if (!empty($registered_devices)) { + $sql_prep3 = $wpdb->prepare("SELECT * FROM $reg_table_devices WHERE lic_key = %s", $fields['lic_key']); + $reg_devices = $wpdb->get_results($sql_prep3, OBJECT); + + if (count($reg_devices) < intval($retLic->max_allowed_devices)) { + foreach ($reg_devices as $reg_device) { + if (!empty($_REQUEST['migrate_from']) && $reg_device->registered_devices === sanitize_text_field($_REQUEST['migrate_from'])) { + $wpdb->update($reg_table_devices, ['registered_devices' => $registered_devices], ['registered_devices' => sanitize_text_field($_REQUEST['migrate_from'])]); + $args = ['result' => 'success', 'message' => 'Registered device has been updated']; + SLM_API_Utility::output_api_response($args); + } + if ($reg_device->registered_devices === $registered_devices) { + $args = [ + 'result' => 'error', + 'icon_url' => SLM_Utility::slm_get_icon_url('1x', 'f-remove.png'), + 'message' => 'License key already in use on ' . $registered_devices, + 'error_code' => SLM_Error_Codes::LICENSE_IN_USE, + 'device' => $reg_device->registered_devices + ]; + SLM_API_Utility::output_api_response($args); + } + } + + // Register new device + // If the registered device is provided, add it to the fields + $fields['registered_devices'] = $registered_devices; + + // Assuming $retLic->id contains the license key ID, add it to the fields + $fields['lic_key_id'] = $retLic->id; + + // Insert into the registered device table + $wpdb->insert($reg_table_devices, $fields); + + // Update license status + $current_date = wp_date('Y-m-d'); + $wpdb->update($slm_lic_table, ['lic_status' => 'active', 'date_activated' => $current_date], ['id' => $retLic->id]); + + // Send activation email + $lic_email = SLM_Utility::slm_get_lic_email($fields['lic_key']); + $subject = 'Your license key was activated'; + $message = 'Your license key: ' . $fields['lic_key'] . ' was activated successfully on ' . wp_date("F j, Y, g:i a") . '.'; + SLM_Utility::slm_send_mail($lic_email, $subject, $message, 'success'); + + // Return success response + $args = [ + 'result' => 'success', + 'registered_device' => $registered_devices, + 'code' => SLM_Error_Codes::LICENSE_ACTIVATED, + 'icon_url' => SLM_Utility::slm_get_icon_url('1x', 'verified.png'), + 'message' => 'License key activated for device.', + ]; + SLM_Utility::create_log($fields['lic_key'], 'License key activated for device ' . $registered_devices); + SLM_API_Utility::output_api_response($args); + } else { + $args = [ + 'result' => 'error', + 'icon_url' => SLM_Utility::slm_get_icon_url('1x', 'f-remove.png'), + 'message' => 'Reached maximum allowable devices for this license. Please upgrade.', + 'error_code' => SLM_Error_Codes::REACHED_MAX_DEVICES + ]; + SLM_Utility::create_log($fields['lic_key'], 'Reached maximum allowable devices'); + SLM_API_Utility::output_api_response($args); + } + } + } + } + + function deactivation_api_listener() + { + if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) === 'slm_deactivate') { + global $slm_debug_logger, $wpdb; + + // Verify the secret key for security + SLM_API_Utility::verify_secret_key(); + $slm_debug_logger->log_debug("API - license deactivation (slm_deactivate) request received."); + + // Trigger deactivation hook for other integrations + do_action('slm_api_listener_slm_deactivate'); + + // Sanitize inputs + $license_key = sanitize_text_field($_REQUEST['license_key']); + $registered_domain = isset($_REQUEST['registered_domain']) ? sanitize_text_field($_REQUEST['registered_domain']) : ''; + $registered_devices = isset($_REQUEST['registered_devices']) ? sanitize_text_field($_REQUEST['registered_devices']) : ''; + + + // Handle domain deactivation if domain info is provided + if (!empty($registered_domain)) { + $registered_dom_table = SLM_TBL_LIC_DOMAIN; + + // Prepare SQL query for domain deactivation + $sql_prep = $wpdb->prepare("DELETE FROM $registered_dom_table WHERE lic_key = %s AND registered_domain = %s", $license_key, $registered_domain); + $delete = $wpdb->query($sql_prep); + + // Check result of the deletion query + if ($delete === false) { + $slm_debug_logger->log_debug("Error - failed to delete the registered domain from the database."); + $args = ['result' => 'error', 'message' => 'Failed to delete the registered domain.', 'error_code' => SLM_Error_Codes::DOMAIN_MISSING]; + SLM_API_Utility::output_api_response($args); + } elseif ($delete === 0) { + $args = [ + 'result' => 'error', + 'message' => 'The license key on this domain is already inactive', + 'error_code' => SLM_Error_Codes::DOMAIN_ALREADY_INACTIVE, + ]; + SLM_Utility::create_log($license_key, 'Domain license deactivation failed - already inactive.'); + SLM_API_Utility::output_api_response($args); + } else { + // Successful deactivation of the domain + $args = [ + 'result' => 'success', + 'message' => 'The license key has been deactivated for this domain.', + 'error_code' => SLM_Error_Codes::KEY_DEACTIVATE_DOMAIN_SUCCESS, + ]; + SLM_Utility::create_log($license_key, 'Domain license deactivated successfully.'); + SLM_API_Utility::output_api_response($args); + } + } + + // Handle device deactivation if device info is provided + if (!empty($registered_devices)) { + $registered_device_table = SLM_TBL_LIC_DEVICES; + + // Prepare SQL query for device deactivation + $sql_prep2 = $wpdb->prepare("DELETE FROM $registered_device_table WHERE lic_key = %s AND registered_devices = %s", $license_key, $registered_devices); + $delete2 = $wpdb->query($sql_prep2); + + // Check result of the deletion query + if ($delete2 === false) { + $slm_debug_logger->log_debug("Error - failed to delete the registered device from the database."); + $args = ['result' => 'error', 'message' => 'Failed to delete the registered device.', 'error_code' => SLM_Error_Codes::DOMAIN_MISSING]; + SLM_API_Utility::output_api_response($args); + } elseif ($delete2 === 0) { + $args = [ + 'result' => 'error', + 'message' => 'The license key on this device is already inactive', + 'error_code' => SLM_Error_Codes::DOMAIN_ALREADY_INACTIVE, + ]; + SLM_Utility::create_log($license_key, 'Device license deactivation failed - already inactive.'); + SLM_API_Utility::output_api_response($args); + } else { + // Successful deactivation of the device + $args = [ + 'result' => 'success', + 'message' => 'The license key has been deactivated for this device.', + 'error_code' => SLM_Error_Codes::KEY_DEACTIVATE_SUCCESS, + ]; + SLM_Utility::create_log($license_key, 'Device license deactivated successfully.'); + SLM_API_Utility::output_api_response($args); + } + } + + // If neither domain nor device info is provided, return an error response + if (empty($registered_domain) && empty($registered_devices)) { + $args = ['result' => 'error', 'message' => 'No deactivation target specified. Either a domain or device must be provided.', 'error_code' => SLM_Error_Codes::DOMAIN_MISSING]; + SLM_API_Utility::output_api_response($args); + } + } + } + + function update_api_listener() + { + if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) === 'slm_update') { + global $slm_debug_logger, $wpdb; + + // Verify secret key for security + SLM_API_Utility::verify_secret_key_for_creation(); + $slm_debug_logger->log_debug("API - license update (slm_update) request received."); + + // Trigger update hook for integrations + do_action('slm_api_listener_slm_update'); + + // Sanitize inputs and build the update fields array + $fields = array( + 'license_key' => sanitize_text_field($_REQUEST['license_key']), + 'date_expiry' => isset($_REQUEST['date_expiry']) ? sanitize_text_field($_REQUEST['date_expiry']) : '', + 'product_ref' => isset($_REQUEST['product_ref']) ? sanitize_text_field($_REQUEST['product_ref']) : '', + 'max_allowed_devices' => isset($_REQUEST['max_allowed_devices']) ? sanitize_text_field($_REQUEST['max_allowed_devices']) : '', + 'max_allowed_domains' => isset($_REQUEST['max_allowed_domains']) ? sanitize_text_field($_REQUEST['max_allowed_domains']) : '', + 'txn_id' => isset($_REQUEST['txn_id']) ? sanitize_text_field($_REQUEST['txn_id']) : '', + 'lic_type' => isset($_REQUEST['lic_type']) ? sanitize_text_field($_REQUEST['lic_type']) : 'subscription', + 'lic_status' => isset($_REQUEST['lic_status']) ? sanitize_text_field($_REQUEST['lic_status']) : 'active', + 'item_reference' => isset($_REQUEST['item_reference']) ? sanitize_text_field($_REQUEST['item_reference']) : '', + ); + + // Validate that the license key is provided + if (empty($fields['license_key'])) { + $args = array( + 'result' => 'error', + 'message' => 'Cannot update license, license key not provided.', + 'error_code' => SLM_Error_Codes::MISSING_KEY_UPDATE_FAILED + ); + $slm_debug_logger->log_debug("API - License update failed: Missing license key."); + SLM_API_Utility::output_api_response($args); + return; + } + + // Update the license in the database + $slm_lic_table = SLM_TBL_LICENSE_KEYS; + $where_clause = array('license_key' => $fields['license_key']); + $update_result = $wpdb->update($slm_lic_table, $fields, $where_clause); + + // Handle update result + if ($update_result === false) { + $args = array( + 'result' => 'error', + 'message' => 'License update failed.', + 'error_code' => SLM_Error_Codes::KEY_UPDATE_FAILED + ); + SLM_Utility::create_log($fields['license_key'], 'License update failed'); + SLM_API_Utility::output_api_response($args); + } else { + $args = array( + 'result' => 'success', + 'message' => 'License successfully updated.', + 'key' => $fields['license_key'], + 'error_code' => SLM_Error_Codes::KEY_UPDATE_SUCCESS + ); + SLM_Utility::create_log($fields['license_key'], 'License successfully updated'); + SLM_API_Utility::output_api_response($args); + } + } else { + // Handle missing or incorrect action parameter + $args = array( + 'result' => 'error', + 'message' => 'Cannot update license, license key not found or invalid action.', + 'error_code' => SLM_Error_Codes::MISSING_KEY_UPDATE_FAILED + ); + SLM_Utility::create_log($_REQUEST['license_key'], 'License update failed: action parameter incorrect or missing.'); + SLM_API_Utility::output_api_response($args); + } + } + + function check_api_listener() + { + if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) === 'slm_check') { + global $slm_debug_logger, $wpdb; + + // Verify secret key for security + SLM_API_Utility::verify_secret_key(); + + $slm_debug_logger->log_debug("API - license check (slm_check) request received."); + + // Sanitize input + $license_key = sanitize_text_field($_REQUEST['license_key']); + $slm_debug_logger->log_debug("Checking license key: " . $license_key); + + // Action hook for additional integrations + do_action('slm_api_listener_slm_check'); + + // Query license key details + $slm_lic_table = SLM_TBL_LICENSE_KEYS; + $reg_domain_table = SLM_TBL_LIC_DOMAIN; + $reg_table_devices = SLM_TBL_LIC_DEVICES; + + // Retrieve the license key details from the database + $license_query = $wpdb->prepare("SELECT * FROM $slm_lic_table WHERE license_key = %s", $license_key); + $retLic = $wpdb->get_row($license_query, OBJECT); + + if ($retLic) { + // If the license exists, retrieve domain and device information + $domain_query = $wpdb->prepare("SELECT * FROM $reg_domain_table WHERE lic_key = %s", $license_key); + $device_query = $wpdb->prepare("SELECT * FROM $reg_table_devices WHERE lic_key = %s", $license_key); + + $registered_domains = $wpdb->get_results($domain_query, OBJECT); + $registered_devices = $wpdb->get_results($device_query, OBJECT); + + // Prepare response with license and registration data + $response_args = apply_filters('slm_check_response_args', array( + 'result' => 'success', + 'code' => SLM_Error_Codes::LICENSE_EXIST, + 'message' => 'License key details retrieved.', + 'status' => $retLic->lic_status, + 'subscr_id' => $retLic->subscr_id, + 'first_name' => $retLic->first_name, + 'last_name' => $retLic->last_name, + 'company_name' => $retLic->company_name, + 'email' => $retLic->email, + 'license_key' => $retLic->license_key, + 'lic_type' => $retLic->lic_type, + 'max_allowed_domains' => $retLic->max_allowed_domains, + 'max_allowed_devices' => $retLic->max_allowed_devices, + 'item_reference' => $retLic->item_reference, + 'registered_domains' => $registered_domains, + 'registered_devices' => $registered_devices, + 'date_created' => $retLic->date_created, + 'date_renewed' => $retLic->date_renewed, + 'date_expiry' => $retLic->date_expiry, + 'product_ref' => $retLic->product_ref, + 'txn_id' => $retLic->txn_id, + 'until' => $retLic->until, + 'current_ver' => $retLic->current_ver, + )); + + // Log and send the response + SLM_Utility::create_log($license_key, 'License check successful'); + SLM_API_Utility::output_api_response($response_args); + } else { + // Invalid license key case + $error_args = array( + 'result' => 'error', + 'message' => 'Invalid license key', + 'error_code' => SLM_Error_Codes::LICENSE_INVALID + ); + + // Log the error and respond + SLM_Utility::create_log($license_key, 'License check failed: Invalid license key'); + SLM_API_Utility::output_api_response($error_args); + } + } + } + + function check_api_info() + { + if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) === 'slm_info') { + global $slm_debug_logger, $wpdb; + + // Verify secret key for security + SLM_API_Utility::verify_secret_key(); + + // Log the API request + $slm_debug_logger->log_debug("API - license info (slm_info) request received."); + + // Sanitize input data + $license_key = sanitize_text_field($_REQUEST['license_key']); + $slm_debug_logger->log_debug("License key: " . $license_key); + + // Action hook for additional integrations + do_action('slm_api_listener_slm_info'); + + // Fetch license details from the database + $slm_lic_table = SLM_TBL_LICENSE_KEYS; + $reg_domain_table = SLM_TBL_LIC_DOMAIN; + $reg_table_devices = SLM_TBL_LIC_DEVICES; + + $license_query = $wpdb->prepare("SELECT * FROM $slm_lic_table WHERE license_key = %s", $license_key); + $retLic = $wpdb->get_row($license_query, OBJECT); + + if ($retLic) { + // If the license exists, fetch associated domains and devices + $domain_query = $wpdb->prepare("SELECT * FROM $reg_domain_table WHERE lic_key = %s", $license_key); + $device_query = $wpdb->prepare("SELECT * FROM $reg_table_devices WHERE lic_key = %s", $license_key); + + $registered_domains = $wpdb->get_results($domain_query, OBJECT); + $registered_devices = $wpdb->get_results($device_query, OBJECT); + + // Prepare the response with the license and registration data + $response_args = apply_filters('slm_info_response_args', array( + 'result' => 'success', + 'message' => 'License key details retrieved.', + 'code' => SLM_Error_Codes::LICENSE_EXIST, + 'status' => $retLic->lic_status, + 'subscr_id' => $retLic->subscr_id, + 'first_name' => $retLic->first_name, + 'last_name' => $retLic->last_name, + 'company_name' => $retLic->company_name, + 'email' => $retLic->email, + 'license_key' => $retLic->license_key, + 'lic_type' => $retLic->lic_type, + 'max_allowed_domains' => $retLic->max_allowed_domains, + 'item_reference' => $retLic->item_reference, + 'max_allowed_devices' => $retLic->max_allowed_devices, + 'date_created' => $retLic->date_created, + 'date_renewed' => $retLic->date_renewed, + 'date_expiry' => $retLic->date_expiry, + 'product_ref' => $retLic->product_ref, + 'txn_id' => $retLic->txn_id, + 'until' => $retLic->until, + 'current_ver' => $retLic->current_ver, + )); + + // Log the successful check + SLM_Utility::create_log($license_key, 'info: valid license key'); + SLM_API_Utility::output_api_response($response_args); + } else { + // Handle invalid license case + $error_args = array( + 'result' => 'error', + 'message' => 'Invalid license key', + 'error_code' => SLM_Error_Codes::LICENSE_INVALID + ); + + // Log the error + SLM_Utility::create_log($license_key, 'info: invalid license key'); + SLM_API_Utility::output_api_response($error_args); + } + } + } + + function wc_slm_handle_license_renewal($order_id, $license_key) + { + // Log the renewal action for debugging + SLM_Helper_Class::write_log("Processing license renewal for Order ID: $order_id, License Key: $license_key"); + + // Retrieve the license data + $license_data = SLM_Utility::get_licence_by_key($license_key); + if (!$license_data) { + SLM_Helper_Class::write_log("License key $license_key not found."); + return; // Stop if the license is not found + } + + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Calculate the new expiration date + $renewal_period = intval($license_data['slm_billing_length']); + $renewal_term = sanitize_text_field($license_data['slm_billing_interval']); + $new_expiry_date = date('Y-m-d', strtotime('+' . $renewal_period . ' ' . $renewal_term)); + + // Update the license expiry date in the database + $update_result = $wpdb->update( + $license_table, + ['date_expiry' => $new_expiry_date, 'lic_status' => 'active'], + ['license_key' => $license_key] + ); + + if ($update_result !== false) { + SLM_Helper_Class::write_log("License $license_key renewed successfully. New expiry date: $new_expiry_date."); + } else { + SLM_Helper_Class::write_log("Failed to renew license $license_key."); + } + + // Add order note for renewal confirmation + $order = wc_get_order($order_id); + if ($order) { + $order->add_order_note(sprintf( + __('License Key %s renewed. New expiry date: %s', 'slm-plus'), + $license_key, + $new_expiry_date + )); + $order->save(); + } + + // Optional: Notify the user about the renewal + wc_slm_notify_user_about_renewal($order, $license_key); + } + + + function renew_api_listener() { + if (isset($_POST['slm_action']) && trim($_POST['slm_action']) === 'renew_license') { + global $wpdb; + + // Verify the secret key for security + SLM_API_Utility::verify_secret_key(); + + // Get and sanitize the license key and WooCommerce order ID + $license_key = sanitize_text_field($_POST['license_key']); + $wc_order_id = intval($_POST['wc_order_id']); + + // Check if license key and order ID are provided + if (empty($license_key) || empty($wc_order_id)) { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'License key or WooCommerce order ID is missing.', + 'error_code' => SLM_Error_Codes::MISSING_PARAMETERS, + 'status_code' => 400, + ]); + return; + } + + // Fetch the WooCommerce order + $order = wc_get_order($wc_order_id); + if (!$order) { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'Invalid WooCommerce order ID.', + 'error_code' => SLM_Error_Codes::ORDER_NOT_FOUND, + 'status_code' => 404, + ]); + return; + } + + // Fetch license details from the database + $license = SLM_Utility::get_license_by_key($license_key); + if (!$license) { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'License not found.', + 'error_code' => SLM_Error_Codes::LICENSE_INVALID, + 'status_code' => 404, + ]); + return; + } + + // Verify the order ID matches the one associated with the license (if applicable) + if (!empty($license->wc_order_id) && $license->wc_order_id != $wc_order_id) { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'The provided order ID does not match the one associated with this license.', + 'error_code' => SLM_Error_Codes::ORDER_ID_MISMATCH, + 'status_code' => 400, + ]); + return; + } + + // Check the WooCommerce order status + if ($order->get_status() !== 'completed') { + SLM_API_Utility::output_api_response([ + 'result' => 'error', + 'message' => 'The WooCommerce order has not been completed.', + 'error_code' => SLM_Error_Codes::ORDER_NOT_COMPLETED, + 'status_code' => 400, + ]); + return; + } + + // Handle license renewal process + $this->handle_license_renewal($wc_order_id, $license_key); + + // Return a success response + SLM_API_Utility::output_api_response([ + 'result' => 'success', + 'message' => 'License renewed successfully.', + 'license_key' => $license_key, + 'renewal_date' => current_time('mysql'), + 'status_code' => 200, + ]); + } + } + + private function handle_license_renewal($order_id, $license_key) { + // Log the renewal action for debugging + SLM_Helper_Class::write_log("Processing license renewal for Order ID: $order_id, License Key: $license_key"); + + // Retrieve the license data + $license_data = SLM_Utility::get_license_by_key($license_key); + if (!$license_data) { + SLM_Helper_Class::write_log("License key $license_key not found."); + return; // Stop if the license is not found + } + + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Calculate the new expiration date + $renewal_period = intval($license_data->slm_billing_length); + $renewal_term = sanitize_text_field($license_data->slm_billing_interval); + $new_expiry_date = date('Y-m-d', strtotime('+' . $renewal_period . ' ' . $renewal_term)); + + // Update the license expiry date in the database + $update_result = $wpdb->update( + $license_table, + ['date_expiry' => $new_expiry_date, 'lic_status' => 'active'], + ['license_key' => $license_key] + ); + + if ($update_result !== false) { + SLM_Helper_Class::write_log("License $license_key renewed successfully. New expiry date: $new_expiry_date."); + } else { + SLM_Helper_Class::write_log("Failed to renew license $license_key."); + } + + // Add order note for renewal confirmation + $order = wc_get_order($order_id); + if ($order) { + $order->add_order_note(sprintf( + __('License Key %s renewed. New expiry date: %s', 'slm-plus'), + $license_key, + $new_expiry_date + )); + $order->save(); + } + + // Notify the user about the renewal + wc_slm_notify_user_about_renewal($order, $license_key); + } +} diff --git a/includes/slm-blocks.php b/includes/slm-blocks.php new file mode 100644 index 0000000..5216ec1 --- /dev/null +++ b/includes/slm-blocks.php @@ -0,0 +1,25 @@ +log_debug("Some debug message"); + * + * OR + * + * - SLM_Debug_Logger::log_debug_st("Some debug message"); + */ + +class SLM_Debug_Logger { + var $log_folder_path; + var $default_log_file = 'log.txt'; + var $default_log_file_cron = 'log-cron-job.txt'; + var $debug_enabled = false; + var $debug_status = array('SUCCESS', 'STATUS', 'NOTICE', 'WARNING', 'FAILURE', 'CRITICAL'); + var $section_break_marker = "\n----------------------------------------------------------\n\n"; + var $log_reset_marker = "-------- Log File Reset --------\n"; + + /** + * Constructor + * Initializes the logger by setting the log folder path and enabling debugging based on plugin settings. + */ + function __construct() { + $this->log_folder_path = SLM_PATH . '/logs'; + + // Check plugin options to see if debugging is enabled + $options = get_option('slm_plugin_options'); + if (!empty($options['enable_debug'])) { + $this->debug_enabled = true; + } + } + + /** + * Get the current timestamp for the log entry + * + * @return string Timestamp in the format [m/d/Y g:i A] + */ + function get_debug_timestamp() { + return '[' . wp_date('m/d/Y g:i A') . '] - '; + } + + /** + * Get the debug status label based on the level + * + * @param int $level The severity level of the log message (0-5) + * @return string The corresponding status label (e.g., 'SUCCESS', 'FAILURE') + */ + function get_debug_status($level) { + $size = count($this->debug_status); + return ($level >= $size) ? 'UNKNOWN' : $this->debug_status[$level]; + } + + /** + * Return a section break marker if required + * + * @param bool $section_break Whether to include a section break + * @return string Section break marker or empty string + */ + function get_section_break($section_break) { + return $section_break ? $this->section_break_marker : ""; + } + + /** + * Reset the log file by clearing its contents and adding a reset marker + * + * @param string $file_name Optional file name to reset, defaults to main log file + */ + function reset_log_file($file_name = '') { + if (empty($file_name)) { + $file_name = $this->default_log_file; + } + $debug_log_file = $this->log_folder_path . '/' . $file_name; + $content = $this->get_debug_timestamp() . $this->log_reset_marker; + $fp = fopen($debug_log_file, 'w'); + fwrite($fp, $content); + fclose($fp); + } + + /** + * Append content to the specified log file + * + * @param string $content The content to log + * @param string $file_name The file to which content will be appended + */ + function append_to_file($content, $file_name) { + if (empty($file_name)) $file_name = $this->default_log_file; + $debug_log_file = $this->log_folder_path . '/' . $file_name; + $fp = fopen($debug_log_file, 'a'); + fwrite($fp, $content); + fclose($fp); + } + + /** + * Log a debug message + * + * @param string $message The debug message to log + * @param int $level The debug level (default 0 - SUCCESS) + * @param bool $section_break Whether to include a section break after the message + * @param string $file_name Optional log file name + */ + function log_debug($message, $level = 0, $section_break = false, $file_name = '') { + if (!$this->debug_enabled) return; + + // Build the log content + $content = $this->get_debug_timestamp(); // Add timestamp + $content .= $this->get_debug_status($level); // Add status level + $content .= ' : ' . $message . "\n"; // Add message + $content .= $this->get_section_break($section_break); // Add section break if needed + + $this->append_to_file($content, $file_name); // Append to log file + } + + /** + * Log a debug message specifically for cron jobs + * + * @param string $message The debug message to log for cron jobs + * @param int $level The debug level (default 0 - SUCCESS) + * @param bool $section_break Whether to include a section break after the message + */ + function log_debug_cron($message, $level = 0, $section_break = false) { + if (!$this->debug_enabled) return; + + // Build the log content for cron jobs + $content = $this->get_debug_timestamp(); + $content .= $this->get_debug_status($level); + $content .= ' : ' . $message . "\n"; + $content .= $this->get_section_break($section_break); + + // Log to the cron-specific log file + $this->append_to_file($content, $this->default_log_file_cron); + } + + /** + * Static method to log debug messages from a static context + * + * @param string $message The debug message to log + * @param int $level The debug level (default 0 - SUCCESS) + * @param bool $section_break Whether to include a section break after the message + * @param string $file_name Optional log file name + */ + static function log_debug_st($message, $level = 0, $section_break = false, $file_name = '') { + $options = get_option('slm_plugin_options'); + if (empty($options['enable_debug'])) { + return; // Debugging is disabled + } + + // Build the log content + $content = '[' . wp_date('m/d/Y g:i A') . '] - STATUS : ' . $message . "\n"; + $debug_log_file = SLM_PUBLIC . '/logs/log.txt'; + + // Append the log content to the file + $fp = fopen($debug_log_file, 'a'); + fwrite($fp, $content); + fclose($fp); + } +} diff --git a/includes/slm-error-codes.php b/includes/slm-error-codes.php new file mode 100755 index 0000000..5af3210 --- /dev/null +++ b/includes/slm-error-codes.php @@ -0,0 +1,67 @@ +load_scripts(); + // Add other init time operations here + add_action('slm_daily_cron_event', array($this, 'slm_daily_cron_event_handler')); +} + +// Load common and admin-specific scripts and styles +public function load_scripts() { + $plugin_version = '1.0.0'; // Replace with your plugin version if available + + wp_enqueue_script('jquery'); // Common scripts + + if (is_admin()) { + wp_enqueue_script('jquery-ui-datepicker'); + // Enqueue admin-specific JS and add version and load in footer + wp_enqueue_script('wplm-custom-admin-js', SLM_ASSETS_URL . 'js/wplm-custom-admin.js', array('jquery-ui-dialog'), $plugin_version, true); // true loads in footer + if (isset($_GET['page']) && $_GET['page'] == 'slm_manage_license') { // Only include if in license management interface + wp_enqueue_style('jquery-ui-style', SLM_ASSETS_URL . 'css/jquery-ui.css', array(), $plugin_version); // Add version for caching + } + } +} + +// Daily cron event handler +public function slm_daily_cron_event_handler() { + $options = get_option('slm_plugin_options'); + do_action('slm_daily_cron_event_triggered'); + + if (isset($options['enable_auto_key_expiry']) && $options['enable_auto_key_expiry'] == '1') { + // Perform auto key expiry task + SLM_Debug_Logger::log_debug_st("SLM daily cronjob - auto expiry of license key is enabled."); + SLM_Utility::do_auto_key_expiry(); + } +} +} diff --git a/includes/slm-plugin-core.php b/includes/slm-plugin-core.php new file mode 100755 index 0000000..b0d36f1 --- /dev/null +++ b/includes/slm-plugin-core.php @@ -0,0 +1,264 @@ +activate(); +} + +function deactivate_slm_plus() +{ + require_once SLM_LIB . 'class-slm-deactivator.php'; + $slm_deactivator->deactivate(); +} + +register_activation_hook(__FILE__, 'activate_slm_plus'); +register_deactivation_hook(__FILE__, 'deactivate_slm_plus'); + +// Improved and Shortened License Key Generator Function +function slm_get_license($lic_key_prefix = '') +{ + global $wpdb; + $max_retries = 5; // Set the maximum number of retries + $retry_count = 0; + $license_key = ''; + + // Use the constant to define the license table + $license_table = SLM_TBL_LICENSE_KEYS; + + // Generate a unique license key + while ($retry_count < $max_retries) { + // Generate a strong, random base using random_int() and uniqid + $random_base = uniqid(random_int(1000, 9999), true); + + // Combine random base with the current GMT date and time and the Unix timestamp for additional uniqueness + $combined_string = $random_base . gmdate('Y-m-d H:i:s') . time(); + + // Create a sha256 hash of the combined string + $hashed_string = substr(hash('sha256', $combined_string), 0, 32); // Take first 32 characters of the sha256 hash + + // Ensure the prefix is added correctly + $license_key = strtoupper($hashed_string); + + // Add dashes every 4 characters + $license_key_with_dashes = implode('-', str_split($license_key, 4)); + + // Add the prefix to the formatted key + $license_key_with_prefix = $lic_key_prefix . $license_key_with_dashes; + + // Check if the generated license key already exists in the database + $existing_license = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $license_table WHERE license_key = %s", $license_key_with_prefix)); + + // If the license doesn't exist, break out of the loop and return the key + if ($existing_license == 0) { + return $license_key_with_prefix; + } + + // If the license already exists, increment the retry count and try again + $retry_count++; + } + + // If we exceed the retry limit, return false or handle the error as needed + error_log('Failed to generate a unique license key after ' . $max_retries . ' attempts.'); + return false; +} + + +// Example hyphenate function (assuming hyphenates every 4 characters) +function hyphenate($string) +{ + return implode('-', str_split($string, 4)); // This splits the string into chunks of 4 characters and adds hyphens +} + +// Action hooks +add_action('init', 'slmplus_init_handler'); +add_action('plugins_loaded', 'slmplus_plugins_loaded_handler'); +add_action('wp_ajax_del_registered_devices', 'slmplus_del_registered_devices'); +add_action('wp_ajax_del_registered_domain', 'slmplus_del_registered_domain'); +add_action('wp_ajax_del_activation', 'slmplus_remove_activation'); +// Initialize plugin on plugins_loaded action +add_action('plugins_loaded', array('SLM_Tabbed_Plugin', 'get_object')); + +// Initialize debug logger +$slm_debug_logger = new SLM_Debug_Logger(); + +// Init-time tasks +function slmplus_init_handler() +{ + $init_task = new SLM_Init_Time_Tasks(); + $api_listener = new SLM_API_Listener(); +} + +// Plugins loaded tasks +function slmplus_plugins_loaded_handler() +{ + if (is_admin() && get_option('slm_db_version') != SLM_DB_VERSION) { + require_once SLM_LIB . 'class-slm-installer.php'; + // TODO - Implement DB update logic here + } +} + +// Singleton pattern for the plugin +class SLM_Tabbed_Plugin +{ + private static $classobj = NULL; + + public static function get_object() + { + if (self::$classobj === NULL) { + self::$classobj = new self(); + } + return self::$classobj; + } + + private function __construct() {} +} + +// AJAX handlers +function slmplus_del_registered_domain() +{ + global $wpdb; + $id = strip_tags($_GET['id']); + $ret = $wpdb->query($wpdb->prepare("DELETE FROM " . SLM_TBL_LIC_DOMAIN . " WHERE id = %d", $id)); + echo ($ret) ? 'success' : 'failed'; + exit; +} + +function slmplus_del_registered_devices() +{ + global $wpdb; + $id = strip_tags($_GET['id']); + $ret = $wpdb->query($wpdb->prepare("DELETE FROM " . SLM_TBL_LIC_DEVICES . " WHERE id = %d", $id)); + echo ($ret) ? 'success' : 'failed'; + exit; +} + +function slmplus_remove_activation() +{ + global $wpdb; + $id = strip_tags($_GET['id']); + $activation_type = strip_tags($_GET['activation_type']); + + $table = ($activation_type == 'Devices') ? SLM_TBL_LIC_DEVICES : SLM_TBL_LIC_DOMAIN; + $ret = $wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE id = %d", $id)); + echo ($ret) ? 'success' : 'failed'; + exit; +} + +// Debugging functions +function wc_print_pretty($args) +{ + echo '
    ';
    +    print_r($args);
    +    echo '
    '; +} + +function wc_log($msg) +{ + $log = ABSPATH . DIRECTORY_SEPARATOR . 'slm_log.txt'; + file_put_contents($log, $msg . '', FILE_APPEND); +} + +// WooCommerce integration +if (SLM_Helper_Class::slm_get_option('slm_woo') == 1 && is_plugin_active('woocommerce/woocommerce.php')) { + require_once SLM_WOO . 'includes/wc_licenses_class.php'; + require_once SLM_WOO . 'includes/slm-meta-boxes.php'; + require_once SLM_WOO . 'includes/register-template.php'; + require_once SLM_WOO . 'includes/purchase.php'; + require_once SLM_WOO . 'includes/create-license-orders.php'; + require_once SLM_WOO . 'includes/hooks/license-checkout-hooks.php'; + // Build WooCommerce tabs + SLM_Utility::slm_woo_build_tab(); +} + +add_action('wp_ajax_slm_user_search', 'slm_user_search'); +function slm_user_search() +{ + global $wpdb; + + $value = sanitize_text_field($_POST['value']); + + // Direct SQL Query to improve performance + $query = $wpdb->prepare( + " + SELECT u.ID, u.display_name, u.user_email, + IFNULL(um_first_name.meta_value, um_billing_first_name.meta_value) AS first_name, + IFNULL(um_last_name.meta_value, um_billing_last_name.meta_value) AS last_name + FROM {$wpdb->users} u + LEFT JOIN {$wpdb->prefix}usermeta um_first_name ON um_first_name.user_id = u.ID AND um_first_name.meta_key = 'first_name' + LEFT JOIN {$wpdb->prefix}usermeta um_last_name ON um_last_name.user_id = u.ID AND um_last_name.meta_key = 'last_name' + LEFT JOIN {$wpdb->prefix}usermeta um_billing_first_name ON um_billing_first_name.user_id = u.ID AND um_billing_first_name.meta_key = 'billing_first_name' + LEFT JOIN {$wpdb->prefix}usermeta um_billing_last_name ON um_billing_last_name.user_id = u.ID AND um_billing_last_name.meta_key = 'billing_last_name' + WHERE (um_first_name.meta_value LIKE %s OR um_last_name.meta_value LIKE %s OR um_billing_first_name.meta_value LIKE %s OR um_billing_last_name.meta_value LIKE %s) + LIMIT 10 + ", + '%' . $wpdb->esc_like($value) . '%', + '%' . $wpdb->esc_like($value) . '%', + '%' . $wpdb->esc_like($value) . '%', + '%' . $wpdb->esc_like($value) . '%' + ); + + $users = $wpdb->get_results($query); + $results = []; + + foreach ($users as $user) { + $results[] = [ + 'ID' => $user->ID, + 'display_name' => $user->display_name ?: "{$user->first_name} {$user->last_name}", + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'email' => $user->user_email, + 'company_name' => get_user_meta($user->ID, 'company_name', true), + 'subscr_id' => $user->ID, // Pass user ID as the subscription ID + ]; + } + + wp_send_json_success($results); +} diff --git a/includes/slm-scripts.php b/includes/slm-scripts.php new file mode 100644 index 0000000..7d0ec56 --- /dev/null +++ b/includes/slm-scripts.php @@ -0,0 +1,13 @@ +options} WHERE option_name LIKE '_transient_$like_pattern' OR option_name LIKE '_transient_timeout_$like_pattern'"; + $wpdb->query($sql); + + add_action('admin_notices', function () { + echo '

    All rate-limiting transients have been cleared.

    '; + }); + } +}); + + +// Custom Hooks +// Hooks added to customize the text or logic: + +// slm_invalid_email_message: Customize the invalid email message. +// slm_success_message: Customize the success message. +// slm_no_license_message: Customize the "no license found" message. +// slm_license_email_message: Modify the email message body. +// slm_license_email_subject: Modify the email subject. +// slm_form_label: Change the form label text. +// slm_form_button_text: Change the form button text. + +class SLM_Forgot_License { + /** + * Initialize the class and hooks. + */ + public function __construct() { + // Register shortcode. + add_shortcode('slm_forgot_license', [$this, 'render_shortcode']); + } + + /** + * Render the shortcode. + * + * @return string + */ + public function render_shortcode() { + // Check if form is submitted. + if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['slm_forgot_license_nonce'])) { + return $this->handle_form_submission(); + } + + // Output the form. + return $this->render_form(); + } + + /** + * Handle form submission. + * + * @return string + */ + protected function handle_form_submission() { + // Verify nonce for security. + if (!isset($_POST['slm_forgot_license_nonce']) || + !wp_verify_nonce($_POST['slm_forgot_license_nonce'], 'slm_forgot_license_action')) { + return '

    Invalid request. Please try again.

    '; + } + + // Sanitize and validate email. + $email = sanitize_email($_POST['slm_forgot_license_email']); + if (!is_email($email)) { + return apply_filters('slm_invalid_email_message', '

    Invalid email address.

    '); + } + + // Rate limiting: prevent repeated submissions from the same IP. + $ip = $_SERVER['REMOTE_ADDR']; + if ($this->is_rate_limited($ip)) { + return '

    You are submitting requests too quickly. Please try again later.

    '; + } + + // Retrieve licenses. + $licenses = SLM_Utility::get_licenses_by_email($email); + + if (!empty($licenses)) { + // Prepare and send the email. + $message = apply_filters('slm_license_email_message', $this->generate_email_message($licenses), $licenses); + wp_mail($email, apply_filters('slm_license_email_subject', 'Your Licenses'), $message); + + return apply_filters('slm_success_message', '

    Your licenses have been sent to your email.

    '); + } + + return apply_filters('slm_no_license_message', '

    No licenses found for the provided email.

    '); + } + + /** + * Check if the IP is rate-limited. + * + * @param string $ip + * @return bool + */ + protected function is_rate_limited($ip) { + // Allow administrators to bypass rate limiting and clear transients. + if (is_user_logged_in() && current_user_can('manage_options')) { + $this->clear_transients(); + return false; // Admins are not rate-limited. + } + + $key = 'slm_rate_limit_' . $ip; + $limit = 3; // Max submissions allowed per hour. + $time_frame = HOUR_IN_SECONDS; + + $attempts = get_transient($key); + + if ($attempts === false) { + set_transient($key, 1, $time_frame); + return false; + } + + if ($attempts >= $limit) { + return true; + } + + set_transient($key, $attempts + 1, $time_frame); + return false; + } + + /** + * Clear all relevant transients. + */ + protected function clear_transients() { + global $wpdb; + + // Search and delete all transients related to rate limiting. + $like_pattern = '%slm_rate_limit_%'; + $sql = "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_$like_pattern' OR option_name LIKE '_transient_timeout_$like_pattern'"; + $wpdb->query($sql); + } + + + + + /** + * Generate the email message. + * + * @param array $licenses + * @return string + */ + protected function generate_email_message($licenses) { + $message = "Here are your licenses:\n\n"; + + foreach ($licenses as $license) { + $message .= "License Key: {$license['license_key']}\n"; + // $message .= "Product: {$license['product_ref']}\n"; + $message .= "Status: {$license['lic_status']}\n\n"; + } + + return $message; + } + + /** + * Render the form HTML. + * + * @return string + */ + protected function render_form() { + ob_start(); + ?> +
    + + + + +
    + ' . __('You must be logged in to view your licenses.', 'slm-plus') . '

    '; + } + + // Get the current user's email. + $current_user = wp_get_current_user(); + $email = $current_user->user_email; + + // Retrieve licenses for the user. + $licenses = $this->get_licenses_by_email($email); + + // Render the licenses table or a message if none are found. + if (empty($licenses)) { + return apply_filters('slm_no_license_message', '

    ' . __('No licenses found for your account.', 'slm-plus') . '

    '); + } + + return $this->render_licenses_table($licenses); + } + + /** + * Retrieve licenses by email from the database. + * + * @param string $email + * @return array + */ + protected function get_licenses_by_email($email) { + global $wpdb; + + $table_name = $wpdb->prefix . 'lic_key_tbl'; // Update to match your actual table name. + $query = $wpdb->prepare( + "SELECT license_key, product_ref, lic_status, date_expiry, date_activated, max_allowed_domains, max_allowed_devices + FROM $table_name WHERE email = %s", + $email + ); + + return $wpdb->get_results($query, ARRAY_A); + } + + /** + * Render the licenses table HTML. + * + * @param array $licenses + * @return string + */ + protected function render_licenses_table($licenses) { + ob_start(); + ?> + + + + + + + + + + + + + + + + + + + + + + + + + +
    + get_error_messages(), true)); + } +} +add_action('wp_mail_failed', 'action_wp_mail_failed', 10, 1); + + +class SLM_Helper_Class +{ + + public static function slm_get_option($option) + { + $slm_opts = get_option('slm_plugin_options'); + if (is_array($slm_opts) && array_key_exists($option, $slm_opts)) { + return $slm_opts[$option]; + } + return ''; + } + + public static function write_log($log) + { + if (defined('WP_DEBUG') && WP_DEBUG === true) { + if (is_array($log) || is_object($log)) { + error_log(print_r($log, true)); + } else { + error_log($log); + } + } + } + public static function get_license_logs($license_key) + { + global $wpdb; + $table_name = SLM_TBL_LIC_LOG; + + // Use a prepared statement for security + $query = $wpdb->prepare( + "SELECT * FROM $table_name WHERE license_key = %s ORDER BY time DESC", + $license_key + ); + + // Fetch results as an associative array + return $wpdb->get_results($query, ARRAY_A); + } + + /** + * PHP Logger + */ + + static function console($data) + { + $output = $data; + if (is_array($output)) + $output = implode(',', $output); + + // print the result into the JavaScript console + echo ""; + } +} + +$slm_helper = new SLM_Helper_Class(); + + +class SLM_API_Utility +{ + + /* + * The args array can contain the following: + * result (success or error) + * message (a message describing the outcome of the action + */ + + public static function output_api_response($args) + { + // Log to debug file (if enabled) + global $slm_debug_logger; + if (isset($slm_debug_logger)) { + $slm_debug_logger->log_debug('API Response - Result: ' . esc_html($args['result']) . ' Message: ' . esc_html($args['message'])); + } + + // Send response + $args = apply_filters('slm_ap_response_args', $args); + $args = apply_filters('slm_api_response_args', $args); + + header('Content-Type: application/json'); + echo json_encode($args); + exit; + } + + /** + * Validate date format to ensure it's in 'YYYY-MM-DD' format. + * Returns the sanitized date or an empty string if invalid. + */ + public static function slm_validate_date($date) + { + $date = sanitize_text_field($date); + $timestamp = strtotime($date); + if ($timestamp && date('Y-m-d', $timestamp) === $date) { + return $date; + } + return ''; // Return an empty string if the date is invalid + } + + public static function verify_secret_key() + { + $slm_options = get_option('slm_plugin_options'); + $right_secret_key = $slm_options['lic_verification_secret'] ?? ''; + $received_secret_key = sanitize_text_field($_REQUEST['secret_key'] ?? ''); + $slm_action = sanitize_text_field($_REQUEST['slm_action'] ?? ''); + + // Case-sensitive comparison for the secret keys + if ($received_secret_key !== $right_secret_key) { + // Prepare the error response with case-sensitivity note + $args = array( + 'result' => 'error', + 'message' => 'Verification API secret key is invalid. Note: The key is case-sensitive.', + 'slm_action' => $slm_action, + 'received_secret_key' => $received_secret_key, + 'error_code' => SLM_Error_Codes::VERIFY_KEY_INVALID + ); + // Output the API response with the error + self::output_api_response($args); + SLM_Helper_Class::write_log('Verification API secret key is invalid. Note: The key is case-sensitive. ' . $slm_action); + } + } + + public static function get_slm_option($option) + { + // Retrieve the option value from the database + $slm_options_func = get_option('slm_plugin_options', []); + + // Check if the option exists; if not, return an empty string + if (!isset($slm_options_func[$option])) { + return ''; + } + + // Get the option value and unslash it (removes slashes from the option value) + $option_value = wp_unslash($slm_options_func[$option]); + + // Sanitize the option value (text field sanitization) + $sanitized_option = sanitize_text_field($option_value); + + // Return the sanitized and unslashed option value + return $sanitized_option; + } + + + public static function verify_secret_key_for_creation() + { + // Get the stored secret key from plugin options + $slm_options = get_option('slm_plugin_options'); + $right_secret_key = $slm_options['lic_creation_secret'] ?? ''; + + // Sanitize and retrieve the received secret key + $received_secret_key = sanitize_text_field($_REQUEST['secret_key'] ?? ''); + + // Case-sensitive comparison for the secret keys + if ($received_secret_key !== $right_secret_key) { + // Prepare the error response with case-sensitivity note + $args = array( + 'result' => 'error', + 'message' => 'Invalid License Creation API Secret Key provided. Note: The key comparison is case-sensitive.', + 'error_code' => SLM_Error_Codes::CREATE_KEY_INVALID + ); + // Output the API response with the error + self::output_api_response($args); + } + } + + public static function insert_license_data_internal($fields) + { + global $wpdb; + $slm_lic_table = SLM_TBL_LICENSE_KEYS; + $fields = array_filter($fields); // Remove any null values. + + $wpdb->insert($slm_lic_table, $fields); + } +} + + +class SLM_Utility +{ + public static function get_licenses_by_email($email) { + global $wpdb; + + // Query the licenses table for entries matching the email. + $table_name = SLM_TBL_LICENSE_KEYS; // Adjust table name if needed. + $query = $wpdb->prepare( + "SELECT license_key, product_ref, lic_status FROM $table_name WHERE email = %s", + $email + ); + + return $wpdb->get_results($query, ARRAY_A); + } + + /** + * Saves a backup of the plugin's database tables in a secure folder. + */ + public static function renew_license($license_key, $order_id) { + global $wpdb; + $wpdb->update(SLM_TBL_LICENSE_KEYS, [ + 'wc_order_id' => $order_id, + 'payment_status' => 'pending' + ], ['license_key' => $license_key]); + } + + public static function slm_save_backup_to_uploads() + { + global $wpdb; + + // Get the upload directory + $upload_dir = wp_upload_dir(); + $unique_hash = slm_get_unique_hash(); // Generate or retrieve the unique hash + $slm_backup_dir = $upload_dir['basedir'] . $unique_hash; + + // Create the slm-plus folder with hash if it doesn't exist + if (!file_exists($slm_backup_dir)) { + wp_mkdir_p($slm_backup_dir); + } + + // Set backup file name and path + $backup_file = $slm_backup_dir . '/slm_plugin_backup_' . gmdate('Y-m-d H:i:s') . '.sql'; + + // Get plugin tables + $backup_tables = [ + SLM_TBL_LICENSE_KEYS, + SLM_TBL_LIC_DOMAIN, + SLM_TBL_LIC_DEVICES, + SLM_TBL_LIC_LOG, + SLM_TBL_EMAILS, + SLM_TBL_LICENSE_STATUS + ]; + + $sql = ""; + foreach ($backup_tables as $table) { + // Get table structure + $create_table_query = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N); + $sql .= "\n\n" . $create_table_query[0][1] . ";\n\n"; + + // Get table data + $rows = $wpdb->get_results("SELECT * FROM $table", ARRAY_A); + foreach ($rows as $row) { + $values = array_map('esc_sql', array_values($row)); // Use esc_sql to escape the values + $values = "'" . implode("','", $values) . "'"; + $sql .= "INSERT INTO $table VALUES ($values);\n"; + } + } + + // Include the WordPress Filesystem API + if (! function_exists('request_filesystem_credentials')) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + + // Ensure the filesystem is ready + if (! WP_Filesystem()) { + request_filesystem_credentials(admin_url()); + } + + global $wp_filesystem; + + // Define the backup file path + $backup_path = $upload_dir['basedir'] . '/' . $unique_hash . '/' . basename($backup_file); + + // Create the backup directory if it doesn't exist + if (! is_dir(dirname($backup_path))) { + $wp_filesystem->mkdir(dirname($backup_path)); + } + + // Save the SQL to the backup file using the WP Filesystem + if ($wp_filesystem->put_contents($backup_path, $sql)) { + $backup_url = $upload_dir['baseurl'] . '/' . $unique_hash . '/' . basename($backup_file); + + // Save backup info in plugin options + $backup_info = [ + 'url' => $backup_url, + 'date' => gmdate('Y-m-d H:i:s'), + ]; + slm_update_option('slm_last_backup_info', $backup_info); + + echo '

    ' . esc_html__('Backup created successfully! Download from: ', 'slm-plus') . '' . esc_html(basename($backup_file)) . '

    '; + } else { + echo '

    ' . esc_html__('Error: Failed to create the backup file.', 'slm-plus') . '

    '; + } + } + + // Function to export a single license as a JSON file + public static function export_license_to_json($license_id_or_key) + { + global $wpdb; + + // Fetch the custom directory path from options (saved with hash) + $slm_options = get_option('slm_plugin_options'); + $custom_dir_hash = isset($slm_options['slm_backup_dir_hash']) ? $slm_options['slm_backup_dir_hash'] : ''; + + // Get the WordPress upload directory + $upload_dir = wp_upload_dir(); + $custom_dir = $upload_dir['basedir'] . $custom_dir_hash; + + // Ensure the directory exists + if (!file_exists($custom_dir)) { + wp_mkdir_p($custom_dir); // Create the directory if it doesn't exist + } + + // Check if the input is a license ID or license key and fetch the license data accordingly + if (is_numeric($license_id_or_key)) { + // Fetch license by ID + $data = $wpdb->get_row($wpdb->prepare("SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE id = %d", $license_id_or_key), ARRAY_A); + } else { + // Fetch license by key + $data = $wpdb->get_row($wpdb->prepare("SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE license_key = %s", $license_id_or_key), ARRAY_A); + } + + if ($data) { + $license_key = $data['license_key']; + + // Prepare the file name as "license_key.json" + $file_name = sanitize_file_name($license_key) . '.json'; + $file_path = $custom_dir . '/' . $file_name; + + // Encode the license data to JSON format + $json_data = wp_json_encode($data, JSON_PRETTY_PRINT); + + // Save the JSON data to a file in the custom directory + if (file_put_contents($file_path, $json_data)) { + $file_url = $upload_dir['baseurl'] . $custom_dir_hash . '/' . $file_name; + + // Return the file URL for download + return $file_url; + } else { + return false; // Return false if the file couldn't be saved + } + } + + return false; // Return false if no data was found + } + + public static function check_for_expired_lic($lic_key = '') + { + global $wpdb; + + // Set up email headers and subject line + $headers = array('Content-Type: text/html; charset=UTF-8'); + $subject = get_bloginfo('name') . ' - Your license has expired'; + $expiration_reminder_text = SLM_Helper_Class::slm_get_option('expiration_reminder_text'); + $expired_licenses_list = []; + $reinstated_licenses_list = []; + + // Query licenses marked as expired but with future expiration dates to correct their status + $incorrectly_expired_query = $wpdb->prepare( + "SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE lic_status = %s AND date_expiry > NOW()", + 'expired' + ); + $incorrectly_expired_licenses = $wpdb->get_results($incorrectly_expired_query, ARRAY_A); + + // Reinstate incorrectly expired licenses + foreach ($incorrectly_expired_licenses as $license) { + $license_key = sanitize_text_field($license['license_key']); + $id = intval($license['id']); + + // Update license status to 'active' + $wpdb->update( + SLM_TBL_LICENSE_KEYS, + ['lic_status' => 'active'], + ['id' => $id] + ); + + self::create_log($license_key, 'status corrected to active'); + $reinstated_licenses_list[] = $license_key; + } + + // Log reinstated licenses + if (!empty($reinstated_licenses_list)) { + SLM_Helper_Class::write_log('Reinstated licenses set to active: ' . implode(', ', $reinstated_licenses_list)); + } + + // Query expired licenses + $expired_query = $wpdb->prepare( + "SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE date_expiry < NOW() AND date_expiry != %s ORDER BY date_expiry ASC;", + '00000000' + ); + $expired_licenses = $wpdb->get_results($expired_query, ARRAY_A); + + // Check if any expired licenses were found + if (empty($expired_licenses)) { + SLM_Helper_Class::write_log('No expired licenses found'); + return []; // Return an empty array if no licenses found + } + + // Process each expired license + foreach ($expired_licenses as $license) { + $id = intval($license['id']); + $license_key = sanitize_text_field($license['license_key']); + // $first_name = sanitize_text_field($license['first_name']); + // $last_name = sanitize_text_field($license['last_name']); + $email = sanitize_email($license['email']); + $date_expiry = sanitize_text_field($license['date_expiry']); + + // Include email template and generate the email body + ob_start(); + include SLM_LIB . 'mails/expired.php'; + $body = ob_get_clean(); + + // Check if auto-expiration is enabled and update the license status + if (SLM_Helper_Class::slm_get_option('enable_auto_key_expiration') == 1) { + $update_data = ['lic_status' => 'expired']; + $where_clause = ['id' => $id]; + $wpdb->update(SLM_TBL_LICENSE_KEYS, $update_data, $where_clause); + + // Log and send expiration notification + self::create_log($license_key, 'set to expired'); + $email_result = self::slm_check_sent_emails($license_key, $email, $subject, $body, $headers); + if ($email_result === '200') { + self::create_log($license_key, 'sent expiration email notification'); + } + } + + // Add license to the expired list + $expired_licenses_list[] = $license_key; + } + + // Log the total count of expired licenses + SLM_Helper_Class::write_log('Expired licenses found and processed: ' . implode(', ', $expired_licenses_list)); + + return [ + 'expired_licenses' => $expired_licenses_list, + 'reinstated_licenses' => $reinstated_licenses_list + ]; // Return both expired and reinstated licenses + } + + + // Define return codes for clarity + const EMAIL_SENT_FIRST_TIME = '200'; + const EMAIL_ALREADY_SENT = '400'; + const EMAIL_SENT_RECORD_NOT_FOUND = '300'; + + public static function slm_check_sent_emails($license_key, $email, $subject, $body, $headers) + { + global $wpdb; + + // Check if an email has already been sent for this license key + $query = $wpdb->prepare( + 'SELECT COUNT(*) FROM ' . SLM_TBL_EMAILS . ' WHERE lic_key = %s', + $license_key + ); + $email_already_sent = $wpdb->get_var($query) > 0; + + // If email already sent, return status code without resending + if ($email_already_sent) { + return self::EMAIL_ALREADY_SENT; + } + + // Send the email if it hasn't been sent before + $mail_sent = wp_mail($email, $subject, $body, $headers); + + // Log the email status + if ($mail_sent) { + self::create_email_log($license_key, $email, 'success', 'yes', current_time('mysql')); + return self::EMAIL_SENT_FIRST_TIME; + } else { + self::create_email_log($license_key, $email, 'failure', 'no', current_time('mysql')); + return self::EMAIL_SENT_RECORD_NOT_FOUND; + } + } + + + public static function do_auto_key_expiry() + { + global $wpdb; + $current_date = current_time('Y-m-d'); + $slm_lic_table = SLM_TBL_LICENSE_KEYS; + + // Query for active (non-expired) licenses + $licenses = $wpdb->get_results( + $wpdb->prepare("SELECT * FROM $slm_lic_table WHERE lic_status != %s", 'expired'), + OBJECT + ); + + // Log and return if no licenses are found + if (empty($licenses)) { + SLM_Debug_Logger::log_debug_st("do_auto_key_expiry() - No active license keys found."); + return false; + } + + $today_dt = new DateTime($current_date); + + foreach ($licenses as $license) { + $license_key = sanitize_text_field($license->license_key); + $expiry_date = sanitize_text_field($license->date_expiry); + + // Skip if expiration date is invalid or empty + if (empty($expiry_date) || in_array($expiry_date, ['0000-00-00', '00000000'])) { + SLM_Debug_Logger::log_debug_st("License key ($license_key) has no valid expiration date set. Skipping expiry check."); + continue; + } + + // Check if the license has expired + $expire_dt = new DateTime($expiry_date); + if ($today_dt > $expire_dt) { + // Update license status to 'expired' + $data = ['lic_status' => 'expired']; + $where = ['id' => intval($license->id)]; + $updated = $wpdb->update($slm_lic_table, $data, $where); + + // Log the expiry and trigger action if successfully updated + if ($updated) { + SLM_Debug_Logger::log_debug_st("License key ($license_key) expired on $expiry_date. Status set to 'expired'."); + do_action('slm_license_key_expired', $license->id); + + // Optional: Send expiry reminder email + self::check_for_expired_lic($license_key); + } else { + SLM_Debug_Logger::log_debug_st("Failed to update status for expired license key ($license_key)."); + } + } + } + + return true; + } + + + + public static function get_user_info($by, $value) + { + // Sanitize the input parameters + $by = sanitize_key($by); + $value = sanitize_text_field($value); + + // Get the user by specified criteria + $user = get_user_by($by, $value); + return $user; + } + + public static function get_days_remaining($date1) + { + // Validate and sanitize the date input + $date1 = sanitize_text_field($date1); + + // Retrieve the date format setting from WordPress settings + $date_format = get_option('date_format'); + + try { + // Create DateTime objects for future and current dates + $future_date = new DateTime($date1); + $current_date = new DateTime(); + + // Check if the future date is valid and in the future + if ($future_date < $current_date) { + return __('0 days remaining', 'slm-plus'); + } + + // Calculate the difference in days + $interval = $current_date->diff($future_date); + $days_remaining = (int) $interval->days; + + // Format and return the result + return sprintf( + // Translators: %1$s is the number of days remaining, %2$s is the formatted future date + __('%1$s days remaining until %2$s', 'slm-plus'), + $days_remaining, + date_i18n($date_format, $future_date->getTimestamp()) + ); + } catch (Exception $e) { + // Return 0 days remaining if date parsing fails + return __('0 days remaining', 'slm-plus'); + } + } + + + public static function delete_license_key_by_row_id($key_row_id) + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Sanitize the input + $key_row_id = intval($key_row_id); + + // Retrieve the license key associated with this row id + $license_key = $wpdb->get_var($wpdb->prepare("SELECT license_key FROM $license_table WHERE id = %d", $key_row_id)); + + // Debug: Log the retrieved license key + SLM_Helper_Class::write_log("License key retrieved: " . $license_key); + + // First, delete the registered domains entry of this key (if any) + SLM_Utility::delete_registered_domains_of_key($key_row_id); + SLM_Helper_Class::write_log("Registered domains for key $license_key deleted."); + + // Now, delete the key from the licenses table + $wpdb->delete($license_table, array('id' => $key_row_id)); + SLM_Helper_Class::write_log("License with row ID $key_row_id deleted from the license table."); + + if ($license_key) { + + // Query to get WooCommerce orders using a custom WP_Query with meta_query + $args = array( + 'post_type' => 'shop_order', + 'posts_per_page' => -1, + 'meta_query' => array( + 'relation' => 'OR', + array( + 'key' => 'License Key', // License Key meta field + 'value' => $license_key, + 'compare' => '=' + ), + array( + 'key' => '_slm_lic_key', // Fallback License Key meta field + 'value' => $license_key, + 'compare' => '=' + ) + ) + ); + + $order_query = new WP_Query($args); + + if ($order_query->have_posts()) { + while ($order_query->have_posts()) { + $order_query->the_post(); + $order_id = get_the_ID(); + + // Get order object + $order = wc_get_order($order_id); + + // Debugging: Log the order ID + SLM_Helper_Class::write_log("Processing order ID: " . $order_id); + + // Meta keys to be removed + $meta_keys = [ + 'License Key', + 'License Type', + 'Current Version', + 'Until Version', + 'Max Devices', + 'Max Domains' + ]; + + // Remove order-level metadata + foreach ($meta_keys as $meta_key) { + $meta_value = $order->get_meta($meta_key, true); // Retrieve the metadata value + if ($meta_value) { + SLM_Helper_Class::write_log("Found meta key $meta_key with value: $meta_value. Deleting..."); + $order->delete_meta_data($meta_key); // Remove meta data from the order + } + } + + // Add a note to the order + $note_content = sprintf(__('License key %s was deleted on %s', 'slm-plus'), $license_key, date_i18n('F j, Y')); + $order->add_order_note($note_content); + + // Process and reset license-related metadata from order items + foreach ($order->get_items() as $item_id => $item) { + // Remove item-level metadata for the specified keys + foreach ($meta_keys as $meta_key) { + $meta_value = $item->get_meta($meta_key, true); // Retrieve the metadata value + if ($meta_value) { + SLM_Helper_Class::write_log("Found meta key $meta_key in order item $item_id with value: $meta_value. Deleting..."); + $item->delete_meta_data($meta_key); // Remove meta data from the order item + } + } + + // Save the updated order item + $item->save(); + } + + // Save the updated order + $order->save(); + } + + wp_reset_postdata(); // Reset the post data after custom query + } else { + // Debugging: Log if no orders were found + SLM_Helper_Class::write_log("No orders found for the license key: " . $license_key); + } + } + } + + /** + * Get license key by WooCommerce Order ID. + * + * @param int $order_id WooCommerce order ID. + * @return string|null License key associated with the order ID, or null if not found. + */ + public static function slm_get_license_by_order_id($order_id) { + global $wpdb; + $lic_key_table = SLM_TBL_LICENSE_KEYS; + + // Query to fetch the license key by order ID + $query = $wpdb->prepare("SELECT license_key FROM $lic_key_table WHERE wc_order_id = %d", $order_id); + $license_key = $wpdb->get_var($query); + + return $license_key ? sanitize_text_field($license_key) : null; + } + + + + /** + * Get associated orders for a license. + * + * @param mixed $identifier The license key (string) or license ID (integer). + * @return array|null List of associated orders or null if none found. + */ + public static function slm_get_associated_orders($identifier) { + global $wpdb; + $lic_key_table = SLM_TBL_LICENSE_KEYS; + + // Ensure identifier is valid + if (empty($identifier)) { + SLM_Helper_Class::write_log('Invalid identifier passed to slm_get_associated_orders: ' . print_r($identifier, true)); + return []; + } + + // Prepare the query based on identifier type + if (is_numeric($identifier)) { + $query = $wpdb->prepare("SELECT associated_orders FROM $lic_key_table WHERE id = %d", $identifier); + } else { + $query = $wpdb->prepare("SELECT associated_orders FROM $lic_key_table WHERE license_key = %s", $identifier); + } + + // Log the query + SLM_Helper_Class::write_log('SQL Query: ' . $query); + + // Execute the query + $result = $wpdb->get_var($query); + + // Debug the raw result + SLM_Helper_Class::write_log('Raw associated_orders value: ' . print_r($result, true)); + + // Process the result if not empty + if (!empty($result)) { + $orders = maybe_unserialize($result); + SLM_Helper_Class::write_log('Unserialized associated_orders value: ' . print_r($orders, true)); + + if (is_array($orders)) { + return array_values(array_unique(array_map('intval', $orders))); + } + } + + // Return empty array if no valid data found + return []; + } + + + + + /** + * Add an order to the associated orders of a license. + * + * @param mixed $identifier The license key (string) or license ID (integer). + * @param int $order_id The WooCommerce order ID to associate with the license. + * @return bool True if the operation was successful, false otherwise. + */ + public static function slm_add_associated_order($identifier, $order_id) { + global $wpdb; + $lic_key_table = SLM_TBL_LICENSE_KEYS; + + // Validate $order_id + if (!is_numeric($order_id) || $order_id <= 0) { + error_log("SLM: Invalid order ID provided: $order_id"); + return false; + } + + // Fetch current license data + if (is_numeric($identifier)) { + // Identifier is a license ID + $license_data = $wpdb->get_row( + $wpdb->prepare("SELECT associated_orders, wc_order_id FROM $lic_key_table WHERE id = %d", $identifier), + ARRAY_A + ); + } else { + // Identifier is a license key + $license_data = $wpdb->get_row( + $wpdb->prepare("SELECT associated_orders, wc_order_id FROM $lic_key_table WHERE license_key = %s", $identifier), + ARRAY_A + ); + } + + if (!$license_data) { + error_log("SLM: License not found for identifier: $identifier"); + return false; + } + + // Extract current associated orders and wc_order_id + $associated_orders = maybe_unserialize($license_data['associated_orders']); + $current_wc_order_id = $license_data['wc_order_id']; + + // Ensure $associated_orders is a valid array + if (!is_array($associated_orders)) { + $associated_orders = []; + } + + // Add the old wc_order_id to the associated_orders array if it's valid + if (!empty($current_wc_order_id) && !in_array($current_wc_order_id, $associated_orders, true)) { + $associated_orders[] = $current_wc_order_id; + } + + // Add the new order_id to the associated_orders array if not already present + if (!in_array($order_id, $associated_orders, true)) { + $associated_orders[] = $order_id; + } + + // Serialize the updated orders for storage + $updated_orders = maybe_serialize($associated_orders); + + // Prepare the query to update the database + if (is_numeric($identifier)) { + // Update based on license ID + $query = $wpdb->prepare( + "UPDATE $lic_key_table SET associated_orders = %s, wc_order_id = %d WHERE id = %d", + $updated_orders, + $order_id, + $identifier + ); + } else { + // Update based on license key + $query = $wpdb->prepare( + "UPDATE $lic_key_table SET associated_orders = %s, wc_order_id = %d WHERE license_key = %s", + $updated_orders, + $order_id, + $identifier + ); + } + + // Execute the query + $result = $wpdb->query($query); + + // Handle and log errors + if ($result === false) { + error_log("SLM: Failed to update associated orders for identifier: $identifier. Error: " . $wpdb->last_error); + return false; + } + + // Log success + error_log("SLM: Successfully updated associated orders for identifier: $identifier with Order ID: $order_id"); + return true; + } + + + + /* + * Retrieves the email associated with a license key + */ + public static function slm_get_lic_email($license) + { + global $wpdb; + $lic_key_table = SLM_TBL_LICENSE_KEYS; + + // Sanitize the input + $license = sanitize_text_field($license); + + // Prepare and execute the query to fetch the email + $email = $wpdb->get_var( + $wpdb->prepare("SELECT email FROM $lic_key_table WHERE license_key = %s", $license) + ); + + // Check if an email was found and is valid + if ($email && is_email($email)) { + return $email; + } else { + // Return a WP_Error if the email was not found or invalid + return new WP_Error('license_not_found', __('License key not found or invalid email.', 'slm-plus')); + } + } + + + /* + * Sends an email with the specified parameters + */ + public static function slm_send_mail($to, $subject, $message, $bgcolor) + { + // Sanitize inputs + $to = sanitize_email($to); + $subject = sanitize_text_field($subject); + $message = sanitize_textarea_field($message); + + // Prepare headers + $headers = array( + 'From: ' . get_bloginfo('name') . ' <' . get_bloginfo('admin_email') . '>', + 'Content-Type: text/html; charset=UTF-8' + ); + + // Prepare the email body + $body = self::slm_email_template($message, $bgcolor); + + // Send the email + wp_mail($to, $subject, $body, $headers); + } + + + public static function slm_email_template($message, $bgcolor = '') + { + switch ($bgcolor) { + case 'success': + $color = '#eceff0'; + break; + case 'error': + $color = '#e23b2f'; + break; + default: + $color = '#eceff0'; + break; + } + + $template = ' + + + + ' . esc_html(get_bloginfo('name')) . ' + + +
    +
    + + + + + + +
    + + + + + + + +
    +
    +
    + +
    +

    License key was activated successfully!

    +

    ' . wp_kses_post($message) . '

    +

    Regards,

    +
    +

    + ' . esc_html(get_bloginfo('name')) . ' +
    + ' . esc_html(get_bloginfo('admin_email')) . ' +

    +
    +
    +
    +
    + + +
    +
    +
    +
    + + '; + + return $template; + } + + public static function count_licenses($status) + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Sanitize input + $status = sanitize_text_field($status); + + // Prepare the SQL statement + $query = $wpdb->prepare("SELECT COUNT(*) FROM $license_table WHERE lic_status = %s", $status); + $get_lic_status = $wpdb->get_var($query); + + return $get_lic_status; + } + + public static function slm_get_icon_url($size, $filename) + { + // Sanitize inputs + $size = sanitize_text_field($size); + $filename = sanitize_file_name($filename); + + return SLM_ASSETS_URL . 'icons/' . $size . '/' . $filename; + } + + public static function count_logrequest() + { + global $wpdb; + $license_table = SLM_TBL_LIC_LOG; + + $getlogs = $wpdb->get_var("SELECT COUNT(*) FROM $license_table"); + return $getlogs; + } + + public static function count_emailsent() + { + global $wpdb; + $license_table = SLM_TBL_EMAILS; + + $getlogs = $wpdb->get_var("SELECT COUNT(*) FROM $license_table"); + return $getlogs; + } + + public static function getstats_licenses($date_created, $interval) + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Sanitize inputs + $date_created = sanitize_text_field($date_created); + $interval = intval($interval); + + $query = $wpdb->prepare( + "SELECT COUNT(*) FROM $license_table WHERE $date_created >= DATE_ADD(CURDATE(), INTERVAL -%d DAY)", + $interval + ); + + return $wpdb->get_var($query); + } + + public static function get_total_licenses() + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + $license_count = $wpdb->get_var("SELECT COUNT(*) FROM $license_table"); + return $license_count; + } + + public static function get_lic_expiringsoon() + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + $license_count = $wpdb->get_var( + "SELECT COUNT(*) FROM $license_table WHERE date_expiry BETWEEN DATE_SUB(CURDATE(), INTERVAL 1 MONTH) AND CURDATE()" + ); + + return $license_count; + } + + public static function block_license_key_by_row_id($key_row_id) + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Sanitize input + $key_row_id = intval($key_row_id); + + $wpdb->update($license_table, array('lic_status' => 'blocked'), array('id' => $key_row_id)); + } + + public static function expire_license_key_by_row_id($key_row_id) + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + + // Sanitize input + $key_row_id = intval($key_row_id); + + $wpdb->update($license_table, array('lic_status' => 'expired'), array('id' => $key_row_id)); + } + + public static function active_license_key_by_row_id($key_row_id) + { + global $wpdb; + $license_table = SLM_TBL_LICENSE_KEYS; + $current_date = current_time('Y-m-d'); + + // Sanitize input + $key_row_id = intval($key_row_id); + + $wpdb->update($license_table, array('lic_status' => 'active'), array('id' => $key_row_id)); + $wpdb->update($license_table, array('date_activated' => $current_date), array('id' => $key_row_id)); + } + + /* + * Deletes any registered domains and related entries for the given license key's row id. + */ + static function delete_registered_domains_of_key($key_row_id) + { + global $slm_debug_logger; + global $wpdb; + + // Table constants + $reg_domain_table = SLM_TBL_LIC_DOMAIN; + $device_table = SLM_TBL_LIC_DEVICES; + $log_table = SLM_TBL_LIC_LOG; + $email_table = SLM_TBL_EMAILS; + + // Retrieve the license key associated with this row id + $license_key = $wpdb->get_var($wpdb->prepare("SELECT license_key FROM " . SLM_TBL_LICENSE_KEYS . " WHERE id = %d", $key_row_id)); + + if ($license_key) { + // Step 1: Delete from registered domains table + $reg_domains = $wpdb->get_results($wpdb->prepare("SELECT id FROM $reg_domain_table WHERE lic_key_id = %d", $key_row_id)); + foreach ($reg_domains as $domain) { + $wpdb->delete($reg_domain_table, array('id' => $domain->id)); + $slm_debug_logger->log_debug("Registered domain with row id (" . $domain->id . ") deleted."); + } + + // Step 2: Delete from devices table + $deleted_devices = $wpdb->delete($device_table, array('lic_key' => $license_key), array('%s')); + $slm_debug_logger->log_debug("$deleted_devices entries deleted from devices table for license key ($license_key)."); + + // Step 3: Delete from log table + $deleted_logs = $wpdb->delete($log_table, array('license_key' => $license_key), array('%s')); + $slm_debug_logger->log_debug("$deleted_logs entries deleted from log table for license key ($license_key)."); + + // Step 4: Delete from emails table + $deleted_emails = $wpdb->delete($email_table, array('lic_key' => $license_key), array('%s')); + $slm_debug_logger->log_debug("$deleted_emails entries deleted from emails table for license key ($license_key)."); + } else { + $slm_debug_logger->log_debug("No license key found for row id ($key_row_id). Deletion aborted."); + } + } + + + static function create_secret_keys() + { + // Generate secure random bytes (32 bytes = 256 bits) + $random_bytes = openssl_random_pseudo_bytes(32); // 32 bytes (256 bits) + + // Convert the random bytes into a hexadecimal string (64 chars) + $random_string = bin2hex($random_bytes); + + // Make the entire string uppercase + $key = strtoupper($random_string); + + return $key; + } + + public static function create_log($license_key, $action) + { + global $wpdb; + $slm_log_table = SLM_TBL_LIC_LOG; + + // Sanitize inputs + $license_key = sanitize_text_field($license_key); + $action = sanitize_text_field($action); + + // Determine the request origin + $origin = ''; + if (!empty($_SERVER['HTTP_ORIGIN'])) { + $origin = sanitize_text_field($_SERVER['HTTP_ORIGIN']); + } elseif (!empty($_SERVER['HTTP_REFERER'])) { + $origin = sanitize_text_field($_SERVER['HTTP_REFERER']); + } elseif (!empty($_SERVER['REMOTE_ADDR'])) { + $origin = sanitize_text_field($_SERVER['REMOTE_ADDR']); + } + + // Get current date and time + $current_date_time = current_time('mysql'); // Returns 'Y-m-d H:i:s' in WordPress timezone + $current_time_only = date('H:i:s', strtotime($current_date_time)); // Extract time portion + + // Prepare log data + $log_data = [ + 'license_key' => $license_key, + 'slm_action' => $action, + 'time' => $current_date_time, // Combined date and time + 'time_only' => $current_time_only, // Time only + 'source' => $origin, + ]; + + // Insert log data into the database + $inserted = $wpdb->insert($slm_log_table, $log_data); + + // Check for insertion errors + if ($inserted === false) { + error_log("Failed to insert log for license key: $license_key, action: $action. Error: " . $wpdb->last_error); + } else { + error_log("Log inserted successfully for license key: $license_key, action: $action."); + } + } + + + + + + public static function create_email_log($lic_key, $sent_to, $status, $sent, $date_sent = null) + { + global $wpdb; + $slm_email_table = SLM_TBL_EMAILS; + + // Sanitize inputs + $lic_key = sanitize_text_field($lic_key); + $sent_to = sanitize_email($sent_to); + $status = sanitize_text_field($status); + $sent = sanitize_text_field($sent); + $date_sent = $date_sent ? sanitize_text_field($date_sent) : current_time('mysql'); + + // Prepare log data + $log_data = array( + 'lic_key' => $lic_key, + 'sent_to' => $sent_to, + 'status' => $status, + 'sent' => $sent, + 'date_sent' => $date_sent, + ); + + // Insert log data into the database + $inserted = $wpdb->insert($slm_email_table, $log_data); + + // Check for insertion success and log accordingly + if ($inserted !== false) { + SLM_Helper_Class::write_log("Email log created for license key: $lic_key"); + } else { + error_log("Failed to create email log for license key: $lic_key. Error: " . $wpdb->last_error); + } + } + + static function slm_wp_dashboards_stats($amount) + { + global $wpdb; + $slm_log_table = SLM_TBL_LICENSE_KEYS; + + $result = $wpdb->get_results(" SELECT * FROM $slm_log_table ORDER BY id DESC LIMIT $amount"); + + foreach ($result as $license) { + echo ' + + ' . esc_html($license->first_name) . ' ' . esc_html($license->last_name) . '
    + ' . esc_html($license->license_key) . ' + + '; + } + } + + static function slm_get_licinfo($api_action, $license_key) + { + $api_url = get_site_url() . '/?secret_key=' . SLM_Helper_Class::slm_get_option('lic_verification_secret') . '&slm_action=' . $api_action . '&license_key=' . $license_key; + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => $api_url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => "", + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => "GET", + )); + $response = curl_exec($curl); + curl_close($curl); + $json = json_decode($response); + return $json; + } + + static function get_subscriber_licenses() + { + global $wpdb; + $email = $_GET['email']; + $manage_subscriber = $_GET['manage_subscriber']; + + if (isset($email) && isset($manage_subscriber) && current_user_can('edit_pages')) { + + echo '

    Listing all licenses related to ' . esc_html($email) . '

    '; + + $result_array = $wpdb->get_results( + $wpdb->prepare( + "SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE email LIKE %s ORDER BY `email` DESC LIMIT 0,1000", + '%' . $wpdb->esc_like($email) . '%' + ), + ARRAY_A + ); + + + foreach ($result_array as $slm_user) { + echo ' + ' . esc_html($slm_user["id"]) . ' + ' . esc_html($slm_user["license_key"]) . ' + ' . esc_html($slm_user["lic_status"]) . ' + ' . esc_html__('View', 'slm-plus') . ' + '; + } + } + } + + static function get_lic_activity($license_key) + { + global $wpdb; + $slm_log_table = SLM_TBL_LIC_LOG; + + echo ' +
    + + + + + + + + + '; + $activity = $wpdb->get_results("SELECT * FROM " . $slm_log_table . " WHERE license_key='" . $license_key . "';"); + foreach ($activity as $log) { + echo ' + ' . + '' . + ' + '; + } + echo ' + +
    ' . esc_html__('ID', 'slm-plus') . '' . esc_html__('Request', 'slm-plus') . '
    ' . esc_html($log->id) . ' ' . esc_html($log->slm_action) . '' . + '

    ' . esc_html__('Source:', 'slm-plus') . ' ' . esc_html($log->source) . + '

    ' . esc_html__('Time:', 'slm-plus') . ' ' . esc_html($log->time) . '

    +
    '; + } + + static function get_license_activation($license_key, $tablename, $item_name, $activation_type, $allow_removal = true) + { +?> +
    +
    + prepare("SELECT * FROM $tablename WHERE lic_key = %s", $license_key); + $activations = $wpdb->get_results($sql_prep, OBJECT); + + if (count($activations) > 0) : ?> +
    +
    + + +
    + registered_devices) . '" aria-label="' . esc_attr($activation->registered_devices) . '" aria-describedby="' . esc_attr($activation->registered_devices) . '" value="' . esc_attr($activation->registered_devices) . '" readonly>'; + } else { + echo ''; + } + ?> + +
    + +
    + +
    + + + +
    +
    + + ' . esc_html__('Not registered yet', 'slm-plus') . '
    '; ?> + +
    +is_type('slm_license')) { + $tabs['shipping'] = array( + 'title' => __('License information', 'slm-plus'), + 'priority' => 50, + 'callback' => 'slm_woo_tab_lic_info' + ); + } + return $tabs; + } + + function slm_woo_tab_lic_info() + { + global $product; + // The new tab content + echo '

    ' . esc_html__('License information', 'slm-plus') . '

    '; + echo esc_html__('License type: ', 'slm-plus') . esc_html(get_post_meta($product->get_id(), '_license_type', true)) . '
    '; + echo esc_html__('Domains allowed: ', 'slm-plus') . esc_html(get_post_meta($product->get_id(), '_domain_licenses', true)) . '
    '; + echo esc_html__('Devices allowed: ', 'slm-plus') . esc_html(get_post_meta($product->get_id(), '_devices_licenses', true)) . '
    '; + echo esc_html__('Renews every ', 'slm-plus') . esc_html(get_post_meta($product->get_id(), '_license_renewal_period_lenght', true)) . ' ' . esc_html(get_post_meta($product->get_id(), '_license_renewal_period_term', true)) . '
    '; + } + } +} + diff --git a/includes/slm-wizard.php b/includes/slm-wizard.php new file mode 100644 index 0000000..02d344b --- /dev/null +++ b/includes/slm-wizard.php @@ -0,0 +1,2 @@ + + */ +class WP_Mail +{ + + private $to = array(); + private $cc = array(); + private $bcc = array(); + private $headers = array(); + private $attachments = array(); + private $sendAsHTML = TRUE; + private $subject = ''; + private $from = ''; + + private $headerTemplate = FALSE; + private $headerVariables = array(); + private $template = FALSE; + private $variables = array(); + private $afterTemplate = FALSE; + private $footerVariables = array(); + + + public static function init() + { + return new Self; + } + + + /** + * Set recipients + * @param Array|String $to + * @return Object $this + */ + public function to($to) + { + if (is_array($to)) { + $this->to = $to; + } else { + $this->to = array($to); + } + return $this; + } + + + /** + * Get recipients + * @return Array $to + */ + public function getTo() + { + return $this->to; + } + + + /** + * Set Cc recipients + * @param String|Array $cc + * @return Object $this + */ + public function cc($cc) + { + if (is_array($cc)) { + $this->cc = $cc; + } else { + $this->cc = array($cc); + } + return $this; + } + + + /** + * Get Cc recipients + * @return Array $cc + */ + public function getCc() + { + return $this->cc; + } + + + /** + * Set Email Bcc recipients + * @param String|Array $bcc + * @return Object $this + */ + public function bcc($bcc) + { + if (is_array($bcc)) { + $this->bcc = $bcc; + } else { + $this->bcc = array($bcc); + } + + return $this; + } + + + /** + * Set email Bcc recipients + * @return Array $bcc + */ + public function getBcc() + { + return $this->bcc; + } + + + /** + * Set email Subject + * @param Srting $subject + * @return Object $this + */ + public function subject($subject) + { + $this->subject = $subject; + return $this; + } + + + /** + * Retruns email subject + * @return Array + */ + public function getSubject() + { + return $this->subject; + } + + + /** + * Set From header + * @param String + * @return Object $this + */ + public function from($from) + { + $this->from = $from; + return $this; + } + + /** + * Set the email's headers + * @param String|Array $headers [description] + * @return Object $this + */ + public function headers($headers) + { + if (is_array($headers)) { + $this->headers = $headers; + } else { + $this->headers = array($headers); + } + + return $this; + } + + + /** + * Retruns headers + * @return Array + */ + public function getHeaders() + { + return $this->headers; + } + + + /** + * Returns email content type + * @return String + */ + public function HTMLFilter() + { + return 'text/html'; + } + + + /** + * Set email content type + * @param Bool $html + * @return Object $this + */ + public function sendAsHTML($html) + { + $this->sendAsHTML = $html; + return $this; + } + + + /** + * Attach a file or array of files. + * Filepaths must be absolute. + * @param String|Array $path + * @throws Exception + * @return Object $this + */ + public function attach($path) + { + if (is_array($path)) { + $this->attachments = array(); + foreach ($path as $path_) { + if (!file_exists($path_)) { + throw new Exception(sprintf('Attachment not found at %s', esc_html($path))); + } else { + $this->attachments[] = $path_; + } + } + } else { + if (!file_exists($path)) { + throw new Exception(sprintf('Attachment not found at %s', esc_html($path))); + } + $this->attachments = array($path); + } + + return $this; + } + + + /** + * Set the before-template file + * @param String $template Path to HTML template + * @param Array $variables + * @throws Exception + * @return Object $this + */ + public function templateHeader($template, $variables = NULL) + { + if (!file_exists($template)) { + throw new Exception('Template file not found'); + } + + if (is_array($variables)) { + $this->headerVariables = $variables; + } + + $this->headerTemplate = $template; + return $this; + } + + + /** + * Set the template file + * @param String $template Path to HTML template + * @param Array $variables + * @throws Exception + * @return Object $this + */ + public function template($template, $variables = NULL) + { + if (!file_exists($template)) { + throw new Exception('File not found'); + } + + if (is_array($variables)) { + $this->variables = $variables; + } + + $this->template = $template; + return $this; + } + + + /** + * Set the after-template file + * @param String $template Path to HTML template + * @param Array $variables + * @throws Exception + * @return Object $this + */ + public function templateFooter($template, $variables = NULL) + { + if (!file_exists($template)) { + throw new Exception('Template file not found'); + } + + if (is_array($variables)) { + $this->footerVariables = $variables; + } + + $this->afterTemplate = $template; + return $this; + } + + + /** + * Renders the template + * @return String + */ + public function render() + { + return $this->renderPart('before') . + $this->renderPart('main') . + $this->renderPart('after'); + } + + + /** + * Render a specific part of the email + * @author Anthony Budd + * @param String $part before, after, main + * @return String + */ + public function renderPart($part = 'main') + { + switch ($part) { + case 'before': + $templateFile = $this->headerTemplate; + $variables = $this->headerVariables; + break; + + case 'after': + $templateFile = $this->afterTemplate; + $variables = $this->footerVariables; + break; + + case 'main': + default: + $templateFile = $this->template; + $variables = $this->variables; + break; + } + + if ($templateFile === FALSE) { + return ''; + } + + + $extension = strtolower(pathinfo($templateFile, PATHINFO_EXTENSION)); + if ($extension === 'php') { + + ob_start(); + ob_clean(); + + foreach ($variables as $key => $value) { + $$key = $value; + } + + include $templateFile; + + $html = ob_get_clean(); + + return $html; + } elseif ($extension === 'html') { + + $template = file_get_contents($templateFile); + + if (!is_array($variables) || empty($variables)) { + return $template; + } + + return $this->parseAsMustache($template, $variables); + } else { + throw new Exception(sprintf('Unknown extension %s in path %s', esc_html($extension), esc_html($templateFile))); + } + } + + public function buildSubject() + { + return $this->parseAsMustache( + $this->subject, + array_merge($this->headerVariables, $this->variables, $this->footerVariables) + ); + } + + public function parseAsMustache($string, $variables = array()) + { + + preg_match_all('/\{\{\s*.+?\s*\}\}/', $string, $matches); + + foreach ($matches[0] as $match) { + $var = str_replace('{', '', str_replace('}', '', preg_replace('/\s+/', '', $match))); + + if (isset($variables[$var]) && !is_array($variables[$var])) { + $string = str_replace($match, $variables[$var], $string); + } + } + + return $string; + } + + + /** + * Builds Email Headers + * @return String email headers + */ + public function buildHeaders() + { + $headers = ''; + + $headers .= implode("\r\n", $this->headers) . "\r\n"; + + foreach ($this->bcc as $bcc) { + $headers .= sprintf("Bcc: %s \r\n", $bcc); + } + + foreach ($this->cc as $cc) { + $headers .= sprintf("Cc: %s \r\n", $cc); + } + + if (!empty($this->from)) { + $headers .= sprintf("From: %s \r\n", $this->from); + } + + return $headers; + } + + + /** + * Sends a rendered email using + * WordPress's wp_mail() function + * @return Bool + */ + public function send() + { + if (count($this->to) === 0) { + throw new Exception('You must set at least 1 recipient'); + } + + if (empty($this->template)) { + throw new Exception('You must set a template'); + } + + if ($this->sendAsHTML) { + add_filter('wp_mail_content_type', array($this, 'HTMLFilter')); + } + + return wp_mail($this->to, $this->buildSubject(), $this->render(), $this->buildHeaders(), $this->attachments); + } +} diff --git a/index.php b/index.php new file mode 100755 index 0000000..6220032 --- /dev/null +++ b/index.php @@ -0,0 +1,2 @@ + - - org.netbeans.modules.php.project - - - software-license-manager - - - diff --git a/public/assets/css/jquery-ui.css b/public/assets/css/jquery-ui.css new file mode 100755 index 0000000..dec9c6a --- /dev/null +++ b/public/assets/css/jquery-ui.css @@ -0,0 +1 @@ +/*! jQuery UI - v1.12.1 - 2019-06-09 * http://jqueryui.com * Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css * To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif * Copyright jQuery Foundation and other contributors; Licensed MIT */ .ui-draggable-handle { -ms-touch-action: none; touch-action: none; } /* Layout helpers ----------------------------------*/ .ui-helper-hidden { display: none; } .ui-helper-hidden-accessible { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } .ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; border-collapse: collapse; } .ui-helper-clearfix:after { clear: both; } .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); /* support: IE8 */ } .ui-front { z-index: 100; } /* Interaction Cues ----------------------------------*/ .ui-state-disabled { cursor: default !important; pointer-events: none; } /* Icons ----------------------------------*/ .ui-icon { display: inline-block; vertical-align: middle; margin-top: -.25em; position: relative; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } .ui-widget-icon-block { left: 50%; margin-left: -8px; display: block; } /* Misc visuals ----------------------------------*/ /* Overlays */ .ui-widget-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; } .ui-resizable { position: relative; } .ui-resizable-handle { position: absolute; font-size: 0.1px; display: block; -ms-touch-action: none; touch-action: none; } .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px; } .ui-selectable { -ms-touch-action: none; touch-action: none; } .ui-selectable-helper { position: absolute; z-index: 100; border: 1px dotted black; } .ui-sortable-handle { -ms-touch-action: none; touch-action: none; } .ui-accordion .ui-accordion-header { display: block; cursor: pointer; position: relative; margin: 2px 0 0 0; padding: .5em .5em .5em .7em; font-size: 100%; } .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; overflow: auto; } .ui-autocomplete { position: absolute; top: 0; left: 0; cursor: default; } .ui-menu { list-style: none; padding: 0; margin: 0; display: block; outline: 0; } .ui-menu .ui-menu { position: absolute; } .ui-menu .ui-menu-item { margin: 0; cursor: pointer; /* support: IE10, see #8844 */ list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); } .ui-menu .ui-menu-item-wrapper { position: relative; padding: 3px 1em 3px .4em; } .ui-menu .ui-menu-divider { margin: 5px 0; height: 0; font-size: 0; line-height: 0; border-width: 1px 0 0 0; } .ui-menu .ui-state-focus, .ui-menu .ui-state-active { margin: -1px; } /* icon support */ .ui-menu-icons { position: relative; } .ui-menu-icons .ui-menu-item-wrapper { padding-left: 2em; } /* left-aligned */ .ui-menu .ui-icon { position: absolute; top: 0; bottom: 0; left: .2em; margin: auto 0; } /* right-aligned */ .ui-menu .ui-menu-icon { left: auto; right: 0; } .ui-button { padding: .4em 1em; display: inline-block; position: relative; line-height: normal; margin-right: .1em; cursor: pointer; vertical-align: middle; text-align: center; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; /* Support: IE <= 11 */ overflow: visible; } .ui-button, .ui-button:link, .ui-button:visited, .ui-button:hover, .ui-button:active { text-decoration: none; } /* to make room for the icon, a width needs to be set here */ .ui-button-icon-only { width: 2em; box-sizing: border-box; text-indent: -9999px; white-space: nowrap; } /* no icon support for input elements */ input.ui-button.ui-button-icon-only { text-indent: 0; } /* button icon element(s) */ .ui-button-icon-only .ui-icon { position: absolute; top: 50%; left: 50%; margin-top: -8px; margin-left: -8px; } .ui-button.ui-icon-notext .ui-icon { padding: 0; width: 2.1em; height: 2.1em; text-indent: -9999px; white-space: nowrap; } input.ui-button.ui-icon-notext .ui-icon { width: auto; height: auto; text-indent: 0; white-space: normal; padding: .4em 1em; } /* workarounds */ /* Support: Firefox 5 - 40 */ input.ui-button::-moz-focus-inner, button.ui-button::-moz-focus-inner { border: 0; padding: 0; } .ui-controlgroup { vertical-align: middle; display: inline-block; } .ui-controlgroup > .ui-controlgroup-item { float: left; margin-left: 0; margin-right: 0; } .ui-controlgroup > .ui-controlgroup-item:focus, .ui-controlgroup > .ui-controlgroup-item.ui-visual-focus { z-index: 9999; } .ui-controlgroup-vertical > .ui-controlgroup-item { display: block; float: none; width: 100%; margin-top: 0; margin-bottom: 0; text-align: left; } .ui-controlgroup-vertical .ui-controlgroup-item { box-sizing: border-box; } .ui-controlgroup .ui-controlgroup-label { padding: .4em 1em; } .ui-controlgroup .ui-controlgroup-label span { font-size: 80%; } .ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item { border-left: none; } .ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item { border-top: none; } .ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content { border-right: none; } .ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content { border-bottom: none; } /* Spinner specific style fixes */ .ui-controlgroup-vertical .ui-spinner-input { /* Support: IE8 only, Android < 4.4 only */ width: 75%; width: calc( 100% - 2.4em ); } .ui-controlgroup-vertical .ui-spinner .ui-spinner-up { border-top-style: solid; } .ui-checkboxradio-label .ui-icon-background { box-shadow: inset 1px 1px 1px #ccc; border-radius: .12em; border: none; } .ui-checkboxradio-radio-label .ui-icon-background { width: 16px; height: 16px; border-radius: 1em; overflow: visible; border: none; } .ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon, .ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon { background-image: none; width: 8px; height: 8px; border-width: 4px; border-style: solid; } .ui-checkboxradio-disabled { pointer-events: none; } .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } .ui-datepicker .ui-datepicker-header { position: relative; padding: .2em 0; } .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position: absolute; top: 2px; width: 1.8em; height: 1.8em; } .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } .ui-datepicker .ui-datepicker-prev { left: 2px; } .ui-datepicker .ui-datepicker-next { right: 2px; } .ui-datepicker .ui-datepicker-prev-hover { left: 1px; } .ui-datepicker .ui-datepicker-next-hover { right: 1px; } .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } .ui-datepicker .ui-datepicker-title select { font-size: 1em; margin: 1px 0; } .ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year { width: 45%; } .ui-datepicker table { width: 100%; font-size: .9em; border-collapse: collapse; margin: 0 0 .4em; } .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } .ui-datepicker td { border: 0; padding: 1px; } .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding: 0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width: auto; overflow: visible; } .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float: left; } /* with multiple calendars */ .ui-datepicker.ui-datepicker-multi { width: auto; } .ui-datepicker-multi .ui-datepicker-group { float: left; } .ui-datepicker-multi .ui-datepicker-group table { width: 95%; margin: 0 auto .4em; } .ui-datepicker-multi-2 .ui-datepicker-group { width: 50%; } .ui-datepicker-multi-3 .ui-datepicker-group { width: 33.3%; } .ui-datepicker-multi-4 .ui-datepicker-group { width: 25%; } .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width: 0; } .ui-datepicker-multi .ui-datepicker-buttonpane { clear: left; } .ui-datepicker-row-break { clear: both; width: 100%; font-size: 0; } /* RTL support */ .ui-datepicker-rtl { direction: rtl; } .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } .ui-datepicker-rtl .ui-datepicker-buttonpane { clear: right; } .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, .ui-datepicker-rtl .ui-datepicker-group { float: right; } .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width: 0; border-left-width: 1px; } /* Icons */ .ui-datepicker .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; left: .5em; top: .3em; } .ui-dialog { position: absolute; top: 0; left: 0; padding: .2em; outline: 0; } .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } .ui-dialog .ui-dialog-title { float: left; margin: .1em 0; white-space: nowrap; width: 90%; overflow: hidden; text-overflow: ellipsis; } .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 20px; margin: -10px 0 0 0; padding: 1px; height: 20px; } .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; } .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin-top: .5em; padding: .3em 1em .5em .4em; } .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } .ui-dialog .ui-resizable-n { height: 2px; top: 0; } .ui-dialog .ui-resizable-e { width: 2px; right: 0; } .ui-dialog .ui-resizable-s { height: 2px; bottom: 0; } .ui-dialog .ui-resizable-w { width: 2px; left: 0; } .ui-dialog .ui-resizable-se, .ui-dialog .ui-resizable-sw, .ui-dialog .ui-resizable-ne, .ui-dialog .ui-resizable-nw { width: 7px; height: 7px; } .ui-dialog .ui-resizable-se { right: 0; bottom: 0; } .ui-dialog .ui-resizable-sw { left: 0; bottom: 0; } .ui-dialog .ui-resizable-ne { right: 0; top: 0; } .ui-dialog .ui-resizable-nw { left: 0; top: 0; } .ui-draggable .ui-dialog-titlebar { cursor: move; } .ui-progressbar { height: 2em; text-align: left; overflow: hidden; } .ui-progressbar .ui-progressbar-value { margin: -1px; height: 100%; } .ui-progressbar .ui-progressbar-overlay { background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw=="); height: 100%; filter: alpha(opacity=25); /* support: IE8 */ opacity: 0.25; } .ui-progressbar-indeterminate .ui-progressbar-value { background-image: none; } .ui-selectmenu-menu { padding: 0; margin: 0; position: absolute; top: 0; left: 0; display: none; } .ui-selectmenu-menu .ui-menu { overflow: auto; overflow-x: hidden; padding-bottom: 1px; } .ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { font-size: 1em; font-weight: bold; line-height: 1.5; padding: 2px 0.4em; margin: 0.5em 0 0 0; height: auto; border: 0; } .ui-selectmenu-open { display: block; } .ui-selectmenu-text { display: block; margin-right: 20px; overflow: hidden; text-overflow: ellipsis; } .ui-selectmenu-button.ui-button { text-align: left; white-space: nowrap; width: 14em; } .ui-selectmenu-icon.ui-icon { float: right; margin-top: 0; } .ui-slider { position: relative; text-align: left; } .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; -ms-touch-action: none; touch-action: none; } .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } /* support: IE8 - See #6727 */ .ui-slider.ui-state-disabled .ui-slider-handle, .ui-slider.ui-state-disabled .ui-slider-range { filter: inherit; } .ui-slider-horizontal { height: .8em; } .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } .ui-slider-horizontal .ui-slider-range-min { left: 0; } .ui-slider-horizontal .ui-slider-range-max { right: 0; } .ui-slider-vertical { width: .8em; height: 100px; } .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } .ui-slider-vertical .ui-slider-range-min { bottom: 0; } .ui-slider-vertical .ui-slider-range-max { top: 0; } .ui-spinner { position: relative; display: inline-block; overflow: hidden; padding: 0; vertical-align: middle; } .ui-spinner-input { border: none; background: none; color: inherit; padding: .222em 0; margin: .2em 0; vertical-align: middle; margin-left: .4em; margin-right: 2em; } .ui-spinner-button { width: 1.6em; height: 50%; font-size: .5em; padding: 0; margin: 0; text-align: center; position: absolute; cursor: default; display: block; overflow: hidden; right: 0; } /* more specificity required here to override default borders */ .ui-spinner a.ui-spinner-button { border-top-style: none; border-bottom-style: none; border-right-style: none; } .ui-spinner-up { top: 0; } .ui-spinner-down { bottom: 0; } .ui-tabs { position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ padding: .2em; } .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 0; margin: 1px .2em 0 0; border-bottom-width: 0; padding: 0; white-space: nowrap; } .ui-tabs .ui-tabs-nav .ui-tabs-anchor { float: left; padding: .5em 1em; text-decoration: none; } .ui-tabs .ui-tabs-nav li.ui-tabs-active { margin-bottom: -1px; padding-bottom: 1px; } .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { cursor: text; } .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { cursor: pointer; } .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } .ui-tooltip { padding: 8px; position: absolute; z-index: 9999; max-width: 300px; } body .ui-tooltip { border-width: 2px; } /* Component containers ----------------------------------*/ .ui-widget { font-family: Arial,Helvetica,sans-serif; font-size: 1em; } .ui-widget .ui-widget { font-size: 1em; } .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Arial,Helvetica,sans-serif; font-size: 1em; } .ui-widget.ui-widget-content { border: 1px solid #c5c5c5; } .ui-widget-content { border: 1px solid #dddddd; background: #ffffff; color: #333333; } .ui-widget-content a { color: #333333; } .ui-widget-header { border: 1px solid #dddddd; background: #e9e9e9; color: #333333; font-weight: bold; } .ui-widget-header a { color: #333333; } /* Interaction states ----------------------------------*/ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, .ui-button, /* We use html here because we need a greater specificity to make sure disabled works properly when clicked or hovered */ html .ui-button.ui-state-disabled:hover, html .ui-button.ui-state-disabled:active { border: 1px solid #c5c5c5; background: #f6f6f6; font-weight: normal; color: #454545; } .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited, a.ui-button, a:link.ui-button, a:visited.ui-button, .ui-button { color: #454545; text-decoration: none; } .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus, .ui-button:hover, .ui-button:focus { border: 1px solid #cccccc; background: #ededed; font-weight: normal; color: #2b2b2b; } .ui-state-hover a, .ui-state-hover a:hover, .ui-state-hover a:link, .ui-state-hover a:visited, .ui-state-focus a, .ui-state-focus a:hover, .ui-state-focus a:link, .ui-state-focus a:visited, a.ui-button:hover, a.ui-button:focus { color: #2b2b2b; text-decoration: none; } .ui-visual-focus { box-shadow: 0 0 3px 1px rgb(94, 158, 214); } .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover { border: 1px solid #003eff; background: #007fff; font-weight: normal; color: #ffffff; } .ui-icon-background, .ui-state-active .ui-icon-background { border: #003eff; background-color: #ffffff; } .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #ffffff; text-decoration: none; } /* Interaction Cues ----------------------------------*/ .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { border: 1px solid #dad55e; background: #fffa90; color: #777620; } .ui-state-checked { border: 1px solid #dad55e; background: #fffa90; } .ui-state-highlight a, .ui-widget-content .ui-state-highlight a, .ui-widget-header .ui-state-highlight a { color: #777620; } .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { border: 1px solid #f1a899; background: #fddfdf; color: #5f3f3f; } .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #5f3f3f; } .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #5f3f3f; } .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); /* support: IE8 */ font-weight: normal; } .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); /* support: IE8 */ background-image: none; } .ui-state-disabled .ui-icon { filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ } /* Icons ----------------------------------*/ /* states and images */ .ui-icon { width: 16px; height: 16px; } .ui-icon, .ui-widget-content .ui-icon { background-image: url("../images/ui-icons_444444_256x240.png"); } .ui-widget-header .ui-icon { background-image: url("../images/ui-icons_444444_256x240.png"); } .ui-state-hover .ui-icon, .ui-state-focus .ui-icon, .ui-button:hover .ui-icon, .ui-button:focus .ui-icon { background-image: url("../images/ui-icons_555555_256x240.png"); } .ui-state-active .ui-icon, .ui-button:active .ui-icon { background-image: url("../images/ui-icons_ffffff_256x240.png"); } .ui-state-highlight .ui-icon, .ui-button .ui-state-highlight.ui-icon { background-image: url("../images/ui-icons_777620_256x240.png"); } .ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background-image: url("../images/ui-icons_cc0000_256x240.png"); } .ui-button .ui-icon { background-image: url("../images/ui-icons_777777_256x240.png"); } /* positioning */ .ui-icon-blank { background-position: 16px 16px; } .ui-icon-caret-1-n { background-position: 0 0; } .ui-icon-caret-1-ne { background-position: -16px 0; } .ui-icon-caret-1-e { background-position: -32px 0; } .ui-icon-caret-1-se { background-position: -48px 0; } .ui-icon-caret-1-s { background-position: -65px 0; } .ui-icon-caret-1-sw { background-position: -80px 0; } .ui-icon-caret-1-w { background-position: -96px 0; } .ui-icon-caret-1-nw { background-position: -112px 0; } .ui-icon-caret-2-n-s { background-position: -128px 0; } .ui-icon-caret-2-e-w { background-position: -144px 0; } .ui-icon-triangle-1-n { background-position: 0 -16px; } .ui-icon-triangle-1-ne { background-position: -16px -16px; } .ui-icon-triangle-1-e { background-position: -32px -16px; } .ui-icon-triangle-1-se { background-position: -48px -16px; } .ui-icon-triangle-1-s { background-position: -65px -16px; } .ui-icon-triangle-1-sw { background-position: -80px -16px; } .ui-icon-triangle-1-w { background-position: -96px -16px; } .ui-icon-triangle-1-nw { background-position: -112px -16px; } .ui-icon-triangle-2-n-s { background-position: -128px -16px; } .ui-icon-triangle-2-e-w { background-position: -144px -16px; } .ui-icon-arrow-1-n { background-position: 0 -32px; } .ui-icon-arrow-1-ne { background-position: -16px -32px; } .ui-icon-arrow-1-e { background-position: -32px -32px; } .ui-icon-arrow-1-se { background-position: -48px -32px; } .ui-icon-arrow-1-s { background-position: -65px -32px; } .ui-icon-arrow-1-sw { background-position: -80px -32px; } .ui-icon-arrow-1-w { background-position: -96px -32px; } .ui-icon-arrow-1-nw { background-position: -112px -32px; } .ui-icon-arrow-2-n-s { background-position: -128px -32px; } .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } .ui-icon-arrow-2-e-w { background-position: -160px -32px; } .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } .ui-icon-arrowstop-1-n { background-position: -192px -32px; } .ui-icon-arrowstop-1-e { background-position: -208px -32px; } .ui-icon-arrowstop-1-s { background-position: -224px -32px; } .ui-icon-arrowstop-1-w { background-position: -240px -32px; } .ui-icon-arrowthick-1-n { background-position: 1px -48px; } .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } .ui-icon-arrowthick-1-e { background-position: -32px -48px; } .ui-icon-arrowthick-1-se { background-position: -48px -48px; } .ui-icon-arrowthick-1-s { background-position: -64px -48px; } .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } .ui-icon-arrowthick-1-w { background-position: -96px -48px; } .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } .ui-icon-arrow-4 { background-position: 0 -80px; } .ui-icon-arrow-4-diag { background-position: -16px -80px; } .ui-icon-extlink { background-position: -32px -80px; } .ui-icon-newwin { background-position: -48px -80px; } .ui-icon-refresh { background-position: -64px -80px; } .ui-icon-shuffle { background-position: -80px -80px; } .ui-icon-transfer-e-w { background-position: -96px -80px; } .ui-icon-transferthick-e-w { background-position: -112px -80px; } .ui-icon-folder-collapsed { background-position: 0 -96px; } .ui-icon-folder-open { background-position: -16px -96px; } .ui-icon-document { background-position: -32px -96px; } .ui-icon-document-b { background-position: -48px -96px; } .ui-icon-note { background-position: -64px -96px; } .ui-icon-mail-closed { background-position: -80px -96px; } .ui-icon-mail-open { background-position: -96px -96px; } .ui-icon-suitcase { background-position: -112px -96px; } .ui-icon-comment { background-position: -128px -96px; } .ui-icon-person { background-position: -144px -96px; } .ui-icon-print { background-position: -160px -96px; } .ui-icon-trash { background-position: -176px -96px; } .ui-icon-locked { background-position: -192px -96px; } .ui-icon-unlocked { background-position: -208px -96px; } .ui-icon-bookmark { background-position: -224px -96px; } .ui-icon-tag { background-position: -240px -96px; } .ui-icon-home { background-position: 0 -112px; } .ui-icon-flag { background-position: -16px -112px; } .ui-icon-calendar { background-position: -32px -112px; } .ui-icon-cart { background-position: -48px -112px; } .ui-icon-pencil { background-position: -64px -112px; } .ui-icon-clock { background-position: -80px -112px; } .ui-icon-disk { background-position: -96px -112px; } .ui-icon-calculator { background-position: -112px -112px; } .ui-icon-zoomin { background-position: -128px -112px; } .ui-icon-zoomout { background-position: -144px -112px; } .ui-icon-search { background-position: -160px -112px; } .ui-icon-wrench { background-position: -176px -112px; } .ui-icon-gear { background-position: -192px -112px; } .ui-icon-heart { background-position: -208px -112px; } .ui-icon-star { background-position: -224px -112px; } .ui-icon-link { background-position: -240px -112px; } .ui-icon-cancel { background-position: 0 -128px; } .ui-icon-plus { background-position: -16px -128px; } .ui-icon-plusthick { background-position: -32px -128px; } .ui-icon-minus { background-position: -48px -128px; } .ui-icon-minusthick { background-position: -64px -128px; } .ui-icon-close { background-position: -80px -128px; } .ui-icon-closethick { background-position: -96px -128px; } .ui-icon-key { background-position: -112px -128px; } .ui-icon-lightbulb { background-position: -128px -128px; } .ui-icon-scissors { background-position: -144px -128px; } .ui-icon-clipboard { background-position: -160px -128px; } .ui-icon-copy { background-position: -176px -128px; } .ui-icon-contact { background-position: -192px -128px; } .ui-icon-image { background-position: -208px -128px; } .ui-icon-video { background-position: -224px -128px; } .ui-icon-script { background-position: -240px -128px; } .ui-icon-alert { background-position: 0 -144px; } .ui-icon-info { background-position: -16px -144px; } .ui-icon-notice { background-position: -32px -144px; } .ui-icon-help { background-position: -48px -144px; } .ui-icon-check { background-position: -64px -144px; } .ui-icon-bullet { background-position: -80px -144px; } .ui-icon-radio-on { background-position: -96px -144px; } .ui-icon-radio-off { background-position: -112px -144px; } .ui-icon-pin-w { background-position: -128px -144px; } .ui-icon-pin-s { background-position: -144px -144px; } .ui-icon-play { background-position: 0 -160px; } .ui-icon-pause { background-position: -16px -160px; } .ui-icon-seek-next { background-position: -32px -160px; } .ui-icon-seek-prev { background-position: -48px -160px; } .ui-icon-seek-end { background-position: -64px -160px; } .ui-icon-seek-start { background-position: -80px -160px; } /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ .ui-icon-seek-first { background-position: -80px -160px; } .ui-icon-stop { background-position: -96px -160px; } .ui-icon-eject { background-position: -112px -160px; } .ui-icon-volume-off { background-position: -128px -160px; } .ui-icon-volume-on { background-position: -144px -160px; } .ui-icon-power { background-position: 0 -176px; } .ui-icon-signal-diag { background-position: -16px -176px; } .ui-icon-signal { background-position: -32px -176px; } .ui-icon-battery-0 { background-position: -48px -176px; } .ui-icon-battery-1 { background-position: -64px -176px; } .ui-icon-battery-2 { background-position: -80px -176px; } .ui-icon-battery-3 { background-position: -96px -176px; } .ui-icon-circle-plus { background-position: 0 -192px; } .ui-icon-circle-minus { background-position: -16px -192px; } .ui-icon-circle-close { background-position: -32px -192px; } .ui-icon-circle-triangle-e { background-position: -48px -192px; } .ui-icon-circle-triangle-s { background-position: -64px -192px; } .ui-icon-circle-triangle-w { background-position: -80px -192px; } .ui-icon-circle-triangle-n { background-position: -96px -192px; } .ui-icon-circle-arrow-e { background-position: -112px -192px; } .ui-icon-circle-arrow-s { background-position: -128px -192px; } .ui-icon-circle-arrow-w { background-position: -144px -192px; } .ui-icon-circle-arrow-n { background-position: -160px -192px; } .ui-icon-circle-zoomin { background-position: -176px -192px; } .ui-icon-circle-zoomout { background-position: -192px -192px; } .ui-icon-circle-check { background-position: -208px -192px; } .ui-icon-circlesmall-plus { background-position: 0 -208px; } .ui-icon-circlesmall-minus { background-position: -16px -208px; } .ui-icon-circlesmall-close { background-position: -32px -208px; } .ui-icon-squaresmall-plus { background-position: -48px -208px; } .ui-icon-squaresmall-minus { background-position: -64px -208px; } .ui-icon-squaresmall-close { background-position: -80px -208px; } .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } .ui-icon-grip-solid-vertical { background-position: -32px -224px; } .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } .ui-icon-grip-diagonal-se { background-position: -80px -224px; } /* Misc visuals ----------------------------------*/ /* Corner radius */ .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { border-top-left-radius: 3px; } .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { border-top-right-radius: 3px; } .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { border-bottom-left-radius: 3px; } .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { border-bottom-right-radius: 3px; } /* Overlays */ .ui-widget-overlay { background: #aaaaaa; opacity: .3; filter: Alpha(Opacity=30); /* support: IE8 */ } .ui-widget-shadow { -webkit-box-shadow: 0px 0px 5px #666666; box-shadow: 0px 0px 5px #666666; } \ No newline at end of file diff --git a/public/assets/css/slm-blocks.css b/public/assets/css/slm-blocks.css new file mode 100644 index 0000000..bf7a5ef --- /dev/null +++ b/public/assets/css/slm-blocks.css @@ -0,0 +1,84 @@ +.wp-block-slm-forgot-license { + background-color: #f9f9f9; + border: 1px dashed #ddd; + padding: 10px; + text-align: center; + font-size: 14px; + font-style: italic; +} +/* Style for the block preview */ +.slm-forgot-license-preview { + padding: 20px; + background-color: #f9f9f9; + border: 1px solid #ddd; + border-radius: 4px; + max-width: 400px; + font-family: Arial, sans-serif; +} + +/* Vertically stacked form */ +.slm-forgot-license-form { + display: flex; + flex-direction: column; /* Stack items vertically */ + gap: 10px; /* Adds spacing between form elements */ + width: 100%; /* Ensure the form takes full width */ +} + +.slm-forgot-license-form label { + font-weight: bold; + margin-bottom: 5px; +} + +.slm-forgot-license-form input { + width: 100%; + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; /* Ensures padding doesn't affect width */ +} + +.slm-forgot-license-form button { + padding: 10px; + background-color: #0073aa; + color: #fff; + border: none; + border-radius: 4px; + cursor: not-allowed; /* Indicate the button is disabled in preview */ + text-align: center; + font-size: 16px; +} + +/* Add hover styling for better preview (if enabled) */ +.slm-forgot-license-form button:hover { + background-color: #005f8d; +} + +/* Style for the List Licenses block preview */ +.slm-list-licenses-preview { + padding: 20px; + background-color: #f9f9f9; + border: 1px solid #ddd; + border-radius: 4px; + max-width: 400px; + font-family: Arial, sans-serif; +} + +.slm-list-licenses-preview-message { + font-weight: bold; + margin-bottom: 10px; +} + +.slm-list-licenses-placeholder { + list-style-type: none; + padding: 0; + margin: 0; +} + +.slm-list-licenses-placeholder li { + padding: 5px 0; + border-bottom: 1px solid #ddd; +} + +.slm-list-licenses-placeholder li:last-child { + border-bottom: none; +} diff --git a/public/assets/css/slm-front-end.css b/public/assets/css/slm-front-end.css new file mode 100644 index 0000000..ce8a4b3 --- /dev/null +++ b/public/assets/css/slm-front-end.css @@ -0,0 +1,53 @@ +/* General Badge Styling */ +.slm-status-badge { + padding: 3px 8px; + border-radius: 4px; + color: white; + font-weight: bold; + text-transform: capitalize; + display: inline-block; +} + +/* Specific Styling for Each Status */ +.slm-status-badge.status-pending { + background-color: #ffc107; /* Amber */ +} + +.slm-status-badge.status-active { + background-color: #007bff; /* Blue */ +} + +.slm-status-badge.status-blocked { + background-color: #dc3545; /* Red */ +} + +.slm-status-badge.status-expired { + background-color: #6c757d; /* Gray */ +} + +.slm-licenses-table { + width: 100%; + border-collapse: collapse; + margin: 20px 0; + font-size: 14px; + font-family: Arial, sans-serif; + text-align: left; +} + +.slm-licenses-table th, .slm-licenses-table td { + border: 1px solid #ddd; + padding: 10px; +} + +.slm-licenses-table th { + background-color: #f4f4f4; + font-weight: bold; +} + +.slm-licenses-table tr:nth-child(even) { + background-color: #f9f9f9; +} + +.slm-licenses-table tr:hover { + background-color: #f1f1f1; +} diff --git a/public/assets/css/slm.css b/public/assets/css/slm.css new file mode 100755 index 0000000..72c65b8 --- /dev/null +++ b/public/assets/css/slm.css @@ -0,0 +1,588 @@ +body.slm_page_slm_manage_license { + background: #f1f1f1; + background-color: #f1f1f1; +} +.logo { + text-align: center; + margin-bottom: 32px; +} +.required.invalid { + border: 1px solid red !important; +} +.container.slm-container { + background: #fff; + padding: 30px 30px 70px; + box-shadow: 0 1px 20px 5px rgba(0, 0, 0, .1);; +} +.clear { + clear: both; + float: none; + width: 100%; +} + +.slm-shadow { + background: #fff; + margin: 2%; + padding: 2%; + box-shadow: 0px 2px 13px #d4d4d4; + border-radius: 3px; +} + +.slm_page_slm_settings .metabox-holder.has-right-sidebar { + background: #fff; + padding: 25px; +} +.slm-logo img { + max-height: 78px; + height: 68px; + padding-top: 8px; +} +.container.slm-container .order_data_column.row { + margin-right: -1px; +} +.container.slm-container pre { + display: block; + font-size: 87.5%; + color: #212529; + max-height: 174px; + overflow-x: scroll; +} +@media (max-width: 1200px){ + + .container.slm-container { + min-height: 665px; + } +} +.slm-img-ico { + float: left; + width: 51px; + margin-right: 16px; + padding-top: 4px; +} +.sml-sep { + border-bottom: 1px solid #eee; + margin: 1px 0 13px 0px; + clear: both; +} +.slm-tab-title { + float: left; + width: 90%; +} +#order_data .invalid{ + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; + position: relative; + padding: .75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: .25rem; + margin: 5px 0; +} +.slm-container { + max-width: 847px; + margin: 0 auto; + float: none; +} +.slm-container .postbox { + border-top: 0; +} +.slm-container #order_data { + background: #f5f5f5; + border: 1px solid #e5e5e5; + box-shadow: 0 1px 1px rgba(0,0,0,.04); +} +.sml-col-right { + padding-right: 0 !important; + margin: 0; +} +.slm-container #order_data .nav-pills .nav-link { + border-radius: 0; +} +.slm-container #order_data .nav-pills .nav-link.active, .nav-pills .show>.nav-link { + color: #32373c; + background-color: #ffffff; +} +.slm-container #order_data .nav-pills a.nav-link { + color: #32373c; + font-size: 13px; + font-weight: 600; +} +.slm-container #order_data .nav-pills li.nav-item { + border-bottom: 1px solid #e6e6e6; +} +.sml-col-left { + background-color: #fff; + border: 1px solid #e5e5e5; + display: inline-block; + margin-left: -1px; + min-height: 200px; + width: 700px; + z-index: 2; +} +.slm-container h1 { + font-size: 32px; + color: #444; + padding-bottom: 8px; + font-weight: 300; +} +.slm-container .center { + text-align: center; +} +.imgh2 img { + vertical-align: middle; + max-height: 40px; + margin-right: 24px; +} +.slm-container button, .slm-container input, .slm-container select, .slm-container textarea { + font-size: 16px; +} +.set-pd { + padding: 24px; +} +.devices-info { + overflow:auto; + height:200px; + width:280px; + border:1px solid #ccc; + padding: 8px; +} +.del_device, +.del { + color: white; + margin: 0 8px; + display: block; + background: #dc3545; + padding: 4px; + border-radius: 12px; + width: 16px; + height: 16px; + line-height: 8px; + text-align: center; + font-size: 10px; +} +.del_device:hover, .del:hover { +background: red; +color: #fff; +cursor: pointer; +} +.slm-container .nav-tabs { + padding:0; + margin-top: 40px; +} +.slm-container h2, .slm-container h3 { + font-size: 1.3em; + margin: 1em 0; + color: #111; +} +.slm-container h3 span { + font-weight: 300; + color: #666; +} +.slm-container .form-field { + line-height: 1.5; + margin: 1em 0; + font-size: 11px; + color: #72777c; +} +.slm-container .form-field label { + font-size: 12px; + color: #333; + font-weight: 600; + display: block; + padding-bottom: 4px; +} +.col-f { + padding: 8px; +} +.col { + padding: 16px; +} +.col-half { + float: left; + width: 50%; +} +.slm-container .tab-pane { + padding: 8px; +} +.col-4{ + float: left; + width: 31.66%; + padding: 5px; +} +.clear { + clear: both; + display: block; + float: none; +} + +.table tr:hover { + opacity: 0.8 +} +.postbox.product_info.col { + min-height: 347px; +} +.error.stripe-apple-pay-message { + display: none; +} + +/* .save_lic, .wp-core-ui .button.save_lic { + display: inline-block; + font-weight: 400; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border: 1px solid transparent; + padding: .375rem .75rem; + font-size: 1rem; + height: auto; + line-height: 1.5; + border-radius: .25rem; + transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; + color: #fff; + background-color: #007bff !important; + border-color: #007bff !important; +} */ +span.slm-status { + height: 8px; + width: 8px; + display: inline-block; + background: #72777c; + border-radius: 8px; + margin-right: 8px; +} +span.slm-status.pending { + background: #72777c; +} +span.slm-status.blocked { + background: #6649c9; +} +span.slm-status.active { + background: #00a0d2; +} +span.slm-status.expired { + background: #f0283e; +} +span.slm-lic-expired-date { + color: #c44343; + display: inline-block; + background: #fed4d4; + padding: 4px 8px; + border-radius: 91px; + font-weight: 600; +} +span.tag.license-date-null { + display: inline-block; + background: #add; + padding: 4px 8px; + border-radius: 91px; + color: #467675; + font-weight: 600; +} +span.tag.license-date-valid { + display: inline-block; + background: #ddeecb; + padding: 4px 8px; + border-radius: 91px; + color: #4f6835; + font-weight: 600; +} +span.days-left { + font-size: 12px; + display: block; + padding: 4px 0; + color: #72777c; +} + +#slm_dashboard_widget .inside { + padding: 0; + margin: 0 +} + +#slm_dashboard_widget .total-licenses a strong { + margin-right: 48px +} + +#slm_dashboard_widget .slm_status_list { + overflow: hidden; + margin: 0 +} + +#slm_dashboard_widget .slm_status_list li { + width: 50%; + float: left; + padding: 0; + box-sizing: border-box; + margin: 0; + border-top: 1px solid #ececec; + color: #aaa +} + +#slm_dashboard_widget .slm_status_list li a { + display: block; + color: #aaa; + padding: 9px 12px; + -webkit-transition: all ease .5s; + transition: all ease .5s; + position: relative; + font-size: 12px +} + +#slm_dashboard_widget .slm_status_list li a .wc_sparkline { + width: 4em; + height: 2em; + display: block; + float: right; + position: absolute; + right: 0; + top: 50%; + margin-right: 12px; + margin-top: -1.25em +} + +#slm_dashboard_widget .slm_status_list li a strong { + font-size: 18px; + line-height: 1.2em; + font-weight: 400; + display: block; + color: #21759b +} + +#slm_dashboard_widget .slm_status_list li a:hover { + color: #2ea2cc +} + +#slm_dashboard_widget .slm_status_list li a:hover strong, +#slm_dashboard_widget .slm_status_list li a:hover::before { + color: #2ea2cc!important +} + +#slm_dashboard_widget .slm_status_list li a .icon { + speak: none; + font-weight: 400; + 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: ""; + font-size: 2em; + position: relative; + width: auto; + line-height: 1.2em; + color: #464646; + float: left; + margin-right: 12px; + margin-bottom: 12px +} + +#slm_dashboard_widget .slm_status_list li:first-child { + border-top: 0 +} + +#slm_dashboard_widget .slm_status_list li.sales-this-month { + width: 100% +} + + +#slm_dashboard_widget .slm_status_list li.total-licenses { + width: 100% +} + +#slm_dashboard_widget .slm_status_list li.total-licenses a .icon { + content: '\e006' +} + +#slm_dashboard_widget .slm_status_list li.active-licenses { + border-right: 1px solid #ececec +} + +#slm_dashboard_widget .slm_status_list li.active-licenses a .icon { + color: #00a0d2 +} + +#slm_dashboard_widget .slm_status_list li.pending-licenses a .icon { + color: #72777c +} + +#slm_dashboard_widget .slm_status_list li.blocked-licenses { + border-right: 1px solid #ececec +} + +#slm_dashboard_widget .slm_status_list li.blocked-licenses a .icon { + color: #6649c9 +} + +#slm_dashboard_widget .slm_status_list li.expired-licenses a .icon { + color: #f0283e +} + +#slm_dashboard_widget li { + line-height: 1.5em; + margin-bottom: 12px +} + +#slm_dashboard_widget h4.meta { + line-height: 1.4; + margin: -.2em 0 0 0; + font-weight: 400; + color: #999 +} +#slm_dashboard_widget .recent_licenses { + padding: 12px; +} +#slm_dashboard_widget blockquote { + padding: 0; + margin: 0 +} +#slm_dashboard_widget .badge { + background: #00769c; + border-radius: 3px; + color: #fff; + font-size: 10px; + padding: 2px 4px; + margin-right: 2px; + display: inline-block; +} +#slm_dashboard_widget td.license-row { + width: 80%; +} +#slm_dashboard_widget .recent_licenses table { + width: 100%; +} +#slm_dashboard_widget .recent_licenses tr { + padding: 3px 0; + display: inline-block; + width: 100%; +} +#slm_dashboard_widget .recent_licenses td { + padding: 3px 0; +} +#slm_dashboard_widget .recent_licenses table thead td { + border-bottom: 1px solid #ececec; + color: #777; +} +#slm_dashboard_widget .recent_licenses table thead td { + border-bottom: 1px solid #ececec; + color: #777; +} +#slm_dashboard_widget .recent_licenses td { + padding: 3px 0; +} +.slm_overview_stats .stats{ + background: #fff; + padding: 7px 9px; + border-radius: 6px; + -webkit-box-shadow: 0px 0px 16px 1px rgba(0,0,0,0.2); + -moz-box-shadow: 0px 0px 16px 1px rgba(0,0,0,0.2); + box-shadow: 0px 0px 16px 1px rgba(0,0,0,0.2); + max-width: 33%; + float: left; + margin: 4px; +} +.slm_overview_stats li:first-child { + margin-left: 0 !important +} +/* .slm_overview_stats li:last-child { + margin-left: 0 !important +} */ +.slm_overview_stats .stats .info { + float: left; + text-align: left; +} +.slm_overview_stats .stats .icon { + float: left; + margin-right: 32px; +} +.slm_overview_stats .stats .icon span { + font-size: 24px; + color: #a4bac5; +} +.slm_overview_stats .stats span { + display: block; + clear: both; + padding: 4px 0 0 0; +} +.slm_overview_stats .stats span.badge { + font-size: 25px; + display: block; + width: 100%; + padding: 4px 0 0 0; +} + +#wpadminbar #wp-admin-bar-slm-menu .ab-icon:before { + content: "\f112"; + top: 3px; +} + +#slm_dashboard_widget .slm_status_list .dashicons:before, #slm_dashboard_widget .slm_status_list .dashicons { + width: 29px !important; + height: 12px !important; + font-size: 30px; + +} +.slm-col-half { + float: left; + width: 50% + +} +.v-spacer { + width: 32px; + float: left; + display: block; + height: 53px; +} +.h-spacer { + height: 24px; + clear: both; +} +.show_if_slm_license.slm-display, .slm-display { +display: block !important +} +.stats .description{ + padding: 10px 15px 0px 15px; +} +/* Suggestions box styling */ +.user-search-suggestions { + display: none; /* Start hidden */ + position: absolute; + border: 1px solid #ddd; + background: #fff; + z-index: 1000; + max-width: 300px; + width: 100%; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + margin-top: 5px; + padding: 5px 0; +} + + +/* Individual suggestion item */ +.user-search-suggestions .suggestion-item { + padding: 8px 12px; + cursor: pointer; + font-size: 13px; +} + +/* Hover effect for suggestion items */ +.user-search-suggestions .suggestion-item:hover { + background-color: #007cba; /* WP blue */ + color: #fff; +} + +/* Style for no results found */ +.user-search-suggestions .no-results { + padding: 8px 12px; + color: #a0a5aa; + font-style: italic; +} diff --git a/public/assets/icons/1x/box-2.png b/public/assets/icons/1x/box-2.png new file mode 100644 index 0000000..168cd42 Binary files /dev/null and b/public/assets/icons/1x/box-2.png differ diff --git a/public/assets/icons/1x/circle-09.png b/public/assets/icons/1x/circle-09.png new file mode 100644 index 0000000..3f38501 Binary files /dev/null and b/public/assets/icons/1x/circle-09.png differ diff --git a/public/assets/icons/1x/detail.png b/public/assets/icons/1x/detail.png new file mode 100644 index 0000000..241115f Binary files /dev/null and b/public/assets/icons/1x/detail.png differ diff --git a/public/assets/icons/1x/l-system-update.png b/public/assets/icons/1x/l-system-update.png new file mode 100644 index 0000000..f3b3bd9 Binary files /dev/null and b/public/assets/icons/1x/l-system-update.png differ diff --git a/public/assets/icons/1x/locked.png b/public/assets/icons/1x/locked.png new file mode 100644 index 0000000..1defa38 Binary files /dev/null and b/public/assets/icons/1x/locked.png differ diff --git a/public/assets/icons/1x/server-rack.png b/public/assets/icons/1x/server-rack.png new file mode 100644 index 0000000..67b026b Binary files /dev/null and b/public/assets/icons/1x/server-rack.png differ diff --git a/public/assets/icons/1x/share-right.png b/public/assets/icons/1x/share-right.png new file mode 100644 index 0000000..87529e7 Binary files /dev/null and b/public/assets/icons/1x/share-right.png differ diff --git a/public/assets/icons/logo/slm-large.svg b/public/assets/icons/logo/slm-large.svg new file mode 100644 index 0000000..10bf2a5 --- /dev/null +++ b/public/assets/icons/logo/slm-large.svg @@ -0,0 +1 @@ +privacy \ No newline at end of file diff --git a/public/assets/icons/logo/slm_logo.png b/public/assets/icons/logo/slm_logo.png new file mode 100755 index 0000000..3c10f4a Binary files /dev/null and b/public/assets/icons/logo/slm_logo.png differ diff --git a/public/assets/icons/logo/slm_logo.svg b/public/assets/icons/logo/slm_logo.svg new file mode 100644 index 0000000..10bf2a5 --- /dev/null +++ b/public/assets/icons/logo/slm_logo.svg @@ -0,0 +1 @@ +privacy \ No newline at end of file diff --git a/public/assets/icons/logo/slm_logo_small.png b/public/assets/icons/logo/slm_logo_small.png new file mode 100755 index 0000000..a209f79 Binary files /dev/null and b/public/assets/icons/logo/slm_logo_small.png differ diff --git a/public/assets/icons/logo/slm_logo_small.svg b/public/assets/icons/logo/slm_logo_small.svg new file mode 100644 index 0000000..10bf2a5 --- /dev/null +++ b/public/assets/icons/logo/slm_logo_small.svg @@ -0,0 +1 @@ +privacy \ No newline at end of file diff --git a/public/assets/images/key-26.svg b/public/assets/images/key-26.svg new file mode 100755 index 0000000..28c5cdf --- /dev/null +++ b/public/assets/images/key-26.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/public/assets/images/previews/1.png b/public/assets/images/previews/1.png new file mode 100644 index 0000000..f70d87e Binary files /dev/null and b/public/assets/images/previews/1.png differ diff --git a/public/assets/images/previews/10.png b/public/assets/images/previews/10.png new file mode 100644 index 0000000..94a13c9 Binary files /dev/null and b/public/assets/images/previews/10.png differ diff --git a/public/assets/images/previews/11.png b/public/assets/images/previews/11.png new file mode 100644 index 0000000..c96a5c1 Binary files /dev/null and b/public/assets/images/previews/11.png differ diff --git a/public/assets/images/previews/12.png b/public/assets/images/previews/12.png new file mode 100644 index 0000000..b1ac0a6 Binary files /dev/null and b/public/assets/images/previews/12.png differ diff --git a/public/assets/images/previews/13.png b/public/assets/images/previews/13.png new file mode 100644 index 0000000..e8cf9ab Binary files /dev/null and b/public/assets/images/previews/13.png differ diff --git a/public/assets/images/previews/14.png b/public/assets/images/previews/14.png new file mode 100644 index 0000000..cf60941 Binary files /dev/null and b/public/assets/images/previews/14.png differ diff --git a/public/assets/images/previews/2.png b/public/assets/images/previews/2.png new file mode 100644 index 0000000..c5263ae Binary files /dev/null and b/public/assets/images/previews/2.png differ diff --git a/public/assets/images/previews/3.png b/public/assets/images/previews/3.png new file mode 100644 index 0000000..8d4740d Binary files /dev/null and b/public/assets/images/previews/3.png differ diff --git a/public/assets/images/previews/4.png b/public/assets/images/previews/4.png new file mode 100644 index 0000000..76a3f8f Binary files /dev/null and b/public/assets/images/previews/4.png differ diff --git a/public/assets/images/previews/5.png b/public/assets/images/previews/5.png new file mode 100644 index 0000000..4a3fc18 Binary files /dev/null and b/public/assets/images/previews/5.png differ diff --git a/public/assets/images/previews/6.png b/public/assets/images/previews/6.png new file mode 100644 index 0000000..154d741 Binary files /dev/null and b/public/assets/images/previews/6.png differ diff --git a/public/assets/images/previews/7.png b/public/assets/images/previews/7.png new file mode 100644 index 0000000..74fbd88 Binary files /dev/null and b/public/assets/images/previews/7.png differ diff --git a/public/assets/images/previews/8.png b/public/assets/images/previews/8.png new file mode 100644 index 0000000..f05eb6d Binary files /dev/null and b/public/assets/images/previews/8.png differ diff --git a/public/assets/images/previews/9.png b/public/assets/images/previews/9.png new file mode 100644 index 0000000..8f634b1 Binary files /dev/null and b/public/assets/images/previews/9.png differ diff --git a/public/assets/images/slm_logo.svg b/public/assets/images/slm_logo.svg new file mode 100644 index 0000000..10bf2a5 --- /dev/null +++ b/public/assets/images/slm_logo.svg @@ -0,0 +1 @@ +privacy \ No newline at end of file diff --git a/public/assets/images/slm_logo_small.svg b/public/assets/images/slm_logo_small.svg new file mode 100755 index 0000000..8f20dac --- /dev/null +++ b/public/assets/images/slm_logo_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/software-license-manager/index.html b/public/assets/js/index.html old mode 100644 new mode 100755 similarity index 100% rename from software-license-manager/index.html rename to public/assets/js/index.html diff --git a/public/assets/js/slm-blocks.js b/public/assets/js/slm-blocks.js new file mode 100644 index 0000000..c27c138 --- /dev/null +++ b/public/assets/js/slm-blocks.js @@ -0,0 +1,97 @@ +const { registerBlockType } = wp.blocks; +const { createElement } = wp.element; +const { __ } = wp.i18n; + +// Register a custom block category. +wp.domReady(() => { + // Check if the category already exists. + const categories = wp.blocks.getCategories(); + + if (!categories.some((category) => category.slug === 'slm-plus')) { + // Add the custom category. + wp.blocks.setCategories([ + ...categories, + { + slug: 'slm-plus', + title: __('SLM Plus', 'slm-plus'), + icon: 'admin-network', // Dashicon or a custom icon. + }, + ]); + } +}); + + +// Register the block in the "SLM Plus" category. +registerBlockType('slm-plus/forgot-license', { + title: __('Forgot License', 'slm-plus'), + icon: 'lock', // Dashicon or custom icon for the block. + category: 'slm-plus', // Assign to the SLM Plus category. + attributes: {}, + + edit: () => { + // Create a more realistic preview for the editor. + return createElement( + 'div', + { className: 'slm-forgot-license-preview' }, + createElement( + 'form', + { className: 'slm-forgot-license-form' }, + createElement( + 'label', + { htmlFor: 'slm-email' }, + __('Enter your email address:', 'slm-plus') + ), + createElement('input', { + type: 'email', + id: 'slm-email', + placeholder: __('example@domain.com', 'slm-plus'), + disabled: true, // Disable interaction in the editor. + }), + createElement( + 'button', + { type: 'button', disabled: true }, + __('Retrieve License', 'slm-plus') + ) + ) + ); + }, + + save: () => { + // The saved output will still be the shortcode for frontend rendering. + return createElement('p', {}, '[slm_forgot_license]'); + }, +}); + + +// Register the "List Licenses" block. +registerBlockType('slm-plus/list-licenses', { + title: __('List Licenses', 'slm-plus'), + icon: 'list-view', // Dashicon for the block. + category: 'slm-plus', // Assign to the SLM Plus category. + attributes: {}, + + edit: () => { + return createElement( + 'div', + { className: 'slm-list-licenses-preview' }, + createElement( + 'p', + { className: 'slm-list-licenses-preview-message' }, + __('This block will display a list of licenses associated with the logged-in user.', 'slm-plus') + ), + createElement( + 'ul', + { className: 'slm-list-licenses-placeholder' }, + createElement('li', {}, __('License Key: ************', 'slm-plus')), + createElement('li', {}, __('Product: Example Product', 'slm-plus')), + createElement('li', {}, __('Status: Active', 'slm-plus')), + createElement('li', {}, __('Expiry Date: 2024-12-31', 'slm-plus')) + ) + ); + }, + + save: () => { + // Outputs the shortcode for rendering on the frontend. + return createElement('p', {}, '[slm_list_licenses]'); + }, +}); diff --git a/public/assets/js/slm-js.js b/public/assets/js/slm-js.js new file mode 100644 index 0000000..056fd75 --- /dev/null +++ b/public/assets/js/slm-js.js @@ -0,0 +1,20 @@ +function download(filename, text) { + var element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', filename); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +} + +// Start file download. +if (document.getElementById('export-lic-key') !=null) { + document.getElementById("export-lic-key").addEventListener("click", function () { + // Generate download of hello.txt file with some content + var license_data = this.getAttribute('data-licdata'); + var text = license_data; + var filename = "slm_license.json"; + download(filename, text); + }, false); +} \ No newline at end of file diff --git a/public/assets/js/slm.js b/public/assets/js/slm.js new file mode 100644 index 0000000..5c99be1 --- /dev/null +++ b/public/assets/js/slm.js @@ -0,0 +1,197 @@ +function download(filename, text) { + var element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', filename); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +} + +function slm_exportlicense(){ + var license_expt_id = document.getElementById("lic-json-data"); + var filelicname = license_expt_id.getAttribute('data-lickey'); + var license_data = document.getElementById("lic-json-data").textContent; + var text = license_data; + var filename = "license-" + filelicname + ".json"; + download(filename, text); +} + +jQuery(document).ready(function($) { + $('.user-search-input').on('input', function() { + const field = $(this).attr('id'); + const value = $(this).val(); + const suggestionsBox = $(`.user-search-suggestions[data-field="${field}"]`); + + if (value.length > 1) { // Trigger search on 2+ characters + $.ajax({ + url: ajaxurl, + method: 'POST', + data: { + action: 'slm_user_search', + value: value, + }, + success: function(response) { + suggestionsBox.empty(); // Clear previous suggestions + + if (response.data.length > 0) { + response.data.forEach(user => { + const suggestion = $('
    ') + .addClass('suggestion-item') + .text(`${user.first_name} ${user.last_name} (${user.email})`); + suggestion.data('user', user); + suggestionsBox.append(suggestion); + }); + suggestionsBox.show(); // Show suggestions box if results are found + } else { + suggestionsBox.hide(); // Hide suggestions box if no results + } + + // Handle suggestion click + $('.suggestion-item').on('click', function() { + const user = $(this).data('user'); + $('#user_id').val(user.ID); + $('#first_name').val(user.first_name); + $('#last_name').val(user.last_name); + $('#email').val(user.email); + $('#subscr_id').val(user.subscr_id); // Populate the subscr_id field + + // Populate company_name if available + if (user.company_name) { + $('#company_name').val(user.company_name); + } else { + $('#company_name').val(''); // Clear if no company name is available + } + + suggestionsBox.hide(); // Hide suggestions after selection + }); + }, + }); + } else { + suggestionsBox.hide(); // Hide suggestions if input length < 2 + } + }); + + // Hide suggestions if clicking outside of them + $(document).on('click', function(e) { + if (!$(e.target).closest('.user-search-input, .user-search-suggestions').length) { + $('.user-search-suggestions').hide(); + } + }); +}); + + +jQuery(document).ready(function($) { + let userSelectedDay = null; // Store manually set day of the month + + // Helper to calculate expiry date based on length, interval, and stored day + function calculateExpiryDate() { + // Exit if lifetime is selected + if ($('#lic_type').val() === 'lifetime') return; + + const dateCreated = new Date($('#date_created').val()); + const billingLength = parseInt($('#slm_billing_length').val()) || 0; + const billingInterval = $('#slm_billing_interval').val(); + + // Use user-selected day if it exists, otherwise default to the day in `dateCreated` + const dayToPreserve = userSelectedDay || dateCreated.getDate(); + + // Adjust expiry date based on interval and preserved day + let expiryDate = new Date(dateCreated); + if (billingInterval === 'years') { + expiryDate.setFullYear(dateCreated.getFullYear() + billingLength); + } else if (billingInterval === 'months') { + expiryDate.setMonth(dateCreated.getMonth() + billingLength); + } else if (billingInterval === 'days') { + expiryDate.setDate(dateCreated.getDate() + billingLength); + } + + // Set the day to the preserved day, adjusting for month-end overflow + expiryDate.setDate(Math.min(dayToPreserve, daysInMonth(expiryDate))); + + // Format and set expiry date + $('#date_expiry').val(expiryDate.toISOString().split('T')[0]); + } + + // Helper to calculate the number of days in a given month/year + function daysInMonth(date) { + return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); + } + + // Calculate interval and length based on expiry date + function calculateIntervalAndLengthFromExpiry() { + if ($('#lic_type').val() === 'lifetime') return; // Skip if lifetime + + const dateCreated = new Date($('#date_created').val()); + const dateExpiry = new Date($('#date_expiry').val()); + + if (dateExpiry < dateCreated) { + alert('Expiration date cannot be before the creation date.'); + $('#date_expiry').val($('#date_created').val()); // Reset to creation date + return; + } + + const diffTime = dateExpiry - dateCreated; + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + const diffMonths = Math.ceil(diffDays / 30); + const diffYears = Math.ceil(diffMonths / 12); + + if (diffYears >= 1) { + $('#slm_billing_interval').val('years'); + $('#slm_billing_length').val(diffYears); + } else if (diffMonths >= 1) { + $('#slm_billing_interval').val('months'); + $('#slm_billing_length').val(diffMonths); + } else { + $('#slm_billing_interval').val('days'); + $('#slm_billing_length').val(diffDays); + } + } + + // Adjust fields based on license type + function adjustFieldsBasedOnType() { + const licType = $('#lic_type').val(); + const isLifetime = licType === 'lifetime'; + + if (isLifetime) { + // Set expiration far in the future for lifetime licenses + const expiryDate = new Date(); + expiryDate.setFullYear(expiryDate.getFullYear() + 200); + $('#date_expiry').val(expiryDate.toISOString().split('T')[0]); + userSelectedDay = null; // Clear any selected day + } + + // Disable or enable fields based on license type + $('#date_expiry, #slm_billing_length, #slm_billing_interval, #date_renewed') + .prop('disabled', isLifetime) + .closest('tr').toggle(!isLifetime); + } + + // Track user-selected day when the expiration date is set manually + $('#date_expiry').on('change', function() { + const selectedDate = new Date($(this).val()); + userSelectedDay = selectedDate.getDate(); // Store selected day (e.g., 15) + calculateIntervalAndLengthFromExpiry(); // Update interval and length if needed + }); + + // Set today's date for date_created if new record and disable the field + const isEditRecord = window.location.search.includes('edit_record') || window.location.search.includes('slm_save_license'); + if (!isEditRecord) { + const today = new Date().toISOString().split('T')[0]; + $('#date_created').val(today).prop('disabled', true); + } + + // Recalculate expiry date when billing length or interval changes + $('#slm_billing_length, #slm_billing_interval').on('change', function() { + if ($('#lic_type').val() !== 'lifetime') { + calculateExpiryDate(); + } + }); + + // Attach change event to license type and initialize on page load + $('#lic_type').on('change', adjustFieldsBasedOnType); + adjustFieldsBasedOnType(); // Initialize + + // Calculate initial expiry date on page load for new records + calculateExpiryDate(); +}); diff --git a/software-license-manager/js/wplm-custom-admin.js b/public/assets/js/wplm-custom-admin.js old mode 100644 new mode 100755 similarity index 58% rename from software-license-manager/js/wplm-custom-admin.js rename to public/assets/js/wplm-custom-admin.js index fb6639a..ad40813 --- a/software-license-manager/js/wplm-custom-admin.js +++ b/public/assets/js/wplm-custom-admin.js @@ -1,11 +1,10 @@ -jQuery(document).ready(function($){ - //Add date picker listener on date fields - if ($.fn.datepicker){ - $('.wplm_pick_date').datepicker({ - dateFormat : 'yy-mm-dd' - }); - } - - //Add other admin side only jquery code below - -}); \ No newline at end of file +jQuery(document).ready(function($){ + //Add date picker listener on date fields + if ($.fn.datepicker){ + jQuery('.wplm_pick_date').datepicker({ + dateFormat : 'yy-mm-dd' + }); + } +}); + + diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..fe45c85 --- /dev/null +++ b/readme.txt @@ -0,0 +1,12 @@ +=== SLM Plus === +Contributors: Michel Velis, Tips and Tricks HQ +Donate link: http://paypal.me/mvelis +Tags: license, software license, woocommerce, license management +Requires at least: 5.6 +Tested up to: 6.7 +Stable tag: 6.3.5 +Tags: license manager, license key, license genrator, subscription +Requires PHP: 7.2 +License: GPLv2 or later +License URI: https://www.gnu.org/licenses/gpl-2.0.html +SLM Plus offers a powerful solution for managing licenses for WordPress plugins, themes, and web apps. With WooCommerce integration. diff --git a/slm-plus.php b/slm-plus.php new file mode 100644 index 0000000..f803664 --- /dev/null +++ b/slm-plus.php @@ -0,0 +1,88 @@ +prefix . "lic_key_tbl"); +define('SLM_TBL_EMAILS', $wpdb->prefix . "lic_emails_tbl"); +define('SLM_TBL_LIC_DOMAIN', $wpdb->prefix . "lic_reg_domain_tbl"); +define('SLM_TBL_LIC_DEVICES', $wpdb->prefix . "lic_reg_devices_tbl"); +define('SLM_TBL_LIC_LOG', $wpdb->prefix . "lic_log_tbl"); +define('SLM_TBL_LICENSE_STATUS', $wpdb->prefix . "lic_status_tbl"); + +define('SLM_MANAGEMENT_PERMISSION', 'manage_options'); +define('SLM_MAIN_MENU_SLUG', 'slm_overview'); +define('SLM_MENU_ICON', 'dashicons-lock'); +define('SLM_API_URL', SLM_SITE_URL); + +// Load core plugin functionalities +if (file_exists(SLM_LIB . 'slm-plugin-core.php')) { + require_once SLM_LIB . 'slm-plugin-core.php'; +} + +// Add settings link to plugin action links +function slm_settings_link($links) { + $settings_link = '' . __('Settings', 'slm-plus') . ''; + $links[] = $settings_link; + return $links; +} +add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'slm_settings_link'); + +// Define default max domains and devices +define('SLM_DEFAULT_MAX_DOMAINS', SLM_API_Utility::get_slm_option('default_max_domains')); +define('SLM_DEFAULT_MAX_DEVICES', SLM_API_Utility::get_slm_option('default_max_devices')); + +// Use native WordPress function for setting options +define('WOO_SLM_API_SECRET', SLM_API_Utility::get_slm_option('lic_creation_secret')); +define('KEY_API', SLM_API_Utility::get_slm_option('lic_creation_secret')); +define('VERIFY_KEY_API', SLM_API_Utility::get_slm_option('lic_verification_secret')); +define('KEY_API_PREFIX', SLM_API_Utility::get_slm_option('lic_prefix')); diff --git a/software-license-manager/client-side-examples/sample-php-scripts/create-license.php b/software-license-manager/client-side-examples/sample-php-scripts/create-license.php deleted file mode 100644 index 1c050e1..0000000 --- a/software-license-manager/client-side-examples/sample-php-scripts/create-license.php +++ /dev/null @@ -1,27 +0,0 @@ -'; - echo '

    Sample License Management

    '; - - /*** License activate button was clicked ***/ - if (isset($_REQUEST['activate_license'])) { - $license_key = $_REQUEST['sample_license_key']; - - // API query parameters - $api_params = array( - 'slm_action' => 'slm_activate', - 'secret_key' => YOUR_SPECIAL_SECRET_KEY, - 'license_key' => $license_key, - 'registered_domain' => $_SERVER['SERVER_NAME'], - 'item_reference' => urlencode(YOUR_ITEM_REFERENCE), - ); - - // Send query to the license manager server - $query = esc_url_raw(add_query_arg($api_params, YOUR_LICENSE_SERVER_URL)); - $response = wp_remote_get($query, array('timeout' => 20, 'sslverify' => false)); - - // Check for error in the response - if (is_wp_error($response)){ - echo "Unexpected Error! The query returned with an error."; - } - - //var_dump($response);//uncomment it if you want to look at the full response - - // License data. - $license_data = json_decode(wp_remote_retrieve_body($response)); - - // TODO - Do something with it. - //var_dump($license_data);//uncomment it to look at the data - - if($license_data->result == 'success'){//Success was returned for the license activation - - //Uncomment the followng line to see the message that returned from the license server - echo '
    The following message was returned from the server: '.$license_data->message; - - //Save the license key in the options table - update_option('sample_license_key', $license_key); - } - else{ - //Show error to the user. Probably entered incorrect license key. - - //Uncomment the followng line to see the message that returned from the license server - echo '
    The following message was returned from the server: '.$license_data->message; - } - - } - /*** End of license activation ***/ - - /*** License activate button was clicked ***/ - if (isset($_REQUEST['deactivate_license'])) { - $license_key = $_REQUEST['sample_license_key']; - - // API query parameters - $api_params = array( - 'slm_action' => 'slm_deactivate', - 'secret_key' => YOUR_SPECIAL_SECRET_KEY, - 'license_key' => $license_key, - 'registered_domain' => $_SERVER['SERVER_NAME'], - 'item_reference' => urlencode(YOUR_ITEM_REFERENCE), - ); - - // Send query to the license manager server - $query = esc_url_raw(add_query_arg($api_params, YOUR_LICENSE_SERVER_URL)); - $response = wp_remote_get($query, array('timeout' => 20, 'sslverify' => false)); - - // Check for error in the response - if (is_wp_error($response)){ - echo "Unexpected Error! The query returned with an error."; - } - - //var_dump($response);//uncomment it if you want to look at the full response - - // License data. - $license_data = json_decode(wp_remote_retrieve_body($response)); - - // TODO - Do something with it. - //var_dump($license_data);//uncomment it to look at the data - - if($license_data->result == 'success'){//Success was returned for the license activation - - //Uncomment the followng line to see the message that returned from the license server - echo '
    The following message was returned from the server: '.$license_data->message; - - //Remove the licensse key from the options table. It will need to be activated again. - update_option('sample_license_key', ''); - } - else{ - //Show error to the user. Probably entered incorrect license key. - - //Uncomment the followng line to see the message that returned from the license server - echo '
    The following message was returned from the server: '.$license_data->message; - } - - } - /*** End of sample license deactivation ***/ - - ?> -

    Please enter the license key for this product to activate it. You were given a license key when you purchased this item.

    -
    - - - - - -
    -

    - - -

    -
    - '; -} \ No newline at end of file diff --git a/software-license-manager/css/jquery-ui.css b/software-license-manager/css/jquery-ui.css deleted file mode 100644 index b775b44..0000000 --- a/software-license-manager/css/jquery-ui.css +++ /dev/null @@ -1,1225 +0,0 @@ -/*! jQuery UI - v1.11.0 - 2014-06-26 -* http://jqueryui.com -* Includes: core.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, draggable.css, menu.css, progressbar.css, resizable.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px -* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ - -/* Layout helpers -----------------------------------*/ -.ui-helper-hidden { - display: none; -} -.ui-helper-hidden-accessible { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} -.ui-helper-reset { - margin: 0; - padding: 0; - border: 0; - outline: 0; - line-height: 1.3; - text-decoration: none; - font-size: 100%; - list-style: none; -} -.ui-helper-clearfix:before, -.ui-helper-clearfix:after { - content: ""; - display: table; - border-collapse: collapse; -} -.ui-helper-clearfix:after { - clear: both; -} -.ui-helper-clearfix { - min-height: 0; /* support: IE7 */ -} -.ui-helper-zfix { - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - opacity: 0; - filter:Alpha(Opacity=0); -} - -.ui-front { - z-index: 100; -} - - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { - cursor: default !important; -} - - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - display: block; - text-indent: -99999px; - overflow: hidden; - background-repeat: no-repeat; -} - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.ui-accordion .ui-accordion-header { - display: block; - cursor: pointer; - position: relative; - margin: 2px 0 0 0; - padding: .5em .5em .5em .7em; - min-height: 0; /* support: IE7 */ - font-size: 100%; -} -.ui-accordion .ui-accordion-icons { - padding-left: 2.2em; -} -.ui-accordion .ui-accordion-icons .ui-accordion-icons { - padding-left: 2.2em; -} -.ui-accordion .ui-accordion-header .ui-accordion-header-icon { - position: absolute; - left: .5em; - top: 50%; - margin-top: -8px; -} -.ui-accordion .ui-accordion-content { - padding: 1em 2.2em; - border-top: 0; - overflow: auto; -} -.ui-autocomplete { - position: absolute; - top: 0; - left: 0; - cursor: default; -} -.ui-button { - display: inline-block; - position: relative; - padding: 0; - line-height: normal; - margin-right: .1em; - cursor: pointer; - vertical-align: middle; - text-align: center; - overflow: visible; /* removes extra width in IE */ -} -.ui-button, -.ui-button:link, -.ui-button:visited, -.ui-button:hover, -.ui-button:active { - text-decoration: none; -} -/* to make room for the icon, a width needs to be set here */ -.ui-button-icon-only { - width: 2.2em; -} -/* button elements seem to need a little more width */ -button.ui-button-icon-only { - width: 2.4em; -} -.ui-button-icons-only { - width: 3.4em; -} -button.ui-button-icons-only { - width: 3.7em; -} - -/* button text element */ -.ui-button .ui-button-text { - display: block; - line-height: normal; -} -.ui-button-text-only .ui-button-text { - padding: .4em 1em; -} -.ui-button-icon-only .ui-button-text, -.ui-button-icons-only .ui-button-text { - padding: .4em; - text-indent: -9999999px; -} -.ui-button-text-icon-primary .ui-button-text, -.ui-button-text-icons .ui-button-text { - padding: .4em 1em .4em 2.1em; -} -.ui-button-text-icon-secondary .ui-button-text, -.ui-button-text-icons .ui-button-text { - padding: .4em 2.1em .4em 1em; -} -.ui-button-text-icons .ui-button-text { - padding-left: 2.1em; - padding-right: 2.1em; -} -/* no icon support for input elements, provide padding by default */ -input.ui-button { - padding: .4em 1em; -} - -/* button icon element(s) */ -.ui-button-icon-only .ui-icon, -.ui-button-text-icon-primary .ui-icon, -.ui-button-text-icon-secondary .ui-icon, -.ui-button-text-icons .ui-icon, -.ui-button-icons-only .ui-icon { - position: absolute; - top: 50%; - margin-top: -8px; -} -.ui-button-icon-only .ui-icon { - left: 50%; - margin-left: -8px; -} -.ui-button-text-icon-primary .ui-button-icon-primary, -.ui-button-text-icons .ui-button-icon-primary, -.ui-button-icons-only .ui-button-icon-primary { - left: .5em; -} -.ui-button-text-icon-secondary .ui-button-icon-secondary, -.ui-button-text-icons .ui-button-icon-secondary, -.ui-button-icons-only .ui-button-icon-secondary { - right: .5em; -} - -/* button sets */ -.ui-buttonset { - margin-right: 7px; -} -.ui-buttonset .ui-button { - margin-left: 0; - margin-right: -.3em; -} - -/* workarounds */ -/* reset extra padding in Firefox, see h5bp.com/l */ -input.ui-button::-moz-focus-inner, -button.ui-button::-moz-focus-inner { - border: 0; - padding: 0; -} -.ui-datepicker { - width: 17em; - padding: .2em .2em 0; - display: none; -} -.ui-datepicker .ui-datepicker-header { - position: relative; - padding: .2em 0; -} -.ui-datepicker .ui-datepicker-prev, -.ui-datepicker .ui-datepicker-next { - position: absolute; - top: 2px; - width: 1.8em; - height: 1.8em; -} -.ui-datepicker .ui-datepicker-prev-hover, -.ui-datepicker .ui-datepicker-next-hover { - top: 1px; -} -.ui-datepicker .ui-datepicker-prev { - left: 2px; -} -.ui-datepicker .ui-datepicker-next { - right: 2px; -} -.ui-datepicker .ui-datepicker-prev-hover { - left: 1px; -} -.ui-datepicker .ui-datepicker-next-hover { - right: 1px; -} -.ui-datepicker .ui-datepicker-prev span, -.ui-datepicker .ui-datepicker-next span { - display: block; - position: absolute; - left: 50%; - margin-left: -8px; - top: 50%; - margin-top: -8px; -} -.ui-datepicker .ui-datepicker-title { - margin: 0 2.3em; - line-height: 1.8em; - text-align: center; -} -.ui-datepicker .ui-datepicker-title select { - font-size: 1em; - margin: 1px 0; -} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { - width: 49%; -} -.ui-datepicker table { - width: 100%; - font-size: .9em; - border-collapse: collapse; - margin: 0 0 .4em; -} -.ui-datepicker th { - padding: .7em .3em; - text-align: center; - font-weight: bold; - border: 0; -} -.ui-datepicker td { - border: 0; - padding: 1px; -} -.ui-datepicker td span, -.ui-datepicker td a { - display: block; - padding: .2em; - text-align: right; - text-decoration: none; -} -.ui-datepicker .ui-datepicker-buttonpane { - background-image: none; - margin: .7em 0 0 0; - padding: 0 .2em; - border-left: 0; - border-right: 0; - border-bottom: 0; -} -.ui-datepicker .ui-datepicker-buttonpane button { - float: right; - margin: .5em .2em .4em; - cursor: pointer; - padding: .2em .6em .3em .6em; - width: auto; - overflow: visible; -} -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { - float: left; -} - -/* with multiple calendars */ -.ui-datepicker.ui-datepicker-multi { - width: auto; -} -.ui-datepicker-multi .ui-datepicker-group { - float: left; -} -.ui-datepicker-multi .ui-datepicker-group table { - width: 95%; - margin: 0 auto .4em; -} -.ui-datepicker-multi-2 .ui-datepicker-group { - width: 50%; -} -.ui-datepicker-multi-3 .ui-datepicker-group { - width: 33.3%; -} -.ui-datepicker-multi-4 .ui-datepicker-group { - width: 25%; -} -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { - border-left-width: 0; -} -.ui-datepicker-multi .ui-datepicker-buttonpane { - clear: left; -} -.ui-datepicker-row-break { - clear: both; - width: 100%; - font-size: 0; -} - -/* RTL support */ -.ui-datepicker-rtl { - direction: rtl; -} -.ui-datepicker-rtl .ui-datepicker-prev { - right: 2px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next { - left: 2px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-prev:hover { - right: 1px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next:hover { - left: 1px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane { - clear: right; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button { - float: left; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, -.ui-datepicker-rtl .ui-datepicker-group { - float: right; -} -.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { - border-right-width: 0; - border-left-width: 1px; -} -.ui-dialog { - overflow: hidden; - position: absolute; - top: 0; - left: 0; - padding: .2em; - outline: 0; -} -.ui-dialog .ui-dialog-titlebar { - padding: .4em 1em; - position: relative; -} -.ui-dialog .ui-dialog-title { - float: left; - margin: .1em 0; - white-space: nowrap; - width: 90%; - overflow: hidden; - text-overflow: ellipsis; -} -.ui-dialog .ui-dialog-titlebar-close { - position: absolute; - right: .3em; - top: 50%; - width: 20px; - margin: -10px 0 0 0; - padding: 1px; - height: 20px; -} -.ui-dialog .ui-dialog-content { - position: relative; - border: 0; - padding: .5em 1em; - background: none; - overflow: auto; -} -.ui-dialog .ui-dialog-buttonpane { - text-align: left; - border-width: 1px 0 0 0; - background-image: none; - margin-top: .5em; - padding: .3em 1em .5em .4em; -} -.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { - float: right; -} -.ui-dialog .ui-dialog-buttonpane button { - margin: .5em .4em .5em 0; - cursor: pointer; -} -.ui-dialog .ui-resizable-se { - width: 12px; - height: 12px; - right: -5px; - bottom: -5px; - background-position: 16px 16px; -} -.ui-draggable .ui-dialog-titlebar { - cursor: move; -} -.ui-draggable-handle { - -ms-touch-action: none; - touch-action: none; -} -.ui-menu { - list-style: none; - padding: 0; - margin: 0; - display: block; - outline: none; -} -.ui-menu .ui-menu { - position: absolute; -} -.ui-menu .ui-menu-item { - position: relative; - margin: 0; - padding: 3px 1em 3px .4em; - cursor: pointer; - min-height: 0; /* support: IE7 */ - /* support: IE10, see #8844 */ - list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); -} -.ui-menu .ui-menu-divider { - margin: 5px 0; - height: 0; - font-size: 0; - line-height: 0; - border-width: 1px 0 0 0; -} -.ui-menu .ui-state-focus, -.ui-menu .ui-state-active { - margin: -1px; -} - -/* icon support */ -.ui-menu-icons { - position: relative; -} -.ui-menu-icons .ui-menu-item { - padding-left: 2em; -} - -/* left-aligned */ -.ui-menu .ui-icon { - position: absolute; - top: 0; - bottom: 0; - left: .2em; - margin: auto 0; -} - -/* right-aligned */ -.ui-menu .ui-menu-icon { - left: auto; - right: 0; -} -.ui-progressbar { - height: 2em; - text-align: left; - overflow: hidden; -} -.ui-progressbar .ui-progressbar-value { - margin: -1px; - height: 100%; -} -.ui-progressbar .ui-progressbar-overlay { - background: url("images/animated-overlay.gif"); - height: 100%; - filter: alpha(opacity=25); - opacity: 0.25; -} -.ui-progressbar-indeterminate .ui-progressbar-value { - background-image: none; -} -.ui-resizable { - position: relative; -} -.ui-resizable-handle { - position: absolute; - font-size: 0.1px; - display: block; - -ms-touch-action: none; - touch-action: none; -} -.ui-resizable-disabled .ui-resizable-handle, -.ui-resizable-autohide .ui-resizable-handle { - display: none; -} -.ui-resizable-n { - cursor: n-resize; - height: 7px; - width: 100%; - top: -5px; - left: 0; -} -.ui-resizable-s { - cursor: s-resize; - height: 7px; - width: 100%; - bottom: -5px; - left: 0; -} -.ui-resizable-e { - cursor: e-resize; - width: 7px; - right: -5px; - top: 0; - height: 100%; -} -.ui-resizable-w { - cursor: w-resize; - width: 7px; - left: -5px; - top: 0; - height: 100%; -} -.ui-resizable-se { - cursor: se-resize; - width: 12px; - height: 12px; - right: 1px; - bottom: 1px; -} -.ui-resizable-sw { - cursor: sw-resize; - width: 9px; - height: 9px; - left: -5px; - bottom: -5px; -} -.ui-resizable-nw { - cursor: nw-resize; - width: 9px; - height: 9px; - left: -5px; - top: -5px; -} -.ui-resizable-ne { - cursor: ne-resize; - width: 9px; - height: 9px; - right: -5px; - top: -5px; -} -.ui-selectable { - -ms-touch-action: none; - touch-action: none; -} -.ui-selectable-helper { - position: absolute; - z-index: 100; - border: 1px dotted black; -} -.ui-selectmenu-menu { - padding: 0; - margin: 0; - position: absolute; - top: 0; - left: 0; - display: none; -} -.ui-selectmenu-menu .ui-menu { - overflow: auto; - /* Support: IE7 */ - overflow-x: hidden; - padding-bottom: 1px; -} -.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { - font-size: 1em; - font-weight: bold; - line-height: 1.5; - padding: 2px 0.4em; - margin: 0.5em 0 0 0; - height: auto; - border: 0; -} -.ui-selectmenu-open { - display: block; -} -.ui-selectmenu-button { - display: inline-block; - overflow: hidden; - position: relative; - text-decoration: none; - cursor: pointer; -} -.ui-selectmenu-button span.ui-icon { - right: 0.5em; - left: auto; - margin-top: -8px; - position: absolute; - top: 50%; -} -.ui-selectmenu-button span.ui-selectmenu-text { - text-align: left; - padding: 0.4em 2.1em 0.4em 1em; - display: block; - line-height: 1.4; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ui-slider { - position: relative; - text-align: left; -} -.ui-slider .ui-slider-handle { - position: absolute; - z-index: 2; - width: 1.2em; - height: 1.2em; - cursor: default; - -ms-touch-action: none; - touch-action: none; -} -.ui-slider .ui-slider-range { - position: absolute; - z-index: 1; - font-size: .7em; - display: block; - border: 0; - background-position: 0 0; -} - -/* For IE8 - See #6727 */ -.ui-slider.ui-state-disabled .ui-slider-handle, -.ui-slider.ui-state-disabled .ui-slider-range { - filter: inherit; -} - -.ui-slider-horizontal { - height: .8em; -} -.ui-slider-horizontal .ui-slider-handle { - top: -.3em; - margin-left: -.6em; -} -.ui-slider-horizontal .ui-slider-range { - top: 0; - height: 100%; -} -.ui-slider-horizontal .ui-slider-range-min { - left: 0; -} -.ui-slider-horizontal .ui-slider-range-max { - right: 0; -} - -.ui-slider-vertical { - width: .8em; - height: 100px; -} -.ui-slider-vertical .ui-slider-handle { - left: -.3em; - margin-left: 0; - margin-bottom: -.6em; -} -.ui-slider-vertical .ui-slider-range { - left: 0; - width: 100%; -} -.ui-slider-vertical .ui-slider-range-min { - bottom: 0; -} -.ui-slider-vertical .ui-slider-range-max { - top: 0; -} -.ui-sortable-handle { - -ms-touch-action: none; - touch-action: none; -} -.ui-spinner { - position: relative; - display: inline-block; - overflow: hidden; - padding: 0; - vertical-align: middle; -} -.ui-spinner-input { - border: none; - background: none; - color: inherit; - padding: 0; - margin: .2em 0; - vertical-align: middle; - margin-left: .4em; - margin-right: 22px; -} -.ui-spinner-button { - width: 16px; - height: 50%; - font-size: .5em; - padding: 0; - margin: 0; - text-align: center; - position: absolute; - cursor: default; - display: block; - overflow: hidden; - right: 0; -} -/* more specificity required here to override default borders */ -.ui-spinner a.ui-spinner-button { - border-top: none; - border-bottom: none; - border-right: none; -} -/* vertically center icon */ -.ui-spinner .ui-icon { - position: absolute; - margin-top: -8px; - top: 50%; - left: 0; -} -.ui-spinner-up { - top: 0; -} -.ui-spinner-down { - bottom: 0; -} - -/* TR overrides */ -.ui-spinner .ui-icon-triangle-1-s { - /* need to fix icons sprite */ - background-position: -65px -16px; -} -.ui-tabs { - position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ - padding: .2em; -} -.ui-tabs .ui-tabs-nav { - margin: 0; - padding: .2em .2em 0; -} -.ui-tabs .ui-tabs-nav li { - list-style: none; - float: left; - position: relative; - top: 0; - margin: 1px .2em 0 0; - border-bottom-width: 0; - padding: 0; - white-space: nowrap; -} -.ui-tabs .ui-tabs-nav .ui-tabs-anchor { - float: left; - padding: .5em 1em; - text-decoration: none; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active { - margin-bottom: -1px; - padding-bottom: 1px; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { - cursor: text; -} -.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { - cursor: pointer; -} -.ui-tabs .ui-tabs-panel { - display: block; - border-width: 0; - padding: 1em 1.4em; - background: none; -} -.ui-tooltip { - padding: 8px; - position: absolute; - z-index: 9999; - max-width: 300px; - -webkit-box-shadow: 0 0 5px #aaa; - box-shadow: 0 0 5px #aaa; -} -body .ui-tooltip { - border-width: 2px; -} - -/* Component containers -----------------------------------*/ -.ui-widget { - font-family: Verdana,Arial,sans-serif; - font-size: 1.1em; -} -.ui-widget .ui-widget { - font-size: 1em; -} -.ui-widget input, -.ui-widget select, -.ui-widget textarea, -.ui-widget button { - font-family: Verdana,Arial,sans-serif; - font-size: 1em; -} -.ui-widget-content { - border: 1px solid #aaaaaa; - background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x; - color: #222222; -} -.ui-widget-content a { - color: #222222; -} -.ui-widget-header { - border: 1px solid #aaaaaa; - background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x; - color: #222222; - font-weight: bold; -} -.ui-widget-header a { - color: #222222; -} - -/* Interaction states -----------------------------------*/ -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default { - border: 1px solid #d3d3d3; - background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #555555; -} -.ui-state-default a, -.ui-state-default a:link, -.ui-state-default a:visited { - color: #555555; - text-decoration: none; -} -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-widget-header .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus { - border: 1px solid #999999; - background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #212121; -} -.ui-state-hover a, -.ui-state-hover a:hover, -.ui-state-hover a:link, -.ui-state-hover a:visited, -.ui-state-focus a, -.ui-state-focus a:hover, -.ui-state-focus a:link, -.ui-state-focus a:visited { - color: #212121; - text-decoration: none; -} -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active { - border: 1px solid #aaaaaa; - background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #212121; -} -.ui-state-active a, -.ui-state-active a:link, -.ui-state-active a:visited { - color: #212121; - text-decoration: none; -} - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { - border: 1px solid #fcefa1; - background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x; - color: #363636; -} -.ui-state-highlight a, -.ui-widget-content .ui-state-highlight a, -.ui-widget-header .ui-state-highlight a { - color: #363636; -} -.ui-state-error, -.ui-widget-content .ui-state-error, -.ui-widget-header .ui-state-error { - border: 1px solid #cd0a0a; - background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x; - color: #cd0a0a; -} -.ui-state-error a, -.ui-widget-content .ui-state-error a, -.ui-widget-header .ui-state-error a { - color: #cd0a0a; -} -.ui-state-error-text, -.ui-widget-content .ui-state-error-text, -.ui-widget-header .ui-state-error-text { - color: #cd0a0a; -} -.ui-priority-primary, -.ui-widget-content .ui-priority-primary, -.ui-widget-header .ui-priority-primary { - font-weight: bold; -} -.ui-priority-secondary, -.ui-widget-content .ui-priority-secondary, -.ui-widget-header .ui-priority-secondary { - opacity: .7; - filter:Alpha(Opacity=70); - font-weight: normal; -} -.ui-state-disabled, -.ui-widget-content .ui-state-disabled, -.ui-widget-header .ui-state-disabled { - opacity: .35; - filter:Alpha(Opacity=35); - background-image: none; -} -.ui-state-disabled .ui-icon { - filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ -} - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - width: 16px; - height: 16px; -} -.ui-icon, -.ui-widget-content .ui-icon { - background-image: url("images/ui-icons_222222_256x240.png"); -} -.ui-widget-header .ui-icon { - background-image: url("images/ui-icons_222222_256x240.png"); -} -.ui-state-default .ui-icon { - background-image: url("images/ui-icons_888888_256x240.png"); -} -.ui-state-hover .ui-icon, -.ui-state-focus .ui-icon { - background-image: url("images/ui-icons_454545_256x240.png"); -} -.ui-state-active .ui-icon { - background-image: url("images/ui-icons_454545_256x240.png"); -} -.ui-state-highlight .ui-icon { - background-image: url("images/ui-icons_2e83ff_256x240.png"); -} -.ui-state-error .ui-icon, -.ui-state-error-text .ui-icon { - background-image: url("images/ui-icons_cd0a0a_256x240.png"); -} - -/* positioning */ -.ui-icon-blank { background-position: 16px 16px; } -.ui-icon-carat-1-n { background-position: 0 0; } -.ui-icon-carat-1-ne { background-position: -16px 0; } -.ui-icon-carat-1-e { background-position: -32px 0; } -.ui-icon-carat-1-se { background-position: -48px 0; } -.ui-icon-carat-1-s { background-position: -64px 0; } -.ui-icon-carat-1-sw { background-position: -80px 0; } -.ui-icon-carat-1-w { background-position: -96px 0; } -.ui-icon-carat-1-nw { background-position: -112px 0; } -.ui-icon-carat-2-n-s { background-position: -128px 0; } -.ui-icon-carat-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -64px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -64px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 0 -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-on { background-position: -96px -144px; } -.ui-icon-radio-off { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, -.ui-corner-top, -.ui-corner-left, -.ui-corner-tl { - border-top-left-radius: 4px; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-right, -.ui-corner-tr { - border-top-right-radius: 4px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-left, -.ui-corner-bl { - border-bottom-left-radius: 4px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-right, -.ui-corner-br { - border-bottom-right-radius: 4px; -} - -/* Overlays */ -.ui-widget-overlay { - background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; - opacity: .3; - filter: Alpha(Opacity=30); -} -.ui-widget-shadow { - margin: -8px 0 0 -8px; - padding: 8px; - background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; - opacity: .3; - filter: Alpha(Opacity=30); - border-radius: 8px; -} diff --git a/software-license-manager/includes/slm-api-listener.php b/software-license-manager/includes/slm-api-listener.php deleted file mode 100644 index 065e5f3..0000000 --- a/software-license-manager/includes/slm-api-listener.php +++ /dev/null @@ -1,233 +0,0 @@ -creation_api_listener(); - $this->activation_api_listener(); - $this->deactivation_api_listener(); - $this->check_api_listener(); - } - } - - function creation_api_listener() { - if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) == 'slm_create_new') { - //Handle the licene creation API query - global $slm_debug_logger; - - $options = get_option('slm_plugin_options'); - $lic_key_prefix = $options['lic_prefix']; - - SLM_API_Utility::verify_secret_key_for_creation(); //Verify the secret key first. - - $slm_debug_logger->log_debug("API - license creation (slm_create_new) request received."); - - //Action hook - do_action('slm_api_listener_slm_create_new'); - - $fields = array(); - if (isset($_REQUEST['license_key']) && !empty($_REQUEST['license_key'])){ - $fields['license_key'] = strip_tags($_REQUEST['license_key']);//Use the key you pass via the request - }else{ - $fields['license_key'] = uniqid($lic_key_prefix);//Use random generated key - } - $fields['lic_status'] = 'pending'; - $fields['first_name'] = wp_unslash(strip_tags($_REQUEST['first_name'])); - $fields['last_name'] = wp_unslash(strip_tags($_REQUEST['last_name'])); - $fields['email'] = strip_tags($_REQUEST['email']); - $fields['company_name'] = wp_unslash(strip_tags($_REQUEST['company_name'])); - $fields['txn_id'] = strip_tags($_REQUEST['txn_id']); - if (empty($_REQUEST['max_allowed_domains'])) { - $fields['max_allowed_domains'] = $options['default_max_domains']; - } else { - $fields['max_allowed_domains'] = strip_tags($_REQUEST['max_allowed_domains']); - } - $fields['date_created'] = isset($_REQUEST['date_created'])?strip_tags($_REQUEST['date_created']):date("Y-m-d"); - $fields['date_expiry'] = isset($_REQUEST['date_expiry'])?strip_tags($_REQUEST['date_expiry']):''; - - global $wpdb; - $tbl_name = SLM_TBL_LICENSE_KEYS; - $result = $wpdb->insert($tbl_name, $fields); - if ($result === false) { - //error inserting - $args = (array('result' => 'error', 'message' => 'License creation failed', 'error_code' => SLM_Error_Codes::CREATE_FAILED)); - SLM_API_Utility::output_api_response($args); - } else { - $args = (array('result' => 'success', 'message' => 'License successfully created', 'key' => $fields['license_key'], 'error_code' => SLM_Error_Codes::CREATE_FAILED)); - SLM_API_Utility::output_api_response($args); - } - } - } - - /* - * Query Parameters - * 1) slm_action = slm_create_new - * 2) secret_key - * 3) license_key - * 4) registered_domain (optional) - */ - - function activation_api_listener() { - if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) == 'slm_activate') { - //Handle the license activation API query - global $slm_debug_logger; - - SLM_API_Utility::verify_secret_key(); //Verify the secret key first. - - $slm_debug_logger->log_debug("API - license activation (slm_activate) request received."); - - //Action hook - do_action('slm_api_listener_slm_activate'); - - $fields = array(); - $fields['lic_key'] = trim(strip_tags($_REQUEST['license_key'])); - $fields['registered_domain'] = trim(wp_unslash(strip_tags($_REQUEST['registered_domain']))); //gethostbyaddr($_SERVER['REMOTE_ADDR']); - $fields['item_reference'] = trim(strip_tags($_REQUEST['item_reference'])); - $slm_debug_logger->log_debug("License key: " . $fields['lic_key'] . " Domain: " . $fields['registered_domain']); - - global $wpdb; - $tbl_name = SLM_TBL_LICENSE_KEYS; - $reg_table = SLM_TBL_LIC_DOMAIN; - $key = $fields['lic_key']; - $sql_prep1 = $wpdb->prepare("SELECT * FROM $tbl_name WHERE license_key = %s", $key); - $retLic = $wpdb->get_row($sql_prep1, OBJECT); - - $sql_prep2 = $wpdb->prepare("SELECT * FROM $reg_table WHERE lic_key = %s", $key); - $reg_domains = $wpdb->get_results($sql_prep2, OBJECT); - if ($retLic) { - if ($retLic->lic_status == 'blocked') { - $args = (array('result' => 'error', 'message' => 'Your License key is blocked', 'error_code' => SLM_Error_Codes::LICENSE_BLOCKED)); - SLM_API_Utility::output_api_response($args); - } elseif ($retLic->lic_status == 'expired') { - $args = (array('result' => 'error', 'message' => 'Your License key has expired', 'error_code' => SLM_Error_Codes::LICENSE_EXPIRED)); - SLM_API_Utility::output_api_response($args); - } - - if (count($reg_domains) < floor($retLic->max_allowed_domains)) { - foreach ($reg_domains as $reg_domain) { - if (isset($_REQUEST['migrate_from']) && (trim($_REQUEST['migrate_from']) == $reg_domain->registered_domain)) { - $wpdb->update($reg_table, array('registered_domain' => $fields['registered_domain']), array('registered_domain' => trim(strip_tags($_REQUEST['migrate_from'])))); - $args = (array('result' => 'success', 'message' => 'Registered domain has been updated')); - SLM_API_Utility::output_api_response($args); - } - if ($fields['registered_domain'] == $reg_domain->registered_domain) { - $args = (array('result' => 'error', 'message' => 'License key already in use on ' . $reg_domain->registered_domain, 'error_code' => SLM_Error_Codes::LICENSE_IN_USE)); - SLM_API_Utility::output_api_response($args); - } - } - $fields['lic_key_id'] = $retLic->id; - $wpdb->insert($reg_table, $fields); - - $slm_debug_logger->log_debug("Updating license key status to active."); - $data = array('lic_status' => 'active'); - $where = array('id' => $retLic->id); - $updated = $wpdb->update($tbl_name, $data, $where); - - $args = (array('result' => 'success', 'message' => 'License key activated')); - SLM_API_Utility::output_api_response($args); - } else { - $args = (array('result' => 'error', 'message' => 'Reached maximum allowable domains', 'error_code' => SLM_Error_Codes::REACHED_MAX_DOMAINS)); - SLM_API_Utility::output_api_response($args); - } - } else { - $args = (array('result' => 'error', 'message' => 'Invalid license key', 'error_code' => SLM_Error_Codes::LICENSE_INVALID)); - SLM_API_Utility::output_api_response($args); - } - } - } - - function deactivation_api_listener() { - if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) == 'slm_deactivate') { - //Handle the license deactivation API query - global $slm_debug_logger; - - SLM_API_Utility::verify_secret_key(); //Verify the secret key first. - - $slm_debug_logger->log_debug("API - license deactivation (slm_deactivate) request received."); - - //Action hook - do_action('slm_api_listener_slm_deactivate'); - - if (empty($_REQUEST['registered_domain'])) { - $args = (array('result' => 'error', 'message' => 'Registered domain information is missing', 'error_code' => SLM_Error_Codes::DOMAIN_MISSING)); - SLM_API_Utility::output_api_response($args); - } - $registered_domain = trim(wp_unslash(strip_tags($_REQUEST['registered_domain']))); - $license_key = trim(strip_tags($_REQUEST['license_key'])); - $slm_debug_logger->log_debug("License key: " . $license_key . " Domain: " . $registered_domain); - - global $wpdb; - $registered_dom_table = SLM_TBL_LIC_DOMAIN; - $sql_prep = $wpdb->prepare("DELETE FROM $registered_dom_table WHERE lic_key=%s AND registered_domain=%s", $license_key, $registered_domain); - $delete = $wpdb->query($sql_prep); - if ($delete === false) { - $slm_debug_logger->log_debug("Error - failed to delete the registered domain from the database."); - } else if ($delete == 0) { - $args = (array('result' => 'error', 'message' => 'The license key on this domain is already inactive', 'error_code' => SLM_Error_Codes::DOMAIN_ALREADY_INACTIVE)); - SLM_API_Utility::output_api_response($args); - } else { - $args = (array('result' => 'success', 'message' => 'The license key has been deactivated for this domain')); - SLM_API_Utility::output_api_response($args); - } - } - } - - function check_api_listener() { - if (isset($_REQUEST['slm_action']) && trim($_REQUEST['slm_action']) == 'slm_check') { - //Handle the license check API query - global $slm_debug_logger; - - SLM_API_Utility::verify_secret_key(); //Verify the secret key first. - - $slm_debug_logger->log_debug("API - license check (slm_check) request received."); - - $fields = array(); - $fields['lic_key'] = trim(strip_tags($_REQUEST['license_key'])); - $slm_debug_logger->log_debug("License key: " . $fields['lic_key']); - - //Action hook - do_action('slm_api_listener_slm_check'); - - global $wpdb; - $tbl_name = SLM_TBL_LICENSE_KEYS; - $reg_table = SLM_TBL_LIC_DOMAIN; - $key = $fields['lic_key']; - $sql_prep1 = $wpdb->prepare("SELECT * FROM $tbl_name WHERE license_key = %s", $key); - $retLic = $wpdb->get_row($sql_prep1, OBJECT); - - $sql_prep2 = $wpdb->prepare("SELECT * FROM $reg_table WHERE lic_key = %s", $key); - $reg_domains = $wpdb->get_results($sql_prep2, OBJECT); - if ($retLic) {//A license key exists - $args = (array( - 'result' => 'success', - 'message' => 'License key details retrieved.', - 'status' => $retLic->lic_status, - 'max_allowed_domains' => $retLic->max_allowed_domains, - 'email' => $retLic->email, - 'registered_domains' => $reg_domains, - 'date_created' => $retLic->date_created, - 'date_renewed' => $retLic->date_renewed, - 'date_expiry' => $retLic->date_expiry, - )); - //Output the license details - SLM_API_Utility::output_api_response($args); - } else { - $args = (array('result' => 'error', 'message' => 'Invalid license key', 'error_code' => SLM_Error_Codes::LICENSE_INVALID)); - SLM_API_Utility::output_api_response($args); - } - } - } - -} \ No newline at end of file diff --git a/software-license-manager/includes/slm-api-utility.php b/software-license-manager/includes/slm-api-utility.php deleted file mode 100644 index 61b3e2f..0000000 --- a/software-license-manager/includes/slm-api-utility.php +++ /dev/null @@ -1,57 +0,0 @@ -log_debug('API Response - Result: ' . $args['result'] . ' Message: ' . $args['message']); - - //Send response - echo json_encode($args); - exit(0); - } - - static function verify_secret_key() { - $slm_options = get_option('slm_plugin_options'); - $right_secret_key = $slm_options['lic_verification_secret']; - $received_secret_key = strip_tags($_REQUEST['secret_key']); - if ($received_secret_key != $right_secret_key) { - $args = (array('result' => 'error', 'message' => 'Verification API secret key is invalid', 'error_code' => SLM_Error_Codes::VERIFY_KEY_INVALID)); - SLM_API_Utility::output_api_response($args); - } - } - - static function verify_secret_key_for_creation() { - $slm_options = get_option('slm_plugin_options'); - $right_secret_key = $slm_options['lic_creation_secret']; - $received_secret_key = strip_tags($_REQUEST['secret_key']); - if ($received_secret_key != $right_secret_key) { - $args = (array('result' => 'error', 'message' => 'License Creation API secret key is invalid', 'error_code' => SLM_Error_Codes::CREATE_KEY_INVALID)); - SLM_API_Utility::output_api_response($args); - } - } - - static function insert_license_data_internal($fields) { - /* The fields array should have values for the following keys - //$fields['license_key'] - //$fields['lic_status'] - //$fields['first_name'] - //$fields['last_name'] - //$fields['email'] - //$fields['company_name'] - //$fields['txn_id'] - //$fields['max_allowed_domains'] - */ - global $wpdb; - $tbl_name = SLM_TBL_LICENSE_KEYS; - $fields = array_filter($fields);//Remove any null values. - $result = $wpdb->insert($tbl_name, $fields); - } - -} \ No newline at end of file diff --git a/software-license-manager/includes/slm-debug-logger.php b/software-license-manager/includes/slm-debug-logger.php deleted file mode 100644 index f0ab6ae..0000000 --- a/software-license-manager/includes/slm-debug-logger.php +++ /dev/null @@ -1,115 +0,0 @@ -log_debug("Some debug message"); - * - * OR - * - * SLM_Debug_Logger::log_debug_st("Some debug message"); - */ - -class SLM_Debug_Logger -{ - var $log_folder_path; - var $default_log_file = 'log.txt'; - var $default_log_file_cron = 'log-cron-job.txt'; - var $debug_enabled = false; - var $debug_status = array('SUCCESS','STATUS','NOTICE','WARNING','FAILURE','CRITICAL'); - var $section_break_marker = "\n----------------------------------------------------------\n\n"; - var $log_reset_marker = "-------- Log File Reset --------\n"; - - function __construct() - { - $this->log_folder_path = WP_LICENSE_MANAGER_PATH . '/logs'; - //Check config and if debug is enabled then set the enabled flag to true - $options = get_option('slm_plugin_options'); - if(!empty($options['enable_debug'])){//Debugging is enabled - $this->debug_enabled = true; - } - } - - function get_debug_timestamp() - { - return '['.date('m/d/Y g:i A').'] - '; - } - - function get_debug_status($level) - { - $size = count($this->debug_status); - if($level >= $size){ - return 'UNKNOWN'; - } - else{ - return $this->debug_status[$level]; - } - } - - function get_section_break($section_break) - { - if ($section_break) { - return $this->section_break_marker; - } - return ""; - } - - function reset_log_file($file_name='') - { - if(empty($file_name)){ - $file_name = $this->default_log_file; - } - $debug_log_file = $this->log_folder_path.'/'.$file_name; - $content = $this->get_debug_timestamp().$this->log_reset_marker; - $fp=fopen($debug_log_file,'w'); - fwrite($fp, $content); - fclose($fp); - } - - function append_to_file($content,$file_name) - { - if(empty($file_name))$file_name = $this->default_log_file; - $debug_log_file = $this->log_folder_path.'/'.$file_name; - $fp=fopen($debug_log_file,'a'); - fwrite($fp, $content); - fclose($fp); - } - - function log_debug($message,$level=0,$section_break=false,$file_name='') - { - if (!$this->debug_enabled) return; - $content = $this->get_debug_timestamp();//Timestamp - $content .= $this->get_debug_status($level);//Debug status - $content .= ' : '; - $content .= $message . "\n"; - $content .= $this->get_section_break($section_break); - $this->append_to_file($content, $file_name); - } - - function log_debug_cron($message,$level=0,$section_break=false) - { - if (!$this->debug_enabled) return; - $content = $this->get_debug_timestamp();//Timestamp - $content .= $this->get_debug_status($level);//Debug status - $content .= ' : '; - $content .= $message . "\n"; - $content .= $this->get_section_break($section_break); - //$file_name = $this->default_log_file_cron; - $this->append_to_file($content, $this->default_log_file_cron); - } - - static function log_debug_st($message,$level=0,$section_break=false,$file_name='') - { - $options = get_option('slm_plugin_options'); - if(empty($options['enable_debug'])){//Debugging is disabled - return; - } - $content = '['.date('m/d/Y g:i A').'] - STATUS : '. $message . "\n"; - $debug_log_file = WP_LICENSE_MANAGER_PATH . '/logs/log.txt'; - $fp=fopen($debug_log_file,'a'); - fwrite($fp, $content); - fclose($fp); - } - -} \ No newline at end of file diff --git a/software-license-manager/includes/slm-error-codes.php b/software-license-manager/includes/slm-error-codes.php deleted file mode 100644 index c501d5d..0000000 --- a/software-license-manager/includes/slm-error-codes.php +++ /dev/null @@ -1,19 +0,0 @@ -load_scripts(); - - //Add other init time operations here - add_action ('slm_daily_cron_event', array(&$this, 'slm_daily_cron_event_handler')); - } - - function load_scripts() - { - //Load all common scripts and styles only - wp_enqueue_script('jquery'); - - //Load all admin side scripts and styles only - if(is_admin()) - { - wp_enqueue_script('jquery-ui-datepicker'); - wp_enqueue_script('wplm-custom-admin-js', WP_LICENSE_MANAGER_URL . '/js/wplm-custom-admin.js', array( 'jquery-ui-dialog' ));//admin only custom js code - - if (isset($_GET['page']) && $_GET['page'] == 'wp_lic_mgr_addedit') {//Only include if we are in the license add/edit interface - wp_enqueue_style('jquery-ui-style', WP_LICENSE_MANAGER_URL .'/css/jquery-ui.css'); - } - //wp_enqueue_style('dialogStylesheet', includes_url().'css/jquery-ui-dialog.css'); - } - } - - function slm_daily_cron_event_handler() - { - $options = get_option('slm_plugin_options'); - - do_action('slm_daily_cron_event_triggered'); - - if ($options['enable_auto_key_expiry'] == '1'){ - //Do the auto key expiry task - SLM_Debug_Logger::log_debug_st("SLM daily cronjob - auto expiry of license key is enabled."); - SLM_Utility::do_auto_key_expiry(); - } - - //Do any ohter daily cronjob tasks. - - } - -}//End of class \ No newline at end of file diff --git a/software-license-manager/includes/slm-third-party-integration.php b/software-license-manager/includes/slm-third-party-integration.php deleted file mode 100644 index 8e06e33..0000000 --- a/software-license-manager/includes/slm-third-party-integration.php +++ /dev/null @@ -1,264 +0,0 @@ -log_debug("WP eStore integration - checking if a license key needs to be created for this transaction."); - $products_table_name = $wpdb->prefix . "wp_eStore_tbl"; - $slm_data = ""; - - foreach ($cart_items as $current_cart_item) { - $prod_id = $current_cart_item['item_number']; - $qty = $current_cart_item['quantity']; - $retrieved_product = $wpdb->get_row("SELECT * FROM $products_table_name WHERE id = '$prod_id'", OBJECT); - $package_product = eStore_is_package_product($retrieved_product); - if ($package_product) { - $slm_debug_logger->log_debug('Checking license key generation for package/bundle product.'); - $product_ids = explode(',', $retrieved_product->product_download_url); - foreach ($product_ids as $id) { - $id = trim($id); - $retrieved_product_for_specific_id = $wpdb->get_row("SELECT * FROM $products_table_name WHERE id = '$id'", OBJECT); - $slm_data .= slm_estore_check_and_generate_key($retrieved_product_for_specific_id, $payment_data, $cart_items, $qty); - } - } else { - $slm_debug_logger->log_debug('Checking license key generation for single item product.'); - $slm_data .= slm_estore_check_and_generate_key($retrieved_product, $payment_data, $cart_items, $qty); - } - } - - $body = str_replace("{slm_data}", $slm_data, $body); - return $body; -} - -function slm_estore_check_and_generate_key($retrieved_product, $payment_data, $cart_items, $qty=1) { - global $slm_debug_logger; - $license_data = ''; - - if ($retrieved_product->create_license == 1) { - $requested_qty = (int)$qty; - $slm_debug_logger->log_debug('Need to create a license key for this product: ' . $retrieved_product->id . '. Requested qty: ' . $requested_qty); - if($requested_qty > 1){ - //More than 1 qty of the same product - for($i=0; $i < $requested_qty; $i++){ - $slm_key = slm_estore_create_license($retrieved_product, $payment_data, $cart_items); - $license_data .= "\n" . __('Item Name: ', 'slm') . $retrieved_product->name . " - " . __('License Key '.($i+1).': ', 'slm') . $slm_key; - } - } - else { - //Standard 1 qty - $slm_key = slm_estore_create_license($retrieved_product, $payment_data, $cart_items); - $license_data = "\n" . __('Item Name: ', 'slm') . $retrieved_product->name . " - " . __('License Key: ', 'slm') . $slm_key; - } - - $slm_debug_logger->log_debug('Liense data: ' . $license_data); - $license_data = apply_filters('slm_estore_item_license_data', $license_data); - } - return $license_data; -} - -function slm_estore_create_license($retrieved_product, $payment_data, $cart_items) { - global $slm_debug_logger; - global $wpdb; - $product_meta_table_name = WP_ESTORE_PRODUCTS_META_TABLE_NAME; - - //Retrieve the default settings values. - $options = get_option('slm_plugin_options'); - $lic_key_prefix = $options['lic_prefix']; - $max_domains = $options['default_max_domains']; - - //Lets check any product specific configuration. - $prod_id = $retrieved_product->id; - $product_meta = $wpdb->get_row("SELECT * FROM $product_meta_table_name WHERE prod_id = '$prod_id' AND meta_key='slm_max_allowed_domains'", OBJECT); - if ($product_meta) { - //Found product specific SLM config data. - $max_domains = $product_meta->meta_value; - } else { - //Use the default value from settings (the $max_domains variable contains the default value already). - } - //Lets check if any product specific expiry date is set - $product_meta = $wpdb->get_row("SELECT * FROM $product_meta_table_name WHERE prod_id = '$prod_id' AND meta_key='slm_date_of_expiry'", OBJECT); - if ($product_meta) { - //Found product specific SLM config data. - $num_days_before_expiry = $product_meta->meta_value; - $slm_date_of_expiry = date('Y-m-d', strtotime('+'.$num_days_before_expiry.' days')); - } else { - //Use the default value (1 year from today). - $current_date_plus_1year = date('Y-m-d', strtotime('+1 year')); - $slm_date_of_expiry = $current_date_plus_1year; - } - - - $fields = array(); - $fields['license_key'] = uniqid($lic_key_prefix); - $fields['lic_status'] = 'pending'; - $fields['first_name'] = $payment_data['first_name']; - $fields['last_name'] = $payment_data['last_name']; - $fields['email'] = $payment_data['payer_email']; - $fields['company_name'] = $payment_data['company_name']; - $fields['txn_id'] = $payment_data['txn_id']; - $fields['max_allowed_domains'] = $max_domains; - $fields['date_created'] = date("Y-m-d"); //Today's date - $fields['date_expiry'] = $slm_date_of_expiry; - - $slm_debug_logger->log_debug('Inserting license data into the license manager DB table.'); - $fields = array_filter($fields); //Remove any null values. - - - $tbl_name = SLM_TBL_LICENSE_KEYS; - $result = $wpdb->insert($tbl_name, $fields); - if (!$result) { - $slm_debug_logger->log_debug('Notice! initial database table insert failed on license key table (User Email: ' . $fields['email'] . '). Trying again by converting charset', true); - //Convert the default PayPal IPN charset to UTF-8 format - $first_name = mb_convert_encoding($fields['first_name'], "UTF-8", "windows-1252"); - $fields['first_name'] = esc_sql($first_name); - $last_name = mb_convert_encoding($fields['last_name'], "UTF-8", "windows-1252"); - $fields['last_name'] = esc_sql($last_name); - $company_name = mb_convert_encoding($fields['company_name'], "UTF-8", "windows-1252"); - $fields['company_name'] = esc_sql($company_name); - - $result = $wpdb->insert($tbl_name, $fields); - if (!$result) { - $slm_debug_logger->log_debug('Error! Failed to update license key table. DB insert query failed.', false); - } - } - //SLM_API_Utility::insert_license_data_internal($fields); - - return $fields['license_key']; -} - -/* Code to handle the eStore's product add/edit interface for SLM specific product configuration */ -add_filter('eStore_addon_product_settings_filter', 'slm_estore_product_configuration_html', 10, 2); //Render the product add/edit HTML -add_action('eStore_new_product_added', 'slm_estore_new_product_added', 10, 2); //Handle the DB insert after a product add. -add_action('eStore_product_updated', 'slm_estore_product_updated', 10, 2); //Handle the DB update after a product edit. -add_action('eStore_product_deleted', 'slm_estore_product_deleted'); //Handle the DB delete after a product delete. - -function slm_estore_product_configuration_html($product_config_html, $prod_id) { - global $wpdb; - $product_meta_table_name = WP_ESTORE_PRODUCTS_META_TABLE_NAME; - - if (empty($prod_id)) { - //New product add - $slm_max_allowed_domains = ""; - $slm_date_of_expiry = ""; - } else { - //Existing product edit - - //Retrieve the max domain value - $product_meta = $wpdb->get_row("SELECT * FROM $product_meta_table_name WHERE prod_id = '$prod_id' AND meta_key='slm_max_allowed_domains'", OBJECT); - if ($product_meta) { - $slm_max_allowed_domains = $product_meta->meta_value; - } else { - $slm_max_allowed_domains = ""; - } - - //Retrieve the expiry date value - $product_meta = $wpdb->get_row("SELECT * FROM $product_meta_table_name WHERE prod_id = '$prod_id' AND meta_key='slm_date_of_expiry'", OBJECT); - if ($product_meta) { - $slm_date_of_expiry = $product_meta->meta_value; - } else { - $slm_date_of_expiry = ""; - } - - } - - $product_config_html .= '
    Software License Manager Plugin (Click to Expand)
    '; - - $product_config_html .= ''; - - $product_config_html .= ''; - - $product_config_html .= '
    Maximum Allowed Domains'; - $product_config_html .= ''; - $product_config_html .= '

    Number of domains/installs in which this license can be used. Leave blank if you wish to use the default value set in the license manager plugin settings.

    '; - $product_config_html .= '
    Number of Days before Expiry'; - $product_config_html .= ' Days'; - $product_config_html .= '

    Number of days before expiry. The expiry date of the license will be set based on this value. For example, if you want the key to expire in 6 months then enter a value of 180.

    '; - $product_config_html .= '
    '; - - return $product_config_html; -} - -function slm_estore_new_product_added($prod_dat_array, $prod_id) { - global $wpdb; - $product_meta_table_name = WP_ESTORE_PRODUCTS_META_TABLE_NAME; - - //Save max domain value - $fields = array(); - $fields['prod_id'] = $prod_id; - $fields['meta_key'] = 'slm_max_allowed_domains'; - $fields['meta_value'] = $prod_dat_array['slm_max_allowed_domains']; - $result = $wpdb->insert($product_meta_table_name, $fields); - if (!$result) { - //insert query failed - } - - //Save expiry date value - $fields = array(); - $fields['prod_id'] = $prod_id; - $fields['meta_key'] = 'slm_date_of_expiry'; - $fields['meta_value'] = $prod_dat_array['slm_date_of_expiry']; - $result = $wpdb->insert($product_meta_table_name, $fields); - if (!$result) { - //insert query failed - } - -} - -function slm_estore_product_updated($prod_dat_array, $prod_id) { - global $wpdb; - $product_meta_table_name = WP_ESTORE_PRODUCTS_META_TABLE_NAME; - - //Find the existing value for the max domains field (for the given product) - $product_meta = $wpdb->get_row("SELECT * FROM $product_meta_table_name WHERE prod_id = '$prod_id' AND meta_key='slm_max_allowed_domains'", OBJECT); - if ($product_meta) { - //Found existing value so lets update it - $fields = array(); - $fields['meta_key'] = 'slm_max_allowed_domains'; - $fields['meta_value'] = $prod_dat_array['slm_max_allowed_domains']; - $result = $wpdb->update($product_meta_table_name, $fields, array('prod_id' => $prod_id)); - - } else { - //No value for this field was there so lets insert one. - $fields = array(); - $fields['prod_id'] = $prod_id; - $fields['meta_key'] = 'slm_max_allowed_domains'; - $fields['meta_value'] = $prod_dat_array['slm_max_allowed_domains']; - $result = $wpdb->insert($product_meta_table_name, $fields); - } - - //Find the existing value for the expiry date field (for the given product) - $product_meta = $wpdb->get_row("SELECT * FROM $product_meta_table_name WHERE prod_id = '$prod_id' AND meta_key='slm_date_of_expiry'", OBJECT); - if ($product_meta) { - //Found existing value so lets update it - $fields = array(); - $fields['meta_key'] = 'slm_date_of_expiry'; - $fields['meta_value'] = $prod_dat_array['slm_date_of_expiry']; - $result = $wpdb->update($product_meta_table_name, $fields, array('prod_id' => $prod_id)); - - } else { - //No value for this field was there so lets insert one. - $fields = array(); - $fields['prod_id'] = $prod_id; - $fields['meta_key'] = 'slm_date_of_expiry'; - $fields['meta_value'] = $prod_dat_array['slm_date_of_expiry']; - $result = $wpdb->insert($product_meta_table_name, $fields); - } - -} - -function slm_estore_product_deleted($prod_id) { - global $wpdb; - $product_meta_table_name = WP_ESTORE_PRODUCTS_META_TABLE_NAME; - - $result = $wpdb->delete($product_meta_table_name, array('prod_id' => $prod_id, 'meta_key' => 'slm_max_allowed_domains')); - $result = $wpdb->delete($product_meta_table_name, array('prod_id' => $prod_id, 'meta_key' => 'slm_date_of_expiry')); -} - -/************************************/ -/*** End of WP eStore integration ***/ -/************************************/ \ No newline at end of file diff --git a/software-license-manager/includes/slm-utility.php b/software-license-manager/includes/slm-utility.php deleted file mode 100644 index 2fdb8f5..0000000 --- a/software-license-manager/includes/slm-utility.php +++ /dev/null @@ -1,74 +0,0 @@ -prepare("SELECT * FROM $tbl_name WHERE lic_status !=%s", 'expired');//Load the non-expired keys - $licenses = $wpdb->get_results($sql_prep, OBJECT); - if(!$licenses){ - SLM_Debug_Logger::log_debug_st("do_auto_key_expiry() - no license keys found."); - return false; - } - - foreach($licenses as $license){ - $key = $license->license_key; - $expiry_date = $license->date_expiry; - if ($expiry_date == '0000-00-00'){ - SLM_Debug_Logger::log_debug_st("This key (".$key.") doesn't have a valid expiry date set. The expiry of this key will not be checked."); - continue; - } - - $today_dt = new DateTime($current_date); - $expire_dt = new DateTime($expiry_date); - if ($today_dt > $expire_dt) { - //This key has reached the expiry. So expire this key. - SLM_Debug_Logger::log_debug_st("This key (".$key.") has expired. Expiry date: ".$expiry_date.". Setting license key status to expired."); - $data = array('lic_status' => 'expired'); - $where = array('id' => $license->id); - $updated = $wpdb->update($tbl_name, $data, $where); - do_action('slm_license_key_expired',$license->id);//Trigger the license expired action hook. - } - - } - } - - /* - * Deletes a license key from the licenses table - */ - static function delete_license_key_by_row_id($key_row_id) { - global $wpdb; - $license_table = SLM_TBL_LICENSE_KEYS; - - //First delete the registered domains entry of this key (if any). - SLM_Utility::delete_registered_domains_of_key($key_row_id); - - //Now, delete the key from the licenses table. - $wpdb->delete( $license_table, array( 'id' => $key_row_id ) ); - - } - - /* - * Deletes any registered domains info from the domain table for the given key's row id. - */ - static function delete_registered_domains_of_key($key_row_id) { - global $slm_debug_logger; - global $wpdb; - $reg_table = SLM_TBL_LIC_DOMAIN; - $sql_prep = $wpdb->prepare("SELECT * FROM $reg_table WHERE lic_key_id = %s", $key_row_id); - $reg_domains = $wpdb->get_results($sql_prep, OBJECT); - foreach ($reg_domains as $domain) { - $row_to_delete = $domain->id; - $wpdb->delete( $reg_table, array( 'id' => $row_to_delete ) ); - //$slm_debug_logger->log_debug("Registered domain with row id (".$row_to_delete.") deleted."); - } - } - -} - diff --git a/software-license-manager/js/index.html b/software-license-manager/js/index.html deleted file mode 100644 index e69de29..0000000 diff --git a/software-license-manager/logs/log.txt b/software-license-manager/logs/log.txt deleted file mode 100644 index e69de29..0000000 diff --git a/software-license-manager/menu/includes/index.html b/software-license-manager/menu/includes/index.html deleted file mode 100644 index e69de29..0000000 diff --git a/software-license-manager/menu/includes/slm-list-table-class.php b/software-license-manager/menu/includes/slm-list-table-class.php deleted file mode 100644 index 82e9df9..0000000 --- a/software-license-manager/menu/includes/slm-list-table-class.php +++ /dev/null @@ -1,907 +0,0 @@ - '', - 'singular' => '', - 'ajax' => false, - 'screen' => null, - ) ); - - $this->screen = convert_to_screen( $args['screen'] ); - - add_filter( "manage_{$this->screen->id}_columns", array( &$this, 'get_columns' ), 0 ); - - if ( !$args['plural'] ) - $args['plural'] = $this->screen->base; - - $args['plural'] = sanitize_key( $args['plural'] ); - $args['singular'] = sanitize_key( $args['singular'] ); - - $this->_args = $args; - - if ( $args['ajax'] ) { - // wp_enqueue_script( 'list-table' ); - add_action( 'admin_footer', array( &$this, '_js_vars' ) ); - } - } - - /** - * Checks the current user's permissions - * @uses wp_die() - * - * @since 3.1.0 - * @access public - * @abstract - */ - function ajax_user_can() { - die( 'function WP_Photo_Seller_List_Table::ajax_user_can() must be over-ridden in a sub-class.' ); - } - - /** - * Prepares the list of items for displaying. - * @uses WP_Photo_Seller_List_Table::set_pagination_args() - * - * @since 3.1.0 - * @access public - * @abstract - */ - function prepare_items() { - die( 'function WP_Photo_Seller_List_Table::prepare_items() must be over-ridden in a sub-class.' ); - } - - /** - * An internal method that sets all the necessary pagination arguments - * - * @param array $args An associative array with information about the pagination - * @access protected - */ - function set_pagination_args( $args ) { - $args = wp_parse_args( $args, array( - 'total_items' => 0, - 'total_pages' => 0, - 'per_page' => 0, - ) ); - - if ( !$args['total_pages'] && $args['per_page'] > 0 ) - $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] ); - - // redirect if page number is invalid and headers are not already sent - if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) { - wp_redirect( esc_url(add_query_arg( 'paged', $args['total_pages'] )) ); - exit; - } - - $this->_pagination_args = $args; - } - - /** - * Access the pagination args - * - * @since 3.1.0 - * @access public - * - * @param string $key - * @return array - */ - function get_pagination_arg( $key ) { - if ( 'page' == $key ) - return $this->get_pagenum(); - - if ( isset( $this->_pagination_args[$key] ) ) - return $this->_pagination_args[$key]; - } - - /** - * Whether the table has items to display or not - * - * @since 3.1.0 - * @access public - * - * @return bool - */ - function has_items() { - return !empty( $this->items ); - } - - /** - * Message to be displayed when there are no items - * - * @since 3.1.0 - * @access public - */ - function no_items() { - _e( 'No items found.' ); - } - - /** - * Display the search box. - * - * @since 3.1.0 - * @access public - * - * @param string $text The search button text - * @param string $input_id The search input id - */ - function search_box( $text, $input_id ) { - if ( empty( $_REQUEST['s'] ) && !$this->has_items() ) - return; - - $input_id = $input_id . '-search-input'; - - if ( ! empty( $_REQUEST['orderby'] ) ) - echo ''; - if ( ! empty( $_REQUEST['order'] ) ) - echo ''; - if ( ! empty( $_REQUEST['post_mime_type'] ) ) - echo ''; - if ( ! empty( $_REQUEST['detached'] ) ) - echo ''; -?> - - link ) with the list - * of views available on this table. - * - * @since 3.1.0 - * @access protected - * - * @return array - */ - function get_views() { - return array(); - } - - /** - * Display the list of views available on this table. - * - * @since 3.1.0 - * @access public - */ - function views() { - $views = $this->get_views(); - $views = apply_filters( 'views_' . $this->screen->id, $views ); - - if ( empty( $views ) ) - return; - - echo "
      \n"; - foreach ( $views as $class => $view ) { - $views[ $class ] = "\t
    • $view"; - } - echo implode( " |
    • \n", $views ) . "\n"; - echo "
    "; - } - - /** - * Get an associative array ( option_name => option_title ) with the list - * of bulk actions available on this table. - * - * @since 3.1.0 - * @access protected - * - * @return array - */ - function get_bulk_actions() { - return array(); - } - - /** - * Display the bulk actions dropdown. - * - * @since 3.1.0 - * @access public - */ - function bulk_actions() { - if ( is_null( $this->_actions ) ) { - $no_new_actions = $this->_actions = $this->get_bulk_actions(); - // This filter can currently only be used to remove actions. - $this->_actions = apply_filters( 'bulk_actions-' . $this->screen->id, $this->_actions ); - $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions ); - $two = ''; - } else { - $two = '2'; - } - - if ( empty( $this->_actions ) ) - return; - - echo "\n"; - - submit_button( __( 'Apply' ), 'action', false, false, array( 'id' => "doaction$two" ) ); - echo "\n"; - } - - /** - * Get the current action selected from the bulk actions dropdown. - * - * @since 3.1.0 - * @access public - * - * @return string|bool The action name or False if no action was selected - */ - function current_action() { - if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] ) - return $_REQUEST['action']; - - if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] ) - return $_REQUEST['action2']; - - return false; - } - - /** - * Generate row actions div - * - * @since 3.1.0 - * @access protected - * - * @param array $actions The list of actions - * @param bool $always_visible Whether the actions should be always visible - * @return string - */ - function row_actions( $actions, $always_visible = false ) { - $action_count = count( $actions ); - $i = 0; - - if ( !$action_count ) - return ''; - - $out = '
    '; - foreach ( $actions as $action => $link ) { - ++$i; - ( $i == $action_count ) ? $sep = '' : $sep = ' | '; - $out .= "$link$sep"; - } - $out .= '
    '; - - return $out; - } - - /** - * Display a monthly dropdown for filtering items - * - * @since 3.1.0 - * @access protected - */ - function months_dropdown( $post_type ) { - global $wpdb, $wp_locale; - - $months = $wpdb->get_results( $wpdb->prepare( " - SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month - FROM $wpdb->posts - WHERE post_type = %s - ORDER BY post_date DESC - ", $post_type ) ); - - $month_count = count( $months ); - - if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) - return; - - $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0; -?> - - __( 'List View' ), - 'excerpt' => __( 'Excerpt View' ) - ); - -?> - -
    - $title ) { - $class = ( $current_mode == $mode ) ? 'class="current"' : ''; - echo "$title\n"; - } - ?> -
    -'; - - echo "" . number_format_i18n( get_comments_number() ) . ""; - - if ( $pending_comments ) - echo ''; - } - - /** - * Get the current page number - * - * @since 3.1.0 - * @access protected - * - * @return int - */ - function get_pagenum() { - $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0; - - if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) - $pagenum = $this->_pagination_args['total_pages']; - - return max( 1, $pagenum ); - } - - /** - * Get number of items to display on a single page - * - * @since 3.1.0 - * @access protected - * - * @return int - */ - function get_items_per_page( $option, $default = 20 ) { - $per_page = (int) get_user_option( $option ); - if ( empty( $per_page ) || $per_page < 1 ) - $per_page = $default; - - return (int) apply_filters( $option, $per_page ); - } - - /** - * Display the pagination. - * - * @since 3.1.0 - * @access protected - */ - function pagination( $which ) { - if ( empty( $this->_pagination_args ) ) - return; - - extract( $this->_pagination_args, EXTR_SKIP ); - - $output = '' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . ''; - - $current = $this->get_pagenum(); - - $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); - - $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url ); - - $page_links = array(); - - $disable_first = $disable_last = ''; - if ( $current == 1 ) - $disable_first = ' disabled'; - if ( $current == $total_pages ) - $disable_last = ' disabled'; - - $page_links[] = sprintf( "%s", - 'first-page' . $disable_first, - esc_attr__( 'Go to the first page' ), - esc_url( remove_query_arg( 'paged', $current_url ) ), - '«' - ); - - $page_links[] = sprintf( "%s", - 'prev-page' . $disable_first, - esc_attr__( 'Go to the previous page' ), - esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ), - '‹' - ); - - if ( 'bottom' == $which ) - $html_current_page = $current; - else - $html_current_page = sprintf( "", - esc_attr__( 'Current page' ), - $current, - strlen( $total_pages ) - ); - - $html_total_pages = sprintf( "%s", number_format_i18n( $total_pages ) ); - $page_links[] = '' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . ''; - - $page_links[] = sprintf( "%s", - 'next-page' . $disable_last, - esc_attr__( 'Go to the next page' ), - esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ), - '›' - ); - - $page_links[] = sprintf( "%s", - 'last-page' . $disable_last, - esc_attr__( 'Go to the last page' ), - esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), - '»' - ); - - $pagination_links_class = 'pagination-links'; - if ( ! empty( $infinite_scroll ) ) - $pagination_links_class = ' hide-if-js'; - $output .= "\n" . join( "\n", $page_links ) . ''; - - if ( $total_pages ) - $page_class = $total_pages < 2 ? ' one-page' : ''; - else - $page_class = ' no-pages'; - - $this->_pagination = "
    $output
    "; - - echo $this->_pagination; - } - - /** - * Get a list of columns. The format is: - * 'internal-name' => 'Title' - * - * @since 3.1.0 - * @access protected - * @abstract - * - * @return array - */ - function get_columns() { - die( 'function WP_Photo_Seller_List_Table::get_columns() must be over-ridden in a sub-class.' ); - } - - /** - * Get a list of sortable columns. The format is: - * 'internal-name' => 'orderby' - * or - * 'internal-name' => array( 'orderby', true ) - * - * The second format will make the initial sorting order be descending - * - * @since 3.1.0 - * @access protected - * - * @return array - */ - function get_sortable_columns() { - return array(); - } - - /** - * Get a list of all, hidden and sortable columns, with filter applied - * - * @since 3.1.0 - * @access protected - * - * @return array - */ - function get_column_info() { - if ( isset( $this->_column_headers ) ) - return $this->_column_headers; - - $columns = get_column_headers( $this->screen ); - $hidden = get_hidden_columns( $this->screen ); - - $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $this->get_sortable_columns() ); - - $sortable = array(); - foreach ( $_sortable as $id => $data ) { - if ( empty( $data ) ) - continue; - - $data = (array) $data; - if ( !isset( $data[1] ) ) - $data[1] = false; - - $sortable[$id] = $data; - } - - $this->_column_headers = array( $columns, $hidden, $sortable ); - - return $this->_column_headers; - } - - /** - * Return number of visible columns - * - * @since 3.1.0 - * @access public - * - * @return int - */ - function get_column_count() { - list ( $columns, $hidden ) = $this->get_column_info(); - $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) ); - return count( $columns ) - count( $hidden ); - } - - /** - * Print column headers, accounting for hidden and sortable columns. - * - * @since 3.1.0 - * @access protected - * - * @param bool $with_id Whether to set the id attribute or not - */ - function print_column_headers( $with_id = true ) { - list( $columns, $hidden, $sortable ) = $this->get_column_info(); - - $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); - $current_url = remove_query_arg( 'paged', $current_url ); - - if ( isset( $_GET['orderby'] ) ) - $current_orderby = $_GET['orderby']; - else - $current_orderby = ''; - - if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) - $current_order = 'desc'; - else - $current_order = 'asc'; - - if ( ! empty( $columns['cb'] ) ) { - static $cb_counter = 1; - $columns['cb'] = '' - . ''; - $cb_counter++; - } - - foreach ( $columns as $column_key => $column_display_name ) { - $class = array( 'manage-column', "column-$column_key" ); - - $style = ''; - if ( in_array( $column_key, $hidden ) ) - $style = 'display:none;'; - - $style = ' style="' . $style . '"'; - - if ( 'cb' == $column_key ) - $class[] = 'check-column'; - elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) ) - $class[] = 'num'; - - if ( isset( $sortable[$column_key] ) ) { - list( $orderby, $desc_first ) = $sortable[$column_key]; - - if ( $current_orderby == $orderby ) { - $order = 'asc' == $current_order ? 'desc' : 'asc'; - $class[] = 'sorted'; - $class[] = $current_order; - } else { - $order = $desc_first ? 'desc' : 'asc'; - $class[] = 'sortable'; - $class[] = $desc_first ? 'asc' : 'desc'; - } - - $column_display_name = '' . $column_display_name . ''; - } - - $id = $with_id ? "id='$column_key'" : ''; - - if ( !empty( $class ) ) - $class = "class='" . join( ' ', $class ) . "'"; - - echo "$column_display_name"; - } - } - - /** - * Display the table - * - * @since 3.1.0 - * @access public - */ - function display() { - extract( $this->_args ); - - $this->display_tablenav( 'top' ); - -?> - - - - print_column_headers(); ?> - - - - - - print_column_headers( false ); ?> - - - - > - display_rows_or_placeholder(); ?> - -
    -display_tablenav( 'bottom' ); - } - - /** - * Get a list of CSS classes for the tag - * - * @since 3.1.0 - * @access protected - * - * @return array - */ - function get_table_classes() { - return array( 'widefat', 'fixed', $this->_args['plural'] ); - } - - /** - * Generate the table navigation above or below the table - * - * @since 3.1.0 - * @access protected - */ - function display_tablenav( $which ) { - if ( 'top' == $which ) - wp_nonce_field( 'bulk-' . $this->_args['plural'] ); -?> -
    - -
    - bulk_actions(); ?> -
    -extra_tablenav( $which ); - $this->pagination( $which ); -?> - -
    -
    - part of the table - * - * @since 3.1.0 - * @access protected - */ - function display_rows_or_placeholder() { - if ( $this->has_items() ) { - $this->display_rows(); - } else { - list( $columns, $hidden ) = $this->get_column_info(); - echo ''; - } - } - - /** - * Generate the table rows - * - * @since 3.1.0 - * @access protected - */ - function display_rows() { - foreach ( $this->items as $item ) - $this->single_row( $item ); - } - - /** - * Generates content for a single row of the table - * - * @since 3.1.0 - * @access protected - * - * @param object $item The current item - */ - function single_row( $item ) { - static $row_class = ''; - $row_class = ( $row_class == '' ? ' class="alternate"' : '' ); - - echo ''; - echo $this->single_row_columns( $item ); - echo ''; - } - - /** - * Generates the columns for a single row of the table - * - * @since 3.1.0 - * @access protected - * - * @param object $item The current item - */ - function single_row_columns( $item ) { - list( $columns, $hidden ) = $this->get_column_info(); - - foreach ( $columns as $column_name => $column_display_name ) { - $class = "class='$column_name column-$column_name'"; - - $style = ''; - if ( in_array( $column_name, $hidden ) ) - $style = ' style="display:none;"'; - - $attributes = "$class$style"; - - if ( 'cb' == $column_name ) { - echo ''; - } - elseif ( method_exists( $this, 'column_' . $column_name ) ) { - echo ""; - } - else { - echo ""; - } - } - } - - /** - * Handle an incoming ajax request (called from admin-ajax.php) - * - * @since 3.1.0 - * @access public - */ - function ajax_response() { - $this->prepare_items(); - - extract( $this->_args ); - extract( $this->_pagination_args, EXTR_SKIP ); - - ob_start(); - if ( ! empty( $_REQUEST['no_placeholder'] ) ) - $this->display_rows(); - else - $this->display_rows_or_placeholder(); - - $rows = ob_get_clean(); - - $response = array( 'rows' => $rows ); - - if ( isset( $total_items ) ) - $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ); - - if ( isset( $total_pages ) ) { - $response['total_pages'] = $total_pages; - $response['total_pages_i18n'] = number_format_i18n( $total_pages ); - } - - die( json_encode( $response ) ); - } - - /** - * Send required variables to JavaScript land - * - * @access private - */ - function _js_vars() { - $args = array( - 'class' => get_class( $this ), - 'screen' => array( - 'id' => $this->screen->id, - 'base' => $this->screen->base, - ) - ); - - printf( "\n", json_encode( $args ) ); - } -} diff --git a/software-license-manager/menu/index.html b/software-license-manager/menu/index.html deleted file mode 100644 index e69de29..0000000 diff --git a/software-license-manager/menu/slm-add-licenses.php b/software-license-manager/menu/slm-add-licenses.php deleted file mode 100644 index 53648fa..0000000 --- a/software-license-manager/menu/slm-add-licenses.php +++ /dev/null @@ -1,311 +0,0 @@ -'; - echo '

    Add/Edit Licenses

    '; - echo '
    '; - - //If product is being edited, grab current product info - if (isset($_GET['edit_record'])) { - $errors = ''; - $id = $_GET['edit_record']; - $lk_table = SLM_TBL_LICENSE_KEYS; - $sql_prep = $wpdb->prepare("SELECT * FROM $lk_table WHERE id = %s", $id); - $record = $wpdb->get_row($sql_prep, OBJECT); - $license_key = $record->license_key; - $max_domains = $record->max_allowed_domains; - $license_status = $record->lic_status; - $first_name = $record->first_name; - $last_name = $record->last_name; - $email = $record->email; - $company_name = $record->company_name; - $txn_id = $record->txn_id; - $reset_count = $record->manual_reset_count; - $created_date = $record->date_created; - $renewed_date = $record->date_renewed; - $expiry_date = $record->date_expiry; - } - - - if (isset($_POST['save_record'])) { - - //Check nonce - if ( !isset($_POST['slm_add_edit_nonce_val']) || !wp_verify_nonce($_POST['slm_add_edit_nonce_val'], 'slm_add_edit_nonce_action' )){ - //Nonce check failed. - wp_die("Error! Nonce verification failed for license save action."); - } - - do_action('slm_add_edit_interface_save_submission'); - - //TODO - do some validation - $license_key = $_POST['license_key']; - $max_domains = $_POST['max_allowed_domains']; - $license_status = $_POST['lic_status']; - $first_name = $_POST['first_name']; - $last_name = $_POST['last_name']; - $email = $_POST['email']; - $company_name = $_POST['company_name']; - $txn_id = $_POST['txn_id']; - $reset_count = $_POST['manual_reset_count']; - $created_date = $_POST['date_created']; - $renewed_date = $_POST['date_renewed']; - $expiry_date = $_POST['date_expiry']; - - if(empty($created_date)){ - $created_date = $current_date; - } - if(empty($renewed_date)){ - $renewed_date = $current_date; - } - if(empty($expiry_date)){ - $expiry_date = $current_date_plus_1year; - } - - //Save the entry to the database - $fields = array(); - $fields['license_key'] = $license_key; - $fields['max_allowed_domains'] = $max_domains; - $fields['lic_status'] = $license_status; - $fields['first_name'] = $first_name; - $fields['last_name'] = $last_name; - $fields['email'] = $email; - $fields['company_name'] = $company_name; - $fields['txn_id'] = $txn_id; - $fields['manual_reset_count'] = $reset_count; - $fields['date_created'] = $created_date; - $fields['date_renewed'] = $renewed_date; - $fields['date_expiry'] = $expiry_date; - - $id = isset($_POST['edit_record'])?$_POST['edit_record']:''; - $lk_table = SLM_TBL_LICENSE_KEYS; - if (empty($id)) {//Insert into database - $result = $wpdb->insert( $lk_table, $fields); - $id = $wpdb->insert_id; - if($result === false){ - $errors .= __('Record could not be inserted into the database!', 'slm'); - } - } else { //Update record - $where = array('id'=>$id); - $updated = $wpdb->update($lk_table, $fields, $where); - if($updated === false){ - //TODO - log error - $errors .= __('Update of the license key table failed!', 'slm'); - } - } - - if(empty($errors)){ - $message = "Record successfully saved!"; - echo '

    '; - echo $message; - echo '

    '; - }else{ - echo '
    ' . $errors . '
    '; - } - - $data = array('row_id' => $id, 'key' => $license_key); - do_action('slm_add_edit_interface_save_record_processed',$data); - - } - -?> - - You can add a new license or edit an existing one from this interface. -

    - -
    -

    -
    - -
    "> - -
    '; - $this->no_items(); - echo '
    '; - echo $this->column_cb( $item ); - echo '"; - echo call_user_func( array( &$this, 'column_' . $column_name ), $item ); - echo ""; - echo $this->column_default( $item, $column_name ); - echo "
    - - '; - } else { - if(!isset($editing_record)){//Create an empty object - $editing_record = new stdClass(); - } - //Auto generate unique key - $lic_key_prefix = $slm_options['lic_prefix']; - if (!empty($lic_key_prefix)) { - $license_key = uniqid($lic_key_prefix); - } else { - $license_key = uniqid(); - } - } - ?> - - - - - - - - - - - - - - - - prepare("SELECT * FROM $reg_table WHERE lic_key_id = %s", $id); - $reg_domains = $wpdb->get_results($sql_prep, OBJECT); - ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    License Key -
    The unique license key. When adding a new record it automatically generates a unique key in this field for you. You can change this value to customize the key if you like.
    Maximum Allowed Domains
    Number of domains/installs in which this license can be used.
    License Status - -
    Registered Domains 0) { - ?> -
    -
    - - - > - - - - -
    registered_domain; ?>id ?>>X
    -
    - -
    First Name
    License user's first name
    Last Name
    License user's last name
    Email Address
    License user's email address
    Company Name
    License user's company name
    Unique Transaction ID
    The unique transaction ID associated with this license key
    Manual Reset Count -
    The number of times this license has been manually reset by the admin (use it if you want to keep track of it). It can be helpful for the admin to keep track of manual reset counts.
    Date Created -
    Creation date of license.
    Date Renewed -
    Renewal date of license.
    Date of Expiry -
    Expiry date of license.
    - - $id, 'key' => $license_key); - $extra_output = apply_filters('slm_add_edit_interface_above_submit','', $data); - if(!empty($extra_output)){ - echo $extra_output; - } - ?> - -
    - -
    - -
    - Manage Licenses

    - - - - -'; - echo '

    License Manager Admin Functions

    '; - echo '
    '; - - $slm_options = get_option('slm_plugin_options'); - - if (isset($_POST['send_deactivation_request'])) { - $postURL = $_POST['lic_mgr_deactivation_req_url']; - $secretKeyForVerification = $slm_options['lic_verification_secret']; - $data = array(); - $data['secret_key'] = $secretKeyForVerification; - - $ch = curl_init($postURL); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - $returnValue = curl_exec($ch); - - $msg = ""; - if ($returnValue == "Success") { - $msg .= "Success message returned from the remote host."; - } - echo '

    '; - echo 'Request sent to the specified URL!'; - echo '
    ' . $msg; - echo '

    '; - } - ?> -
    -
    -

    -
    -
    Enter the URL where the license deactivation message will be sent to -

    -
    - - -
    - -
    -
    -
    -
    '; - echo '
    '; -} diff --git a/software-license-manager/menu/slm-admin-init.php b/software-license-manager/menu/slm-admin-init.php deleted file mode 100644 index aed5fe7..0000000 --- a/software-license-manager/menu/slm-admin-init.php +++ /dev/null @@ -1,25 +0,0 @@ - - - License Manager Integration Help v' . WP_LICENSE_MANAGER_VERSION . ''; - echo '
    '; - echo '
    '; - - echo '

    For information, updates and documentation, please visit the License Manager Documentation page.

    '; - - $api_query_post_url = SLM_SITE_HOME_URL; - echo "The License API Query POST URL For Your Installation"; - echo '
    ' . $api_query_post_url . '
    '; - - echo "The License Activation or Deactivation API secret key"; - echo '
    ' . $secret_verification_key . '
    '; - - echo "The License Creation API secret key"; - echo '
    ' . $creation_secret_key . '
    '; - ?> -

    3rd Party Integration

    - - Integrating a 3rd party payment system or shopping cart with License Manager is easy. -

    - The integration process can be accomplished in three steps, namely: -
    -
    1. Generate POST data -
    2. Send POST data to the API POST URL -
    3. Process the returned data -

    - POST Values -
    - License Manager expects a certain set of variables to be sent to it via HTTP POST or GET. These variables are: -

    - Mandatory Variables -
    - ---------------- -
    a. secret_key - A Secret API key for authentication (you can find the secret key value in the settings menu of this plugin) -
    b. slm_action - The action being performed. The values can be slm_create_new or slm_activate or slm_deactivate -

    - Optional Variables -
    - --------------- -
    c. Customer First Name: The first name of the customer -
    d. Customer Last Name: The last name of the customer -
    e. Customer Email: The email address of the customer -
    f. Company Name: The customer's company name -
    g. Maximum Domains Allowed: The number of domains this license key can be used on -
    h. Transaction ID: A unique transaction ID to reference the transaction -

    - Return Value -
    - Upon successful processing, License Manager will return a plain text message that will have two or three lines similar to the following: -
    -
    - Success -
    License key -
    WPLICMGR4bc29fd61e471 -
    - or -
    - Error -
    Secret key is invalid -
    - - 1. The first line is an indication of success or error -
    2. The second line is the result. -
    3. The third line is additional message that resulted from the request. -

    - Sample PHP Code -
    - Below is a sample PHP code that shows how you can create a license via the API -
    - -
    - /*** Mandatory data ***/ -
    // Post URL -
    $postURL = ""; -
    // The Secret key -
    $secretKey = ""; -
    -
    /*** Optional Data ***/ -
    $firstname = "John"; -
    $lastname = "Doe"; -
    $email = "john.doe@gmail.com"; -
    -
    // prepare the data -
    $data = array (); -
    $data['secret_key'] = $secretKey; -
    $data['slm_action'] = 'slm_create_new'; -
    $data['first_name'] = $firstname; -
    $data['last_name'] = $lastname; -
    $data['email'] = $email; -
    -
    // send data to post URL -
    $ch = curl_init ($postURL); -
    curl_setopt ($ch, CURLOPT_POST, true); -
    curl_setopt ($ch, CURLOPT_POSTFIELDS, $data); -
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true); -
    $returnValue = curl_exec ($ch); -
    -
    // Process the return values -
    //var_dump($returnValue); -
    - -
    '; - echo '
    '; -} diff --git a/software-license-manager/menu/slm-lic-settings.php b/software-license-manager/menu/slm-lic-settings.php deleted file mode 100644 index 5b3d0bb..0000000 --- a/software-license-manager/menu/slm-lic-settings.php +++ /dev/null @@ -1,133 +0,0 @@ -'; - echo '

    WP License Manager Settings v' . WP_LICENSE_MANAGER_VERSION . '

    '; - echo '
    '; - - wp_lic_mgr_general_settings(); - - echo '
    '; - echo '
    '; -} - -function wp_lic_mgr_general_settings() { - - if (isset($_REQUEST['slm_reset_log'])){ - //$slm_logger = new SLM_Debug_Logger(); - global $slm_debug_logger; - $slm_debug_logger->reset_log_file("log.txt"); - $slm_debug_logger->reset_log_file("log-cron-job.txt"); - echo '

    Debug log files have been reset!

    '; - } - - if (isset($_POST['slm_save_settings'])) { - - if (!is_numeric($_POST["default_max_domains"])) {//Set it to one by default if incorrect value is entered - $_POST["default_max_domains"] = '1'; - } - - $options = array( - 'lic_creation_secret' => trim($_POST["lic_creation_secret"]), - 'lic_prefix' => trim($_POST["lic_prefix"]), - 'default_max_domains' => trim($_POST["default_max_domains"]), - 'lic_verification_secret' => trim($_POST["lic_verification_secret"]), - 'enable_auto_key_expiry' => isset($_POST['enable_auto_key_expiry']) ? '1':'', - 'enable_debug' => isset($_POST['enable_debug']) ? '1':'', - ); - update_option('slm_plugin_options', $options); - - echo '

    '; - echo 'Options Updated!'; - echo '

    '; - } - - $options = get_option('slm_plugin_options'); - - $secret_key = $options['lic_creation_secret']; - if (empty($secret_key)) { - $secret_key = uniqid('', true); - } - $secret_verification_key = $options['lic_verification_secret']; - if (empty($secret_verification_key)) { - $secret_verification_key = uniqid('', true); - } - ?> -

    For information, updates and documentation, please visit the License Manager Documentation page.

    - -
    -

    -
    - -

    1. First register a key at purchase time.

    -

    2. Add the code so at activation time it asks for the key.

    -

    3. Integrate the real time online key verification part.

    -
    - -
    - -
    -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Secret Key for License Creation -
    This secret key will be used to authenticate any license creation request. You can change it with something random.
    Secret Key for License Verification Requests -
    This secret key will be used to authenticate any license verification request from customer's site. Important! Do not change this value once your customers start to use your product(s)!
    License Key Prefix -
    You can optionaly specify a prefix for the license keys. This prefix will be added to the uniquely generated license keys.
    Maximum Allowed Domains -
    Maximum number of domains/installs which each license is valid for (default value).
    Auto Expire License Keys value="1"/> -

    When enabled, it will automatically set the status of a license key to "Expired" when the expiry date value of the key is reached. - It doesn't remotely deactivate a key. It simply changes the status of the key in your database to expired.

    -
    -
    - -
    -

    -
    - - - - - - - -
    Enable Debug Logging value="1"/> -

    If checked, debug output will be written to log files (keep it disabled unless you are troubleshooting).

    -
    - View debug log file by clicking here. -
    - Reset debug log file by clicking here. -
    -
    - -
    - -
    -
    - 'item', //singular name of the listed records - 'plural' => 'items', //plural name of the listed records - 'ajax' => false //does this table support ajax? - ) ); - - } - - function column_default($item, $column_name){ - return $item[$column_name]; - } - - function column_id($item){ - $row_id = $item['id']; - $actions = array( - 'edit' => sprintf('Edit', $row_id), - 'delete' => sprintf('Delete',$row_id), - ); - return sprintf('%1$s %2$s', - /*$1%s*/ $item['id'], - /*$2%s*/ $this->row_actions($actions) - ); - } - - - function column_cb($item){ - return sprintf( - '', - /*$1%s*/ $this->_args['singular'], //Let's simply repurpose the table's singular label - /*$2%s*/ $item['id'] //The value of the checkbox should be the record's id - ); - } - - function column_active($item){ - if ($item['active'] == 1){ - return 'active'; - } else{ - return 'inactive'; - } - } - - - function get_columns(){ - $columns = array( - 'cb' => '', //Render a checkbox - 'id' => 'ID', - 'license_key' => 'License Key', - 'lic_status' => 'Status', - 'max_allowed_domains' => 'Domains Allowed', - 'email' => 'Registered Email', - 'date_created' => 'Date Created', - 'date_renewed' => 'Date Renewed', - 'date_expiry' => 'Expiry', - ); - return $columns; - } - - function get_sortable_columns() { - $sortable_columns = array( - 'id' => array('id',false), - 'license_key' => array('license_key',false), - 'lic_status' => array('lic_status',false), - 'date_created' => array('date_created',false), - 'date_renewed' => array('date_renewed',false), - 'date_expiry' => array('date_expiry',false), - ); - return $sortable_columns; - } - - function get_bulk_actions() { - $actions = array( - 'delete' => 'Delete', - ); - return $actions; - } - - function process_bulk_action() { - if('delete'===$this->current_action()) - { - //Process delete bulk actions - if(!isset($_REQUEST['item'])){ - $error_msg = '

    '.__('Error - Please select some records using the checkboxes', 'slm').'

    '; - echo '
    '.$error_msg.'
    '; - return; - }else { - $nvp_key = $this->_args['singular']; - $records_to_delete = $_GET[$nvp_key]; - foreach ($records_to_delete as $row){ - SLM_Utility::delete_license_key_by_row_id($row); - } - echo '

    Selected records deleted successfully!

    '; - } - } - } - - - /* - * This function will delete the selected license key entries from the DB. - */ - function delete_license_key($key_row_id) - { - SLM_Utility::delete_license_key_by_row_id($key_row_id); - $success_msg = '

    '; - $success_msg .= 'The selected entry was deleted successfully!'; - $success_msg .= '

    '; - echo $success_msg; - } - - - function prepare_items() { - /** - * First, lets decide how many records per page to show - */ - $per_page = 50; - $columns = $this->get_columns(); - $hidden = array(); - $sortable = $this->get_sortable_columns(); - - $this->_column_headers = array($columns, $hidden, $sortable); - - $this->process_bulk_action(); - - global $wpdb; - $license_table = SLM_TBL_LICENSE_KEYS; - - /* -- Ordering parameters -- */ - //Parameters that are going to be used to order the result - $orderby = !empty($_GET["orderby"]) ? strip_tags($_GET["orderby"]) : 'id'; - $order = !empty($_GET["order"]) ? strip_tags($_GET["order"]) : 'DESC'; - - if (isset($_POST['slm_search'])) { - $search_term = trim(strip_tags($_POST['slm_search'])); - $prepare_query = $wpdb->prepare("SELECT * FROM " . $license_table . " WHERE `license_key` LIKE '%%%s%%' OR `email` LIKE '%%%s%%' OR `txn_id` LIKE '%%%s%%' OR `first_name` LIKE '%%%s%%' OR `last_name` LIKE '%%%s%%'", $search_term, $search_term, $search_term, $search_term, $search_term); - $data = $wpdb->get_results($prepare_query, ARRAY_A); - }else{ - $data = $wpdb->get_results("SELECT * FROM $license_table ORDER BY $orderby $order", ARRAY_A); - } - - $current_page = $this->get_pagenum(); - $total_items = count($data); - $data = array_slice($data,(($current_page-1)*$per_page),$per_page); - $this->items = $data; - $this->set_pagination_args( array( - 'total_items' => $total_items, //WE have to calculate the total number of items - 'per_page' => $per_page, //WE have to determine how many items to show on a page - 'total_pages' => ceil($total_items/$per_page) //WE have to calculate the total number of pages - ) ); - } -} \ No newline at end of file diff --git a/software-license-manager/menu/slm-manage-licenses.php b/software-license-manager/menu/slm-manage-licenses.php deleted file mode 100644 index b1da0fa..0000000 --- a/software-license-manager/menu/slm-manage-licenses.php +++ /dev/null @@ -1,49 +0,0 @@ -'; - echo '

    Manage Licenses

    '; - echo '
    '; - ?> - -
    -

    -
    - Search for a license by using email, name, key or transaction ID -

    -
    "> - - -
    -
    - - -
    -

    -
    - delete_license_key(sanitize_text_field($_REQUEST['id'])); - } - } - //Fetch, prepare, sort, and filter our data... - $license_list->prepare_items(); - //echo "put table of locked entries here"; - ?> -
    - - - - display(); ?> -
    - -
    - -
    '; - echo '
    '; -} - diff --git a/software-license-manager/readme.txt b/software-license-manager/readme.txt deleted file mode 100644 index 8b170f1..0000000 --- a/software-license-manager/readme.txt +++ /dev/null @@ -1,136 +0,0 @@ -=== Software License Manager === -Contributors: Tips and Tricks HQ, Ruhul Amin -Donate link: https://www.tipsandtricks-hq.com/software-license-manager-plugin-for-wordpress -Tags: license key, serial key, manager, license, serial, key, selling, sell, license activation, manage license, software license, software license manager -Requires at least: 4.0 -Tested up to: 4.8 -Stable tag: 3.0 -License: GPLv2 or later - -Create and manage license keys for your software applications easily - -== Description == - -Software license management solution for your web applications (WordPress plugins, Themes, PHP based membership script etc.) - -This plugin is very useful for creating a license server and doing the following via API: - -- Create license keys in your system (license server) -- Check the status of a license key from from your application (remotely) -- Activate a license key from your application (remotely) -- Deactivate a license key (remotely) -- Check a license key (remotely) -- Track where the license key is being used. - -You can also create license keys manually from the admin dashboard of this plugin. - -= Please note that this plugin is ONLY for developers = - -Check [license manager documentation](https://www.tipsandtricks-hq.com/software-license-manager-plugin-for-wordpress) to learn more. - -= Integration with WP eStore plugin = -Check [WP eStore integration documentation](https://www.tipsandtricks-hq.com/ecommerce/integrate-wp-estore-with-software-license-manager-plugin-3731) - -= Github repository = - -https://github.com/Arsenal21/software-license-manager - -If you need some extra action hooks or filters for this plugin then let us know. - -== Installation == - -1. Go to the Add New plugins screen in your WordPress admin area -1. Click the upload tab -1. Browse for the plugin file (software-license-manager.zip) -1. Click Install Now and then activate the plugin - -== Frequently Asked Questions == -None - -== Screenshots == -See the following page: -https://www.tipsandtricks-hq.com/software-license-manager-plugin-for-wordpress - -== Changelog == - -= 3.0 = -- The integration with WP eStore cart will create multiple licenses when a customer purchases more than 1 quantity of a product. - -= 2.9 = -- The API response will now include a numeric error code (in the event of an error). Thanks to Steve Gehrman. - -= 2.8 = -- The registered domains (if any) of a license key will get deleted when that key is deleted from the manage licenses menu. -- Added wp_unslash() for firstname, lastname, registered domain and company name. Thanks to @sgehrman. -- Added a new action hook (slm_license_key_expired) that gets triggered when a license key expires. - -= 2.7 = -- eStore integration update: changed expiry date field to accept number of days so the plugin can dynamically calculate the expiry date for the key. - -= 2.6 = -- Updated the eStore integration so a custom "Expiry Date" value can be set in the product configuration. - -= 2.5 = -- Updated the eStore plugin integration so a custom "Maximum Allowed Domains" value can be specified in the eStore product configuration. - -= 2.4 = -- Added new action and filter hooks in the add/edit interface so an addon can extend the functionality of that interface. -- Added nonce check in the add/edit license interface. - -= 2.3 = -- Added a new feature to enable auto expiry of the license keys. You can enable this option from the settings. -- If you don't specify a expiry date, when adding a manual license key, it will use the current date plus 1 year as the expiry date. -- Increased the width and height of the "Registered Domains" box in the edit license interface. -- Added a new table column product_ref in the license keys table. -- Added couple of new hooks in the plugin. - -= 2.2 = -- Added integration with the squeeze form submission of eStore plugin. - -= 2.1 = -- The license check query now outputs the date values also. -- Improvement for the WP eStore integration. - -= 2.0 = -- Added a filter to remove any null values from the DB insert query parameter of the API Utility class. - -= 1.9 = -- Replaced "esc_url()" with "esc_url_raw()" in the sample plugin. -- Updated some CSS in the admin interface for WordPress 4.4 - -= 1.8 = -- Added new hooks before the API query is executed. This allows a developer to override the API query and do custom stuff. -- Added a new API to check the details of an existing license key. - -= 1.7 = -* The license key is also included with the response sent to the new license creation request. Below is an example response: -{"result":"success","message":"License successfully created","key":"5580effe188d3"} - -* You can now pass a pre-generated license key to the license creation API using the "license_key" parameter in the request. - -= 1.6 = -* Updated the sample plugin code so the query works better. -* Added the ability to reset the debug log file from the plugin settings interface. -* The item_reference value will be stored in the database (if sent via the activation API query). - -= 1.5 = -* Added the option to search a license key from the manage licenses interface. - -= 1.4 = -* Updated the license key creation API check to use the value from "Secret Key for License Creation" field. - -= 1.3 = -* Added more sanitization. - -= 1.2 = -* Fixed a bug with the bulk delete license operation. - -= 1.1 = -* First commit to wordpress repository. - -== Upgrade Notice == -None - -== Arbitrary section == -See the following sample/example for multi-site environment/setup: -https://github.com/paratheme/Software-License-Manager-Multisite-licensed \ No newline at end of file diff --git a/software-license-manager/slm_bootstrap.php b/software-license-manager/slm_bootstrap.php deleted file mode 100644 index 933267e..0000000 --- a/software-license-manager/slm_bootstrap.php +++ /dev/null @@ -1,53 +0,0 @@ -charset)){ - $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset"; -}else{ - $charset_collate = "DEFAULT CHARSET=utf8"; -} -if (!empty($wpdb->collate)){ - $charset_collate .= " COLLATE $wpdb->collate"; -} - -$lk_tbl_sql = "CREATE TABLE " . $lic_key_table . " ( - id int(12) NOT NULL auto_increment, - license_key varchar(255) NOT NULL, - max_allowed_domains int(12) NOT NULL, - lic_status ENUM('pending', 'active', 'blocked', 'expired') NOT NULL DEFAULT 'pending', - first_name varchar(32) NOT NULL default '', - last_name varchar(32) NOT NULL default '', - email varchar(64) NOT NULL, - company_name varchar(100) NOT NULL default '', - txn_id varchar(64) NOT NULL default '', - manual_reset_count varchar(128) NOT NULL default '', - date_created date NOT NULL DEFAULT '0000-00-00', - date_renewed date NOT NULL DEFAULT '0000-00-00', - date_expiry date NOT NULL DEFAULT '0000-00-00', - product_ref varchar(255) NOT NULL default '', - PRIMARY KEY (id) - )" . $charset_collate . ";"; -dbDelta($lk_tbl_sql); - -$ld_tbl_sql = "CREATE TABLE " .$lic_domain_table. " ( - id INT NOT NULL AUTO_INCREMENT , - lic_key_id INT NOT NULL , - lic_key varchar(255) NOT NULL , - registered_domain text NOT NULL , - item_reference varchar(255) NOT NULL, - PRIMARY KEY ( id ) - )" . $charset_collate . ";"; -dbDelta($ld_tbl_sql); - -update_option("wp_lic_mgr_db_version", WP_LICENSE_MANAGER_DB_VERSION); - -// Add default options -$options = array( - 'lic_creation_secret' => uniqid('', true), - 'lic_prefix' => '', - 'default_max_domains' => '1', - 'lic_verification_secret' => uniqid('', true), - 'enable_debug' => '', -); -add_option('slm_plugin_options', $options); diff --git a/software-license-manager/slm_plugin_core.php b/software-license-manager/slm_plugin_core.php deleted file mode 100644 index 0005fe6..0000000 --- a/software-license-manager/slm_plugin_core.php +++ /dev/null @@ -1,60 +0,0 @@ -prefix . "lic_key_tbl"); -define('SLM_TBL_LIC_DOMAIN', $wpdb->prefix . "lic_reg_domain_tbl"); -define('SLM_MANAGEMENT_PERMISSION', 'manage_options'); -define('SLM_MAIN_MENU_SLUG', 'slm-main'); -define('SLM_MENU_ICON', 'dashicons-lock'); - -//Includes -include_once('includes/slm-debug-logger.php'); -include_once('includes/slm-error-codes.php'); -include_once('includes/slm-utility.php'); -include_once('includes/slm-init-time-tasks.php'); -include_once('includes/slm-api-utility.php'); -include_once('includes/slm-api-listener.php'); -include_once('includes/slm-third-party-integration.php'); -//Include admin side only files -if (is_admin()) { - include_once('menu/slm-admin-init.php'); - include_once('menu/includes/slm-list-table-class.php'); //Load our own WP List Table class -} - -//Action hooks -add_action('init', 'slm_init_handler'); -add_action('plugins_loaded', 'slm_plugins_loaded_handler'); - -//Initialize debug logger -global $slm_debug_logger; -$slm_debug_logger = new SLM_Debug_Logger(); - -//Do init time tasks -function slm_init_handler() { - $init_task = new SLM_Init_Time_Tasks(); - $api_listener = new SLM_API_Listener(); -} - -//Do plugins loaded time tasks -function slm_plugins_loaded_handler() { - //Runs when plugins_loaded action gets fired - if (is_admin()) { - //Check if db update needed - if (get_option('wp_lic_mgr_db_version') != WP_LICENSE_MANAGER_DB_VERSION) { - require_once(dirname(__FILE__) . '/slm_installer.php'); - } - } - -} - -//TODO - need to move this to an ajax handler file -add_action('wp_ajax_del_reistered_domain', 'slm_del_reg_dom'); -function slm_del_reg_dom() { - global $wpdb; - $reg_table = SLM_TBL_LIC_DOMAIN; - $id = strip_tags($_GET['id']); - $ret = $wpdb->query("DELETE FROM $reg_table WHERE id='$id'"); - echo ($ret) ? 'success' : 'failed'; - exit(0); -} diff --git a/templates/page-license-cart.php b/templates/page-license-cart.php new file mode 100644 index 0000000..272a029 --- /dev/null +++ b/templates/page-license-cart.php @@ -0,0 +1,134 @@ +session) { + WC()->session->set('renew_license_key', $renew_license_key); + } + SLM_Helper_Class::write_log("Renewal license key set from URL: {$renew_license_key}"); + return $renew_license_key; + } + + // Retrieve license key from session + if (class_exists('WooCommerce') && WC()->session) { + $renew_license_key = WC()->session->get('renew_license_key'); + if (!empty($renew_license_key)) { + SLM_Helper_Class::write_log("Renewal license key retrieved from session: {$renew_license_key}"); + return $renew_license_key; + } + } + + SLM_Helper_Class::write_log("No renewal license key found in URL or session."); + return null; +} + +// Determine if it's a renewal +$renew_license_key = get_renew_license_key(); +$is_renewal = !empty($renew_license_key); + +?> + +
    +

    + + cart && WC()->cart->is_empty()) : ?> + +
    + +
    + + +
    +

    +
      + 'product', + 'posts_per_page' => 5, // Number of products to display + 'tax_query' => array( + array( + 'taxonomy' => 'product_type', + 'field' => 'slug', + 'terms' => 'slm_license', + ), + ), + ); + + $license_products = new WP_Query($args); + + if ($license_products->have_posts()) : + while ($license_products->have_posts()) : $license_products->the_post(); + global $product; + + // Ensure the product is valid and contains license data + if ($product && $product->is_type('slm_license')) : + ?> +
    • + + + + +

      + get_price_html(); ?> +
      + + + +
    • + +

      + +
    + + +
    + + + +
    +

    + + +

    +

    +
    + + +
    + +
    + + + +
    + +
    +
    + +prefix . 'lic_key_tbl', + $wpdb->prefix . 'lic_reg_domain_tbl', + $wpdb->prefix . 'lic_reg_devices_tbl', + $wpdb->prefix . 'lic_log_tbl', + $wpdb->prefix . 'slm_license_status', + $wpdb->prefix . 'slm_subscribers_tbl', + $wpdb->prefix . 'slm_activations_tbl', +); + +// Drop custom database tables using the `prepare` method +foreach ($tables_to_drop as $table) { + // Check if the table exists before attempting to drop it + if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== null) { + // Drop the table if it exists + $wpdb->query("DROP TABLE IF EXISTS {$table}"); + } +} + +// Delete Custom Post Type posts and related metadata using the `delete` method +$post_types = array('slm_manage_license', 'slm_license_product'); // Add any other custom post types if needed +foreach ($post_types as $post_type) { + // Check if post type data is cached + $cached_posts = wp_cache_get($post_type, 'slm_posts'); + if ($cached_posts) { + wp_cache_delete($post_type, 'slm_posts'); + } + + // Safely delete posts and metadata for this post type + $wpdb->delete($wpdb->posts, array('post_type' => $post_type)); + $wpdb->delete($wpdb->postmeta, array('post_id' => $post_type)); +} + +// Clean orphaned postmeta entries using `DELETE` queries +$wpdb->query( + "DELETE pm FROM {$wpdb->postmeta} pm + LEFT JOIN {$wpdb->posts} p ON pm.post_id = p.ID + WHERE p.ID IS NULL" +); + +// Clean orphaned term relationships if there are custom taxonomies involved +$wpdb->query( + "DELETE tr FROM {$wpdb->term_relationships} tr + LEFT JOIN {$wpdb->posts} p ON tr.object_id = p.ID + WHERE p.ID IS NULL" +); + +// Delete custom user meta related to the plugin (if applicable) +$user_meta_keys = array( + 'slm_user_license_data', + // Add any other related user meta keys here +); + +foreach ($user_meta_keys as $meta_key) { + // Check if user meta data is cached + $cached_meta = wp_cache_get($meta_key, 'slm_usermeta'); + if ($cached_meta) { + wp_cache_delete($meta_key, 'slm_usermeta'); + } + + $wpdb->delete($wpdb->usermeta, array('meta_key' => $meta_key)); +} + +// Clear the relevant cache after heavy operations +wp_cache_flush(); diff --git a/woocommerce/includes/create-license-orders.php b/woocommerce/includes/create-license-orders.php new file mode 100644 index 0000000..b8db286 --- /dev/null +++ b/woocommerce/includes/create-license-orders.php @@ -0,0 +1,308 @@ + '']; + $success_count = 0; + $failure_count = 0; + $skipped_orders = []; + $skipped_reasons = []; + $generated_licenses = []; + + // Retrieve Product ID and Subscription Type from the request + $default_product_id = isset($_POST['slm_product_id']) ? absint($_POST['slm_product_id']) : null; + $slm_lic_type = isset($_POST['subscription_type']) && in_array($_POST['subscription_type'], ['subscription', 'lifetime']) + ? sanitize_text_field($_POST['subscription_type']) + : 'subscription'; + + //SLM_Helper_Class::write_log("Starting license generation with Product ID: {$default_product_id} and License Type: {$slm_lic_type}."); + + // Check if Product ID is missing; if so, log an error, add an error response, and exit. + if (empty($default_product_id)) { + //SLM_Helper_Class::write_log('Error: Product ID is missing in the request.'); + + // Track failure and skip reason for the response + $failure_count++; + $skipped_orders[] = 0; + $skipped_reasons[0] = __('Product ID is missing in the request.', 'slm-plus'); + + // Return early with a JSON error response for AJAX display + $response_data['html'] .= '
  • Error: Product ID is missing in the request. Please provide a valid product ID.
  • '; + wp_send_json_error($response_data); + return; + } + + // Check if the Product ID corresponds to an existing WooCommerce product + $product = wc_get_product($default_product_id); + if (!$product) { + //SLM_Helper_Class::write_log("Error: Product with ID $default_product_id does not exist in WooCommerce."); + + // Track failure and skip reason for the response + $failure_count++; + $skipped_orders[] = 0; + $skipped_reasons[0] = __('The provided Product ID does not correspond to a valid WooCommerce product. Please check the ID and try again.', 'slm-plus'); + + // Return early with a JSON error response for AJAX display + $response_data['html'] .= '
  • Error: The provided Product ID does not correspond to a valid WooCommerce product. Please check the ID and try again.
  • '; + wp_send_json_error($response_data); + return; + } + + // Query to get WooCommerce orders without a license key + $orders_without_license = $wpdb->get_results(" + SELECT p.ID as order_id + FROM {$wpdb->prefix}posts p + LEFT JOIN {$wpdb->prefix}postmeta pm ON pm.post_id = p.ID AND pm.meta_key = '_license_key' + WHERE p.post_type = 'shop_order' + AND (pm.meta_value IS NULL OR pm.meta_value = '') + GROUP BY p.ID + "); + + foreach ($orders_without_license as $order_data) { + $order_id = $order_data->order_id; + $order = wc_get_order($order_id); + + // Only proceed for orders that are completed or processing + if (!in_array($order->get_status(), ['completed', 'processing'])) { + $skipped_orders[] = $order_id; + $skipped_reasons[$order_id] = 'Status not completed/processing'; + continue; + } + + // Gather customer info and order details + $first_name = $order->get_billing_first_name(); + $last_name = $order->get_billing_last_name(); + $email = $order->get_billing_email(); + $purchase_id = $order->get_id(); + $txn_id = $order->get_transaction_id(); + $company_name = $order->get_billing_company(); + $date_created = $order->get_date_created()->date('Y-m-d'); + $user_id = $order->get_user_id(); + + // Billing length and interval for subscription + $slm_billing_length = SLM_API_Utility::get_slm_option('slm_billing_length'); + $slm_billing_interval = SLM_API_Utility::get_slm_option('slm_billing_interval'); + $subscr_id = $user_id; + + // Check license type and set expiration date accordingly + if ($slm_lic_type === 'lifetime') { + $date_expiry = date('Y-m-d', strtotime("+120 years", strtotime($date_created))); + } else { + // Calculate expiration based on billing interval and length + switch ($slm_billing_interval) { + case 'years': + $date_expiry = date('Y-m-d', strtotime("+$slm_billing_length years", strtotime($date_created))); + break; + case 'months': + $date_expiry = date('Y-m-d', strtotime("+$slm_billing_length months", strtotime($date_created))); + break; + case 'days': + $date_expiry = date('Y-m-d', strtotime("+$slm_billing_length days", strtotime($date_created))); + break; + default: + $date_expiry = $date_created; + } + } + + //SLM_Helper_Class::write_log("Interval: {$slm_billing_interval} - Length: {$slm_billing_length}"); + + $order_items = $order->get_items(); + + if (count($order_items) === 0) { + // Handle empty orders by adding a default product item + + $product = wc_get_product($default_product_id); + if (!$product) { + //SLM_Helper_Class::write_log("Error: Product with ID {$default_product_id} does not exist."); + continue; + } + + $item = new WC_Order_Item_Product(); + $item->set_product_id($default_product_id); + $item->set_name($product->get_name()); + $item->set_quantity(1); + $item->set_total($product->get_price()); + + if ($item->meta_exists('_slm_lic_key')) { + $skipped_orders[] = $order_id; + $skipped_reasons[$order_id] = 'Already has a license'; + continue; + } + + $license_data = [ + 'slm_action' => 'slm_create_new', + 'lic_status' => 'pending', + 'lic_type' => $slm_lic_type, + 'first_name' => $first_name, + 'last_name' => $last_name, + 'email' => $email, + 'purchase_id_' => $purchase_id, + 'txn_id' => $txn_id, + 'company_name' => $company_name, + 'max_allowed_domains' => SLM_DEFAULT_MAX_DOMAINS, + 'max_allowed_devices' => SLM_DEFAULT_MAX_DEVICES, + 'date_created' => $date_created, + 'date_expiry' => $date_expiry, + 'product_ref' => $product->get_name(), + 'current_ver' => SLM_API_Utility::get_slm_option('license_current_version'), + 'until' => SLM_API_Utility::get_slm_option('license_until_version'), + 'subscr_id' => $subscr_id, + 'item_reference' => $order_id, + 'slm_billing_length' => $slm_billing_length, + 'slm_billing_interval' => $slm_billing_interval, + 'secret_key' => KEY_API + ]; + + $license_key = ''; + $response = wp_remote_post(SLM_API_URL, [ + 'method' => 'POST', + 'body' => $license_data, + 'timeout' => 45, + 'sslverify' => false, + ]); + + if (!is_wp_error($response)) { + $body = wp_remote_retrieve_body($response); + $api_response = json_decode($body, true); + + if ($api_response && isset($api_response['result']) && $api_response['result'] === 'success') { + $license_key = sanitize_text_field($api_response['key']); + $success_count++; + + $order->add_order_note( + // Translators: %s is the generated license key + sprintf(__('License Key generated: %s', 'slm-plus'), $license_key) + ); + $generated_licenses[] = [ + 'license_key' => $license_key, + 'order_id' => $order_id, + ]; + } else { + $failure_count++; + } + } else { + $failure_count++; + } + + $order->add_item($item); + $order->save(); + } else { + foreach ($order_items as $item) { + $product_id = $item->get_product_id(); + $product_name = $item->get_name(); + + // Fetch the license key directly using the order ID + $existing_license_key = SLM_Utility::slm_get_license_by_order_id($order_id); + + if ($existing_license_key) { + // Add the current order to associated orders for the existing license + SLM_Utility::slm_add_associated_order($existing_license_key, $order_id); + + $skipped_orders[] = $order_id; + $skipped_reasons[$order_id] = 'Already has a license'; + continue; + } + + if ($item->meta_exists('_slm_lic_key')) { + $skipped_orders[] = $order_id; + $skipped_reasons[$order_id] = 'Already has a license'; + continue; + } + + $license_data['product_ref'] = $product_name; + $license_key = ''; + + $response = wp_remote_post(SLM_API_URL, [ + 'method' => 'POST', + 'body' => $license_data, + 'timeout' => 45, + 'sslverify' => false, + ]); + + if (!is_wp_error($response)) { + $body = wp_remote_retrieve_body($response); + $api_response = json_decode($body, true); + + if ($api_response && isset($api_response['result']) && $api_response['result'] === 'success') { + $license_key = sanitize_text_field($api_response['key']); + $success_count++; + $generated_licenses[] = ['license_key' => $license_key, 'order_id' => $order_id]; + + $item->add_meta_data('_slm_lic_key', $license_key, true); + $item->add_meta_data('License Key', $license_key, true); + $item->add_meta_data('License Type', $slm_lic_type, true); + + $order->add_order_note( + // Translators: %s is the generated license key + sprintf(__('License Key generated: %s', 'slm-plus'), $license_key) + ); + } else { + $failure_count++; + } + } else { + $failure_count++; + } + $item->save(); + } + $order->save(); + } + } + + if (!empty($skipped_orders)) { + $response_data['html'] .= '
  • ' . sprintf( + // Translators: %1$d is the number of orders skipped + __('%1$d orders were skipped:', 'slm-plus'), + count($skipped_orders) + ) . '
      '; + foreach ($skipped_orders as $order_id) { + if ($order_id !== 0) { + $order_link = admin_url('post.php?post=' . $order_id . '&action=edit'); + // Translators: %1$d is the order ID, %2$s is the reason why the order was skipped, %3$s is the order view link + $response_data['html'] .= '
    • ' . sprintf(__('Order ID %1$d was skipped due to: %2$s. View Order', 'slm-plus'), $order_id, esc_html($skipped_reasons[$order_id]), esc_url($order_link)) . '
    • '; + } else { + $response_data['html'] .= '
    • ' . esc_html($skipped_reasons[0]) . '
    • '; + } + } + $response_data['html'] .= '
  • '; + } + + if ($success_count > 0) { + $response_data['html'] .= '
  • ' . sprintf( + // Translators: %1$d is the number of successfully generated licenses + __('%1$d licenses generated successfully:', 'slm-plus'), + $success_count + ) . '
      '; + foreach ($generated_licenses as $license_data) { + $order_link = admin_url('post.php?post=' . $license_data['order_id'] . '&action=edit'); + // Translators: %1$s is the license key, %2$d is the order ID, %3$s is the order view link + $response_data['html'] .= '
    • ' . sprintf(__('License Key: %1$s for Order ID %2$d - View Order', 'slm-plus'), esc_html($license_data['license_key']), $license_data['order_id'], esc_url($order_link)) . '
    • '; + } + $response_data['html'] .= '
  • '; + } + + if ($failure_count > 0) { + $response_data['html'] .= '
  • ' . sprintf( + // Translators: %1$d is the number of licenses that failed to generate + __('%1$d licenses failed to generate.', 'slm-plus'), + $failure_count + ) . '
  • '; + } + + wp_send_json_success($response_data); +} diff --git a/woocommerce/includes/hooks/license-checkout-hooks.php b/woocommerce/includes/hooks/license-checkout-hooks.php new file mode 100644 index 0000000..65f1629 --- /dev/null +++ b/woocommerce/includes/hooks/license-checkout-hooks.php @@ -0,0 +1,444 @@ +get_id(), '_renew_license_key', true); + if (!empty($renew_license_key)) { + echo '

    ' . esc_html__('Renewal License Key:', 'slm-plus') . ' ' . esc_html($renew_license_key) . '

    '; + } +} + +/** + * Add the renewal key to WooCommerce email notifications. + */ +add_action('woocommerce_email_order_meta', 'slm_add_renewal_key_to_email', 10, 3); +function slm_add_renewal_key_to_email($order, $sent_to_admin, $plain_text) { + $renew_license_key = get_post_meta($order->get_id(), '_renew_license_key', true); + + if (!empty($renew_license_key)) { + echo '

    ' . esc_html__('Renewal License Key:', 'slm-plus') . ' ' . esc_html($renew_license_key) . '

    '; + } +} + + + + +/** + * Remove the "Additional Information" section on the custom checkout page. + */ +add_action('template_redirect', 'slm_remove_additional_info_on_custom_checkout'); +function slm_remove_additional_info_on_custom_checkout() { + // Check if the current page is your custom checkout page + if (is_page_template('license-checkout.php')) { + add_filter('woocommerce_enable_order_notes_field', '__return_false'); + } +} + +/** + * Handle license renewal during order processing. + */ +add_action('woocommerce_order_status_completed', 'slm_process_license_renewal', 10, 1); +function slm_process_license_renewal($order_id) { + $renew_license_key = get_post_meta($order_id, '_renew_license_key', true); + + if (!empty($renew_license_key)) { + // Log the renewal process + SLM_Helper_Class::write_log("Processing renewal for License Key {$renew_license_key} on Order ID {$order_id}"); + + // Call the renewal function from purchase.php + wc_slm_renew_license(wc_get_order($order_id)); + } else { + // Log and fallback to new license creation + SLM_Helper_Class::write_log("No renewal key found. Proceeding with new license creation for Order ID {$order_id}"); + wc_slm_create_new_license(wc_get_order($order_id)); + } +} + + +add_action('woocommerce_add_to_cart', 'slm_clear_old_renew_license_key', 5); +function slm_clear_old_renew_license_key() { + if (WC()->session->get('renew_license_key')) { + WC()->session->__unset('renew_license_key'); + slm_debug_log("Cleared old renewal license key from session."); + } +} + + +add_action('woocommerce_before_cart_item_quantity_zero', 'slm_clear_renew_license_key_on_cart_change', 10); +function slm_clear_renew_license_key_on_cart_change($cart_item_key) { + $cart = WC()->cart->get_cart(); + + if (isset($cart[$cart_item_key]['renew_license_key'])) { + unset(WC()->cart->cart_contents[$cart_item_key]['renew_license_key']); + SLM_Helper_Class::write_log("License key removed from cart item: {$cart_item_key}"); + } +} + +add_filter('woocommerce_cart_item_display', 'slm_reset_license_key_on_cart_change', 10, 3); +function slm_reset_license_key_on_cart_change($cart_item_html, $cart_item, $cart_item_key) { + if (isset($cart_item['renew_license_key'])) { + $current_license = $cart_item['renew_license_key']; + + // Check if license key is mismatched or invalid + if (!slm_is_valid_license($current_license)) { + WC()->cart->cart_contents[$cart_item_key]['renew_license_key'] = null; + SLM_Helper_Class::write_log("Invalid or mismatched license key cleared from cart."); + } + } + + return $cart_item_html; +} + +function slm_is_valid_license($license_key) { + global $wpdb; + $lic_key_table = $wpdb->prefix . 'lic_key_tbl'; + $license = $wpdb->get_var($wpdb->prepare("SELECT license_key FROM $lic_key_table WHERE license_key = %s", $license_key)); + return !empty($license); +} + +/** + * Restrict cart to one license product. + */ +add_filter('woocommerce_add_to_cart_validation', 'slm_restrict_cart_quantity', 10, 5); +function slm_restrict_cart_quantity($passed, $product_id, $quantity, $variation_id = '', $cart_item_data = []) { + // Ensure WooCommerce is initialized + if (!WC()->cart) { + return $passed; + } + + // Get the product being added + $product = wc_get_product($product_id); + + // Check if the product is a license product + if ($product && $product->is_type('slm_license')) { + // Loop through existing cart items + foreach (WC()->cart->get_cart() as $cart_item) { + if ($cart_item['data']->is_type('slm_license')) { + // Add notice only once + if (!wc_has_notice(__('You can only add one license product (either new or renewal) to your cart at a time.', 'slm-plus'))) { + wc_add_notice(__('You can only add one license product (either new or renewal) to your cart at a time.', 'slm-plus'), 'error'); + } + return false; + } + } + } + + return $passed; +} + +/** + * Validate cart before checkout to ensure only one license product. + */ +add_action('woocommerce_check_cart_items', 'slm_validate_cart_before_checkout'); +function slm_validate_cart_before_checkout() { + if (!WC()->cart) { + return; + } + + $license_count = 0; + + // Count license products in the cart + foreach (WC()->cart->get_cart() as $cart_item) { + if ($cart_item['data']->is_type('slm_license')) { + $license_count++; + } + } + + // If more than one license product is in the cart, display an error and block checkout + if ($license_count > 1) { + if (!wc_has_notice(__('You can only have one license product (new or renewal) in your cart.', 'slm-plus'))) { + wc_add_notice(__('You can only have one license product (new or renewal) in your cart.', 'slm-plus'), 'error'); + } + } +} + +/** + * Automatically remove extra license products if multiple are added to the cart. + */ +add_action('woocommerce_before_calculate_totals', 'slm_remove_extra_license_products', 10, 1); +function slm_remove_extra_license_products($cart) { + if (is_admin() && !defined('DOING_AJAX')) { + return; + } + + $license_product_key = null; + + // Loop through the cart to find license products + foreach ($cart->get_cart() as $cart_item_key => $cart_item) { + $product = $cart_item['data']; + + if ($product && $product->is_type('slm_license')) { + // Keep the first license product, remove the rest + if ($license_product_key === null) { + $license_product_key = $cart_item_key; + } else { + $cart->remove_cart_item($cart_item_key); + wc_add_notice(__('Only one license product is allowed in the cart. Extra items have been removed.', 'slm-plus'), 'notice'); + } + } + } +} + + + +add_filter('woocommerce_cart_item_quantity', 'slm_limit_cart_quantity', 10, 3); + +function slm_limit_cart_quantity($product_quantity, $cart_item_key, $cart_item) { + if ($cart_item['data']->is_type('slm_license')) { + $product_quantity = sprintf( + '', + $cart_item_key + ); + } + + return $product_quantity; +} + + + +add_filter('woocommerce_cart_item_quantity', 'slm_disable_quantity_change', 10, 3); + +function slm_disable_quantity_change($quantity, $cart_item_key, $cart_item) { + if ($cart_item['data']->is_type('slm_license')) { + // Display quantity as non-editable text + return '' . esc_html($cart_item['quantity']) . ''; + } + + return $quantity; +} + +/** + * Handle license product restrictions in cart. + */ +add_action('woocommerce_check_cart_items', 'slm_remove_existing_license_product'); +function slm_remove_existing_license_product() { + $license_product_key = null; + + foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) { + $product = $cart_item['data']; + if ($product->is_type('slm_license')) { + if ($license_product_key === null) { + $license_product_key = $cart_item_key; + } else { + WC()->cart->remove_cart_item($cart_item_key); + slm_debug_log("Removed additional license product from the cart."); + } + } + } +} + + + + +add_filter('woocommerce_add_cart_item_data', 'slm_add_renew_license_key_to_cart', 10, 2); + +function slm_add_renew_license_key_to_cart($cart_item_data, $product_id) { + if (isset($_POST['renew_license_key']) && !empty($_POST['renew_license_key'])) { + $renew_license_key = sanitize_text_field($_POST['renew_license_key']); + $cart_item_data['renew_license_key'] = $renew_license_key; + + // Save to session for later use + WC()->session->set('renew_license_key', $renew_license_key); + slm_debug_log("Renewal License Key added to cart item data and session: {$renew_license_key}"); + } + + return $cart_item_data; +} + + + +add_filter('woocommerce_get_cart_item_from_session', 'slm_get_renew_license_key_from_session', 10, 2); + +function slm_get_renew_license_key_from_session($cart_item, $values) { + if (isset($values['renew_license_key'])) { + $cart_item['renew_license_key'] = $values['renew_license_key']; + slm_debug_log("Renewal License Key retrieved from session for cart item: {$values['renew_license_key']}"); + } + + return $cart_item; +} + + +add_action('woocommerce_check_cart_items', 'slm_validate_license_cart'); + +function slm_validate_license_cart() { + $license_count = 0; + + foreach (WC()->cart->get_cart() as $cart_item) { + if ($cart_item['data']->is_type('slm_license')) { + $license_count++; + } + } + + if ($license_count > 1) { + wc_add_notice(__('You can only have one license (new or renewal) in your cart.', 'slm-plus'), 'error'); + } +} + + +/** + * Redirect to custom cart page for license products. + */ +add_filter('woocommerce_add_to_cart_redirect', 'slm_redirect_to_custom_cart_page'); +function slm_redirect_to_custom_cart_page($url) { + if (isset($_POST['add-to-cart']) && !empty($_POST['add-to-cart'])) { + $product_id = intval($_POST['add-to-cart']); + $product = wc_get_product($product_id); + + if ($product && $product->is_type('slm_license')) { + $custom_cart_url = home_url('/license-cart'); + if (isset($_POST['renew_license_key']) && !empty($_POST['renew_license_key'])) { + $renew_license_key = sanitize_text_field($_POST['renew_license_key']); + $custom_cart_url = add_query_arg('renew_license_key', $renew_license_key, $custom_cart_url); + } + return $custom_cart_url; + } + } + return $url; +} + + +/** + * Customize WooCommerce checkout fields. + */ +/** + * Customize WooCommerce checkout fields. + */ +add_filter('woocommerce_checkout_fields', 'slm_customize_checkout_fields'); +function slm_customize_checkout_fields($fields) { + // Retrieve the renewal license key from the session + $renew_license_key = WC()->session->get('renew_license_key'); + SLM_Helper_Class::write_log("Renew license key retrieved in customize_checkout_fields: {$renew_license_key}"); + + // Add the renewal license field if the key is set + if (!empty($renew_license_key)) { + $fields['billing']['billing_license_renewal'] = array( + 'type' => 'text', + 'label' => esc_html__('This order includes a license renewal for:', 'slm-plus'), + 'placeholder' => '', + 'class' => array('form-row-wide'), + 'custom_attributes' => array('readonly' => 'readonly'), + 'priority' => 29, // Position it above "Company Name" + ); + + // Force the value of the field to the session key + add_filter('woocommerce_checkout_get_value', function ($value, $input) use ($renew_license_key) { + if ($input === 'billing_license_renewal') { + SLM_Helper_Class::write_log("Forcing value for billing_license_renewal: {$renew_license_key}"); + return $renew_license_key; + } + return $value; + }, 10, 2); + } + + return $fields; +} + + +// Set renew license key in session during redirect. +add_action('init', function () { + if (isset($_GET['renew_license']) && isset($_GET['product_id'])) { + // Ensure WooCommerce session exists + if (class_exists('WooCommerce') && WC()->session) { + $renew_license_key = sanitize_text_field($_GET['renew_license']); + WC()->session->set('renew_license_key', $renew_license_key); + SLM_Helper_Class::write_log("Renew license key set in session during redirect: {$renew_license_key}"); + } else { + SLM_Helper_Class::write_log("WooCommerce session not initialized. Cannot set renew license key."); + } + } +}); + +// Clear the renew license key from session on specific conditions. +add_action('wp_loaded', function () { + if (class_exists('WooCommerce') && WC()->session) { + // Example: Clear session key after completing the process or on specific conditions + if (isset($_GET['clear_renew_key'])) { // Example condition + $renew_license_key = WC()->session->get('renew_license_key'); + WC()->session->__unset('renew_license_key'); + SLM_Helper_Class::write_log("Renew license key cleared from session: {$renew_license_key}"); + } + } else { + SLM_Helper_Class::write_log("WooCommerce session not available. Cannot clear renew license key."); + } +}); + +/** + * Display a notice on the checkout page for license renewal. + */ +add_action('woocommerce_before_checkout_form', function () { + $renew_license_key = WC()->session->get('renew_license_key'); +}); + + + + +/** + * Clear renewal license session data when the cart is empty. + */ +add_action('woocommerce_cart_updated', 'slm_clear_session_if_cart_empty'); +function slm_clear_session_if_cart_empty() { + // Ensure WooCommerce session is initialized + if (class_exists('WooCommerce') && WC()->session) { + // Check if the cart is empty + if (WC()->cart->is_empty()) { + // Clear the renew license key from the session + if (WC()->session->get('renew_license_key')) { + $renew_license_key = WC()->session->get('renew_license_key'); + WC()->session->__unset('renew_license_key'); + SLM_Helper_Class::write_log("Cart is empty. Cleared renew license key from session: {$renew_license_key}"); + } + + // Optionally, clear WooCommerce cookies + WC()->session->destroy_session(); + SLM_Helper_Class::write_log("Cart is empty. WooCommerce session and cookies cleared."); + } + } +} + +add_action('woocommerce_before_cart', function () { + if (WC()->cart->is_empty()) { + wc_add_notice(__('Your cart is empty. Session data has been cleared.', 'slm-plus'), 'notice'); + } +}); + +add_action('woocommerce_cart_is_empty', function () { + if (WC()->session && WC()->session->get('renew_license_key')) { + WC()->session->__unset('renew_license_key'); + SLM_Helper_Class::write_log("Cleared renew_license_key as cart is empty."); + } +}); diff --git a/woocommerce/includes/hooks/slm-create-pages.php b/woocommerce/includes/hooks/slm-create-pages.php new file mode 100644 index 0000000..1c335fa --- /dev/null +++ b/woocommerce/includes/hooks/slm-create-pages.php @@ -0,0 +1,49 @@ + 'page', + 'name' => 'license-cart', // Check for the slug + 'post_status' => 'publish', + 'posts_per_page' => 1, + )); + + if (!$query->have_posts()) { + // Create the License Cart page + $page_id = wp_insert_post(array( + 'post_title' => 'License Cart', + 'post_content' => '', // Empty content for now + 'post_status' => 'publish', + 'post_type' => 'page', + 'post_name' => 'license-cart', // Set the slug + 'meta_input' => array('_wp_page_template' => 'page-license-cart.php'), // Assign the custom template + )); + + if ($page_id && !is_wp_error($page_id)) { + // Optionally, hide the page from menus/navigation + update_post_meta($page_id, '_menu_item_visibility', 'hidden'); + error_log(__('License Cart page created successfully with ID: ', 'slm-plus') . $page_id); + } else { + error_log(__('Failed to create License Cart page.', 'slm-plus')); + } + } else { + error_log(__('License Cart page already exists.', 'slm-plus')); + } +} + +/** + * Hook into plugin activation to create the License Cart page. + */ +register_activation_hook(__FILE__, 'slm_create_license_cart_page'); diff --git a/woocommerce/includes/purchase.php b/woocommerce/includes/purchase.php new file mode 100755 index 0000000..fd3da5e --- /dev/null +++ b/woocommerce/includes/purchase.php @@ -0,0 +1,273 @@ +get_id(), '_renew_license_key', true); + + if (empty($renew_license_key)) { + return SLM_Helper_Class::write_log("No renew_license_key found for Order ID: " . $order->get_id()); + } + + // Define the license key table + $lic_key_table = $wpdb->prefix . 'lic_key_tbl'; + + // Fetch the license data + $license_data = $wpdb->get_row( + $wpdb->prepare( + "SELECT * FROM $lic_key_table WHERE license_key = %s LIMIT 1", + $renew_license_key + ), + ARRAY_A + ); + + if (!$license_data) { + return SLM_Helper_Class::write_log("License not found for renewal. Order ID: " . $order->get_id()); + } + + if ($license_data['lic_status'] === 'blocked') { + return SLM_Helper_Class::write_log("Blocked license cannot be renewed. License Key: {$renew_license_key}"); + } + + // Calculate the new expiration date + $current_expiry_date = $license_data['date_expiry']; + $new_expiration_date = date( + 'Y-m-d', + strtotime($current_expiry_date . ' +' . $license_data['slm_billing_length'] . ' ' . $license_data['slm_billing_interval']) + ); + + // Loop through the order items to get the product ID + $product_id = null; + foreach ($order->get_items() as $item) { + $product_id = $item->get_product_id(); // Get the product ID + SLM_Helper_Class::write_log("Processing renewal for Product ID: {$product_id}, Order ID: " . $order->get_id()); + break; // Only one product ID is needed for a single license renewal + } + + // Update the license data + $updated = $wpdb->update( + $lic_key_table, + [ + 'date_expiry' => $new_expiration_date, + 'lic_status' => 'active', + 'date_renewed' => current_time('mysql'), + 'wc_order_id' => $order->get_id(), + 'txn_id' => $order->get_id(), + 'item_reference' => $product_id, + 'purchase_id_' => $order->get_id(), + ], + ['license_key' => $renew_license_key], + ['%s', '%s', '%s', '%d', '%d', '%s', '%d'], // Added placeholders for product ID + ['%s'] + ); + + // Debugging: Check if update was successful + if ($updated === false) { + SLM_Helper_Class::write_log("Failed to renew license. License Key: {$renew_license_key}, Order ID: " . $order->get_id()); + } else { + SLM_Helper_Class::write_log("License renewed successfully. License Key: {$renew_license_key}, Product ID: {$product_id}, Order ID: " . $order->get_id() . ", New Expiration Date: $new_expiration_date."); + + // Add the order to the associated orders + $associated_updated = SLM_Utility::slm_add_associated_order($renew_license_key, $order->get_id()); + + if ($associated_updated) { + SLM_Helper_Class::write_log("Order ID: {$order->get_id()} successfully added to associated orders for License Key: {$renew_license_key}"); + } else { + SLM_Helper_Class::write_log("Failed to add Order ID: {$order->get_id()} to associated orders for License Key: {$renew_license_key}"); + } + } +} + +function slm_display_licenses_in_order_details($order) { + global $wpdb; + + // Fetch the WooCommerce order ID + $order_id = $order->get_id(); + + // Fetch license keys for this order from the license table + $lic_key_table = $wpdb->prefix . 'lic_key_tbl'; + $licenses = $wpdb->get_results( + $wpdb->prepare("SELECT license_key, lic_status FROM $lic_key_table WHERE wc_order_id = %d", $order_id), + ARRAY_A + ); + + // If no licenses exist, return + if (empty($licenses)) { + return; + } + + // Display licenses + echo '

    ' . esc_html__('License Information', 'slm-plus') . '

    '; + echo ' + + + + + + + + '; + + foreach ($licenses as $license) { + $license_key = esc_html($license['license_key']); + $status = esc_html(ucfirst($license['lic_status'])); + $license_url = esc_url(add_query_arg('license_key', $license_key, wc_get_page_permalink('myaccount') . 'my-licenses')); + + echo " + + + + "; + } + + echo '
    ' . esc_html__('License Key', 'slm-plus') . '' . esc_html__('Status', 'slm-plus') . '' . esc_html__('Actions', 'slm-plus') . '
    $license_key$status" . esc_html__('View License', 'slm-plus') . "
    '; +} + +function wc_slm_create_new_license($order) { + global $wpdb; + + // Validate the order object + if (!$order instanceof WC_Order) { + SLM_Helper_Class::write_log("Invalid order object passed."); + return; + } + + // Check if a license has already been created for this order + $license_created = get_post_meta($order->get_id(), '_slm_license_created', true); + if (!empty($license_created)) { + SLM_Helper_Class::write_log("License already created for Order ID: {$order->get_id()}"); + return; + } + + $items = $order->get_items(); + $customer_id = $order->get_user_id(); + $billing_email = sanitize_email($order->get_billing_email()); + + // Loop through the order items + foreach ($items as $item_key => $values) { + $product_id = $values->get_product_id(); + $product = $values->get_product(); + + // Skip if product is not a license product + if (!$product->is_type('slm_license')) { + continue; + } + + // Retrieve product custom fields + $custom_fields = [ + 'max_allowed_domains' => intval(get_post_meta($product_id, '_domain_licenses', true)), + 'max_allowed_devices' => intval(get_post_meta($product_id, '_devices_licenses', true)), + 'slm_billing_interval' => sanitize_text_field(get_post_meta($product_id, '_license_renewal_period_term', true)), + 'slm_billing_length' => intval(get_post_meta($product_id, '_license_renewal_period_length', true)), + 'current_ver' => sanitize_text_field(get_post_meta($product_id, '_license_current_version', true)), + 'until' => sanitize_text_field(get_post_meta($product_id, '_license_until_version', true)), + 'lic_type' => sanitize_text_field(get_post_meta($product_id, '_license_type', true)), + 'item_reference' => sanitize_text_field(get_post_meta($product_id, '_license_item_reference', true)), + ]; + + // Calculate expiration date + $expiration_date = ($custom_fields['slm_billing_interval'] === 'onetime') + ? date('Y-m-d', strtotime('+200 years')) + : date('Y-m-d', strtotime('+' . $custom_fields['slm_billing_length'] . ' ' . $custom_fields['slm_billing_interval'])); + + // Generate a new license key + $new_license_key = slm_get_license(get_option('slm_license_prefix', 'SLM')); + + // Insert the license into the database + $result = $wpdb->insert( + SLM_TBL_LICENSE_KEYS, // Ensure constant is defined for table name + [ + 'license_key' => $new_license_key, + 'max_allowed_domains' => $custom_fields['max_allowed_domains'], + 'max_allowed_devices' => $custom_fields['max_allowed_devices'], + 'slm_billing_interval' => $custom_fields['slm_billing_interval'], + 'slm_billing_length' => $custom_fields['slm_billing_length'], + 'current_ver' => $custom_fields['current_ver'], + 'until' => $custom_fields['until'], + 'lic_type' => $custom_fields['lic_type'], + 'item_reference' => $product_id, + 'wc_order_id' => $order->get_id(), + 'product_ref' => $product_id, + 'email' => $billing_email, + 'first_name' => sanitize_text_field($order->get_billing_first_name()), + 'last_name' => sanitize_text_field($order->get_billing_last_name()), + 'date_created' => current_time('mysql'), + 'date_expiry' => $expiration_date, + 'lic_status' => 'pending', + 'purchase_id_' => $order->get_id(), + 'txn_id' => $order->get_id(), + 'subscr_id' => $customer_id, + ] + ); + + if ($result === false) { + SLM_Helper_Class::write_log("Failed to create license for Product ID: {$product_id} in Order ID: {$order->get_id()}. Error: {$wpdb->last_error}"); + continue; + } + + // Associate license with WooCommerce order + update_post_meta($order->get_id(), '_slm_license_key', $new_license_key); + update_post_meta($order->get_id(), 'License Key', $new_license_key); + + SLM_Helper_Class::write_log("New license key created for Product ID: {$product_id} in Order ID: {$order->get_id()}"); + } + + // Mark the license as created + update_post_meta($order->get_id(), '_slm_license_created', true); + SLM_Helper_Class::write_log("License creation process completed for Order ID: {$order->get_id()}"); +} diff --git a/woocommerce/includes/register-template.php b/woocommerce/includes/register-template.php new file mode 100644 index 0000000..9a6435c --- /dev/null +++ b/woocommerce/includes/register-template.php @@ -0,0 +1,126 @@ +get_type() : $product->product_type; + + // Check if the product type is 'slm_license' + if ($product_type === 'slm_license') { + $template_path = SLM_WOO; + + // Detect if the request includes a renew_license_key + $is_renewal = isset($_GET['renew_license_key']) && !empty($_GET['renew_license_key']); + $renew_license_key = $is_renewal ? sanitize_text_field($_GET['renew_license_key']) : ''; + + // Pass renewal status and key as variables to the template + wc_get_template( + 'single-product/add-to-cart/slm_license.php', + array( + 'is_renewal' => $is_renewal, + 'renew_license_key' => $renew_license_key, + ), + '', + trailingslashit($template_path) + ); + } +} + + + +/** + * Override default WooCommerce templates and template parts from plugin. + * + * E.g. + * Override template 'woocommerce/loop/result-count.php' with 'my-plugin/woocommerce/loop/result-count.php'. + * Override template part 'woocommerce/content-product.php' with 'my-plugin/woocommerce/content-product.php'. + * + * Note: We used folder name 'woocommerce' in plugin to override all woocommerce templates and template parts. + * You can change it as per your requirement. + */ +// Override Template Part's. +add_filter( 'wc_get_template_part', 'slm_override_woocommerce_template_part', 10, 3 ); +// Override Template's. +add_filter( 'woocommerce_locate_template', 'slm_override_woocommerce_template', 10, 3 ); +/** + * Template Part's + * + * @param string $template Default template file path. + * @param string $slug Template file slug. + * @param string $name Template file name. + * @return string Return the template part from plugin. + */ +function slm_override_woocommerce_template_part( $template, $slug, $name ) { + // UNCOMMENT FOR @DEBUGGING + // echo '
    ';
    +    // echo 'template: ' . $template . '
    '; + // echo 'slug: ' . $slug . '
    '; + // echo 'name: ' . $name . '
    '; + // echo '
    '; + // Template directory. + // E.g. /wp-content/plugins/my-plugin/woocommerce/ + $template_directory = untrailingslashit( plugin_dir_path( __FILE__ ) ) . 'woocommerce/'; + if ( $name ) { + $path = $template_directory . "{$slug}-{$name}.php"; + } else { + $path = $template_directory . "{$slug}.php"; + } + return file_exists( $path ) ? $path : $template; +} +/** + * Template File + * + * @param string $template Default template file path. + * @param string $template_name Template file name. + * @param string $template_path Template file directory file path. + * @return string Return the template file from plugin. + */ +function slm_override_woocommerce_template( $template, $template_name, $template_path ) { + // UNCOMMENT FOR @DEBUGGING + // echo '
    ';
    +    // echo 'template: ' . $template . '
    '; + // echo 'template_name: ' . $template_name . '
    '; + // echo 'template_path: ' . $template_path . '
    '; + // echo '
    '; + // Template directory. + // E.g. /wp-content/plugins/my-plugin/woocommerce/ + $template_directory = untrailingslashit( plugin_dir_path( __FILE__ ) ) . 'woocommerce/'; + $path = $template_directory . $template_name; + return file_exists( $path ) ? $path : $template; +} + + + +// Load template for the specific page +add_filter('page_template', 'slm_load_license_cart_template'); +function slm_load_license_cart_template($page_template) { + if (get_page_template_slug() == 'page-license-cart.php') { + $page_template = SLM_TEMPLATES . 'page-license-cart.php'; + } + return $page_template; +} + +/** + * Add "License Cart" template to the Page Attributes template dropdown. + */ +add_filter('theme_page_templates', 'slm_add_license_cart_template', 10, 4); +function slm_add_license_cart_template($post_templates, $wp_theme, $post, $post_type) { + // Add the custom template to the dropdown + $post_templates['page-license-cart.php'] = __('License Cart', 'slm-plus'); + return $post_templates; +} diff --git a/woocommerce/includes/slm-meta-boxes.php b/woocommerce/includes/slm-meta-boxes.php new file mode 100644 index 0000000..38a5170 --- /dev/null +++ b/woocommerce/includes/slm-meta-boxes.php @@ -0,0 +1,629 @@ +version, '3.0.0', '>=')) { + class WC_Product_SLM_License extends WC_Product_Simple + { + protected $product_type = 'slm_license'; + + public function __construct($product = 0) + { + parent::__construct($product); + } + + public function get_type() + { + return 'slm_license'; + } + } + } else { + // Older versions use WC_Product as the base class + class WC_Product_SLM_License extends WC_Product + { + protected $product_type = 'slm_license'; + + public function __construct($product = 0) + { + parent::__construct($product); + } + + public function get_type() + { + return 'slm_license'; + } + } + } + } +} +add_action('init', 'slm_register_product_type'); + + +function slm_register_product_class($classname, $product_type) +{ + if ($product_type == 'slm_license') { + $classname = 'WC_Product_SLM_License'; + } + return $classname; +} +add_filter('woocommerce_product_class', 'slm_register_product_class', 10, 2); + + +function slm_add_product_type($types) +{ + $types['slm_license'] = __('License product', 'slm-plus'); + return $types; + error_log("Saving product type for Product ID: " . $types); +} +add_filter('product_type_selector', 'slm_add_product_type'); + + +/** + * Add 'License Manager' product option. + */ +function add_wc_slm_data_tab_enabled_product_option($product_type_options) +{ + // Check if the current product type is the custom license product type + if (isset($_GET['product_type']) && $_GET['product_type'] === 'slm_license') { + $product_type_options['wc_slm_data_tab_enabled'] = array( + 'id' => '_wc_slm_data_tab_enabled', + 'wrapper_class' => 'show_if_slm_license', + 'label' => __('License Manager', 'slm-plus'), + 'default' => 'no', + 'description' => __('Enables the license creation API.', 'slm-plus'), + 'type' => 'checkbox' + ); + } + return $product_type_options; +} +add_filter('product_type_options', 'add_wc_slm_data_tab_enabled_product_option', 10); + + +/** + * CSS To Add Custom tab Icon + */ +function wcpp_custom_style() +{ +?> + + + __('License Info', 'slm-plus'), + 'target' => 'wc_slm_meta', + 'class' => array('show_if_slm_license', 'show_if_wc_slm_data_tab_enabled'), + ); + return $wc_slm_data_tabs; +} +add_filter('woocommerce_product_data_tabs', 'wc_slm_add_tab'); + +/** + * Custom WooCommerce Data Panel + */ +function wc_slm_data_panel() +{ + global $post; + $product_id = get_the_ID(); + $slm_options = get_option('slm_plugin_options'); +?> +
    +
    + '_domain_licenses', + 'label' => __('Domain Licenses', 'slm-plus'), + 'placeholder' => SLM_Helper_Class::slm_get_option('default_max_domains'), + 'desc_tip' => 'true', + 'value' => $value, + 'type' => 'number', + 'custom_attributes' => array('step' => 'any', 'min' => 0), + 'description' => __('Enter the allowed number of domains this license can have (websites).', 'slm-plus') + )); + + // Device Licenses Input + $value = get_post_meta($product_id, '_devices_licenses', true); + $value = ($value === '') ? SLM_Helper_Class::slm_get_option('default_max_devices') : $value; + + woocommerce_wp_text_input(array( + 'id' => '_devices_licenses', + 'label' => __('Devices Licenses', 'slm-plus'), + 'placeholder' => SLM_Helper_Class::slm_get_option('default_max_devices'), + 'desc_tip' => 'true', + 'value' => $value, + 'type' => 'number', + 'custom_attributes' => array('step' => 'any', 'min' => 0), + 'description' => __('Enter the allowed number of devices this license can have (computers, mobile, etc).', 'slm-plus') + )); + + // Item Reference Field (if enabled) + if (!empty($slm_options['slm_multiple_items']) && $slm_options['slm_multiple_items'] == 1) { + woocommerce_wp_text_input(array( + 'id' => '_license_item_reference', + 'label' => __('Item Reference', 'slm-plus'), + 'placeholder' => __("Software's item reference", 'slm-plus'), + 'desc_tip' => 'true', + 'description' => __('Enter the item reference of your application, theme, or plug-in. The license will be then bound to this exact software.', 'slm-plus') + )); + } + + // License Type Dropdown + woocommerce_wp_select(array( + 'id' => '_license_type', + 'label' => __('License Type', 'slm-plus'), + 'desc_tip' => 'true', + 'description' => __('Type of license: subscription base or lifetime', 'slm-plus'), + 'options' => array( + 'none' => __('Select one', 'slm-plus'), + 'subscription' => __('Subscription', 'slm-plus'), + 'lifetime' => __('Lifetime', 'slm-plus'), + ) + )); + + // License Renewal Period Length + woocommerce_wp_text_input(array( + 'id' => '_license_renewal_period_length', + 'label' => __('Renewal Period Length', 'slm-plus'), + 'description' => __('XX Amount of days, months, or years.', 'slm-plus'), + 'type' => 'text', + 'value' => get_post_meta($product_id, '_license_renewal_period_length', true) ?: SLM_Helper_Class::slm_get_option('slm_billing_length'), + )); + + + // License Renewal Period Term Dropdown + woocommerce_wp_select(array( + 'id' => '_license_renewal_period_term', + 'label' => __('Expiration Term', 'slm-plus'), + 'placeholder' => 'select time frame', + 'description' => __('Choose between days, months, or years', 'slm-plus'), + 'options' => array( + 'days' => __('Day(s)', 'slm-plus'), + 'months' => __('Month(s)', 'slm-plus'), + 'years' => __('Year(s)', 'slm-plus'), + 'onetime' => __('One Time', 'slm-plus'), + ), + 'value' => get_post_meta($product_id, '_license_renewal_period_term', true) ?: SLM_Helper_Class::slm_get_option('slm_billing_interval'), // Ensure default value is set to 'years' if empty + )); + + echo '

    '; + + // Current Version Input + woocommerce_wp_text_input(array( + 'id' => '_license_current_version', + 'label' => __('Current Version', 'slm-plus'), + 'placeholder' => '0.0.0', + 'desc_tip' => 'true', + 'description' => __('Enter the current version of your application, theme, or plug-in', 'slm-plus') + )); + + // Until Version Input + woocommerce_wp_text_input(array( + 'id' => '_license_until_version', + 'label' => __('Until Version', 'slm-plus'), + 'placeholder' => '0.0.0', + 'desc_tip' => 'true', + 'description' => __('Enter the version until support expires.', 'slm-plus') + )); + ?> +
    +
    + + +ID); + if (!$order) { + echo '

    ' . esc_html__('Order not found.', 'slm-plus') . '

    '; + return; + } + + $order_id = $order->get_id(); + $order_status = $order->get_status(); + + // Fetch license details from the database + $license_data = $wpdb->get_row( + $wpdb->prepare( + "SELECT license_key, lic_type FROM " . SLM_TBL_LICENSE_KEYS . " WHERE wc_order_id = %d LIMIT 1", + $order_id + ) + ); + + $license_key = $license_data->license_key ?? ''; + $license_type = $license_data->lic_type ?? ''; + + // Determine if a new license can be created based on the order status + $can_create_license = empty($license_key) && in_array($order_status, ['completed', 'processing']); + + // Display license information if it exists + if (!empty($license_key)) { + $license_id = $wpdb->get_var( + $wpdb->prepare( + "SELECT id FROM " . SLM_TBL_LICENSE_KEYS . " WHERE license_key = %s LIMIT 1", + $license_key + ) + ); + + if ($license_id) { + echo '

    ' . esc_html__('License Key:', 'slm-plus') . ' ' . esc_html($license_key) . '

    '; + echo '

    ' . esc_html__('License Type:', 'slm-plus') . ' ' . esc_html($license_type) . '

    '; + echo '

    ' . esc_html__('A license key is already assigned to this order.', 'slm-plus') . '

    '; + + // Link to view the license using its ID + $license_view_url = esc_url(admin_url('admin.php?page=slm_manage_license&edit_record=' . $license_id)); + echo '' . esc_html__('View License', 'slm-plus') . ''; + } else { + echo '

    ' . esc_html__('License information could not be retrieved.', 'slm-plus') . '

    '; + } + } + elseif ($can_create_license) { + // Show license creation options for eligible orders + echo ''; + echo '

    '; + + echo ''; + } else { + // Informational message for ineligible orders + echo '

    ' . esc_html__('Order must be completed or processing to create a license.', 'slm-plus') . '

    '; + echo ''; + } + +?> + + + __('Invalid order ID', 'slm-plus')]); + } + + // Fetch the WooCommerce order + $order = wc_get_order($order_id); + if (!$order || !in_array($order->get_status(), ['completed', 'processing'])) { + wp_send_json_error(['message' => __('Order must be completed or processing to create a license', 'slm-plus')]); + } + + // Fetch necessary details from the order + $first_name = $order->get_billing_first_name(); + $last_name = $order->get_billing_last_name(); + $email = $order->get_billing_email(); + $txn_id = $order->get_transaction_id(); + $company_name = $order->get_billing_company(); + $date_created = $order->get_date_created() ? $order->get_date_created()->date('Y-m-d') : current_time('mysql'); + $user_id = $order->get_user_id(); + + // Default values from options + $slm_billing_length = SLM_API_Utility::get_slm_option('slm_billing_length'); + $slm_billing_interval = SLM_API_Utility::get_slm_option('slm_billing_interval'); + $default_domains = SLM_DEFAULT_MAX_DOMAINS; + $default_devices = SLM_DEFAULT_MAX_DEVICES; + + // Determine expiration date + $date_expiry = $lic_type === 'lifetime' + ? date('Y-m-d', strtotime('+120 years', strtotime($date_created))) + : date('Y-m-d', strtotime("+$slm_billing_length $slm_billing_interval", strtotime($date_created))); + + $licenses = []; + foreach ($order->get_items() as $item) { + $product_id = $item->get_product_id(); + $product = wc_get_product($product_id); + + if ($product && $product->is_type('slm_license')) { + // Fetch custom fields for the license + $product_data = [ + 'current_ver' => get_post_meta($product_id, '_license_current_version', true), + 'until_ver' => get_post_meta($product_id, '_license_until_version', true), + 'max_devices' => get_post_meta($product_id, '_devices_licenses', true) ?: $default_devices, + 'max_domains' => get_post_meta($product_id, '_domain_licenses', true) ?: $default_domains, + 'item_reference' => get_post_meta($product_id, '_license_item_reference', true), + ]; + + // Generate a new license key + $new_license_key = slm_get_license(KEY_API_PREFIX); + + // Insert the new license into the database + $wpdb->insert(SLM_TBL_LICENSE_KEYS, [ + 'license_key' => $new_license_key, + 'wc_order_id' => $order_id, + 'product_ref' => $product_id, + 'txn_id' => $order_id, + 'purchase_id_' => $order_id, + 'subscr_id' => $user_id, + 'item_reference' => $product_data['item_reference'], + 'max_allowed_domains' => intval($product_data['max_domains']), + 'max_allowed_devices' => intval($product_data['max_devices']), + 'date_created' => $date_created, + 'date_expiry' => $date_expiry, + 'slm_billing_length' => intval($slm_billing_length), + 'slm_billing_interval' => sanitize_text_field($slm_billing_interval), + 'current_ver' => sanitize_text_field($product_data['current_ver']), + 'until' => sanitize_text_field($product_data['until_ver']), + 'lic_type' => sanitize_text_field($lic_type), + 'email' => sanitize_email($email), + 'first_name' => sanitize_text_field($first_name), + 'last_name' => sanitize_text_field($last_name), + 'company_name' => sanitize_text_field($company_name), + 'lic_status' => 'pending', + ]); + + // Add the license key to the order note + $order->add_order_note(sprintf(__('License Key generated: %s', 'slm-plus'), $new_license_key)); + + // Collect license info for the response + $licenses[] = [ + 'license_key' => $new_license_key, + 'product_name' => $product->get_name(), + ]; + } + } + + // Save the order after updating + $order->save(); + + // Send success response with license information + if (!empty($licenses)) { + wp_send_json_success([ + 'message' => __('License created successfully', 'slm-plus'), + 'licenses' => $licenses, + ]); + } else { + wp_send_json_error(['message' => __('No licenses were generated', 'slm-plus')]); + } +} + + +add_action('wp_ajax_check_order_user_info', 'check_order_user_info_callback'); +function check_order_user_info_callback(){ + check_ajax_referer('slm_generate_license_nonce', 'security'); + + $order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : null; + if (!$order_id) { + wp_send_json_error(['message' => __('Invalid order ID', 'slm-plus')]); + } + + $order = wc_get_order($order_id); + if ($order) { + $last_name = $order->get_billing_last_name(); + $email = $order->get_billing_email(); + wp_send_json_success(['last_name' => $last_name, 'email' => $email]); + } + else { + wp_send_json_error(['message' => __('Order not found', 'slm-plus')]); + } +} diff --git a/woocommerce/includes/wc_licenses_class.php b/woocommerce/includes/wc_licenses_class.php new file mode 100755 index 0000000..275c6b5 --- /dev/null +++ b/woocommerce/includes/wc_licenses_class.php @@ -0,0 +1,569 @@ + + * @link https://github.com/michelve/software-license-manager + */ + +// If this file is called directly, abort. +if (!defined('WPINC')) { + die(); + +} + +//slm_woo_downloads +function slm_remove_downloads_from_account_menu($items) { + // Remove "Downloads" menu item. + unset($items['downloads']); + return $items; +} + +function slm_disable_downloads_endpoint_redirect() { + // Check if the current endpoint is "downloads" and if it's part of the My Account page. + if (is_wc_endpoint_url('downloads')) { + // Redirect to the My Account dashboard. + wp_safe_redirect(wc_get_page_permalink('myaccount')); + exit; + } +} + +$enable_downloads_page = SLM_API_Utility::get_slm_option('slm_woo_downloads'); +// Check if the 'enable_downloads_page' option is enabled. +if ($enable_downloads_page == 1) { + // If the option is set and enabled, trigger the action. + add_action('template_redirect', 'slm_disable_downloads_endpoint_redirect'); + add_filter('woocommerce_account_menu_items', 'slm_remove_downloads_from_account_menu', 10); +} + + +// Register the endpoint and add it to WooCommerce’s query vars +add_filter('woocommerce_get_query_vars', function($query_vars) { + $query_vars['my-licenses'] = 'my-licenses'; + return $query_vars; +}); + +// Flush rewrite rules to ensure the endpoint is recognized on activation +function slm_flush_rewrite_rules() { + flush_rewrite_rules(); +} +register_activation_hook(__FILE__, 'slm_flush_rewrite_rules'); + +//Add the My Licenses link to WooCommerce account menu +function slm_add_my_licenses_endpoint($items) { + if (SLM_API_Utility::get_slm_option('slm_woo')) { + // Add "My Licenses" item just before "Log out" + $logout = $items['customer-logout']; // Store the "Log out" item + unset($items['customer-logout']); // Remove "Log out" temporarily + + // Add "My Licenses" to the array + $items['my-licenses'] = __('My Licenses', 'slm-plus'); + + // Re-add "Log out" to the end + $items['customer-logout'] = $logout; + } + + return $items; +} +add_filter('woocommerce_account_menu_items', 'slm_add_my_licenses_endpoint'); + +//Display content based on endpoint value +add_action('woocommerce_account_my-licenses_endpoint', function($value) { + + //SLM_Helper_Class::write_log('slm_add_my_licenses_endpoint loaded'); + + if ($value === 'view') { + // Use $_GET instead of get_query_var to directly retrieve the URL parameter + $license_id = isset($_GET['slm_lic']) ? $_GET['slm_lic'] : false; + //SLM_Helper_Class::write_log('license_id var ' . $license_id); + + if ($license_id) { + //SLM_Helper_Class::write_log('license_id call2 ' . $license_id); + slm_license_view($license_id); + } + else { + echo '

    ' . esc_html__('Invalid license or access denied.', 'slm-plus') . '

    '; + //SLM_Helper_Class::write_log('user id ' . get_current_user_id()); + } + } else { + // Display the licenses table if no specific value is passed + slm_display_license_table(); + } +}); + +//Display the main licenses table +function slm_display_license_table() { + + //SLM_Helper_Class::write_log('slm_display_license_table loaded'); + + $user_id = get_current_user_id(); + $user_email = wp_get_current_user()->user_email; + + global $wpdb; + + // Sanitize user inputs + $user_email = sanitize_email($user_email); + $user_id = intval($user_id); + + // Directly using the constant table name as it's not possible to prepare table names + $table_name = SLM_TBL_LICENSE_KEYS; // Ensure that SLM_TBL_LICENSE_KEYS is defined as a constant + + // Use prepare for query construction with placeholders for dynamic values + $query = $wpdb->prepare( + "SELECT DISTINCT license_key, purchase_id_, lic_status + FROM $table_name + WHERE email = %s OR subscr_id = %d", + $user_email, $user_id + ); + + // Execute the query safely + $licenses = $wpdb->get_results($query); + + + if ($licenses) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + foreach ($licenses as $license) { + $order_id = $license->purchase_id_; + $license_key = $license->license_key; + $status = $license->lic_status; + $encoded_license = base64_encode($license_key); + $action_link = esc_url(add_query_arg(['my-licenses' => 'view', 'slm_lic' => $encoded_license], site_url('/my-account/my-licenses'))); + + echo ''; + + // Display Order ID or Custom Message + if (!empty($order_id)) { + echo ''; + } else { + echo ''; + } + + echo ''; + + // Display the status with custom badge classes + echo ''; + + echo ''; + echo ''; + } + + echo ''; + echo ''; + } else { + echo '

    ' . esc_html__('No licenses found.', 'slm-plus') . '

    '; + } + +} + + +add_action('wp_loaded', function () { + if (isset($_GET['renew_license']) && isset($_GET['product_id'])) { + // Sanitize inputs + $license_key = sanitize_text_field($_GET['renew_license']); + $product_id = absint($_GET['product_id']); + + // Validate and process + if (!empty($license_key) && !empty($product_id)) { + slm_direct_renew_and_redirect($license_key, $product_id); + } + } +}); + + + +function slm_direct_renew_and_redirect($license_key, $product_id) { + // Ensure WooCommerce is loaded + if (!function_exists('wc_get_product')) { + error_log('WooCommerce is not loaded. Cannot process renewal.'); + return; + } + + // Prevent redirection loops by checking a custom flag + if (isset($_GET['redirected']) && $_GET['redirected'] === 'true') { + return; // Skip processing if already redirected + } + + // Validate inputs + if (empty($license_key) || empty($product_id)) { + wc_add_notice(__('Invalid license or product.', 'slm-plus'), 'error'); + return; + } + + // Safely retrieve the product + $product = wc_get_product($product_id); + if (!$product || !$product->is_type('simple')) { + wc_add_notice(__('Invalid product for renewal.', 'slm-plus'), 'error'); + return; + } + + // Delay cart operations + add_action('woocommerce_cart_loaded_from_session', function () use ($license_key, $product_id) { + $cart_item_data = [ + 'renew_license_key' => sanitize_text_field($license_key), // Attach the license key + ]; + $added_to_cart = WC()->cart->add_to_cart($product_id, 1, 0, [], $cart_item_data); + + if (!$added_to_cart) { + wc_add_notice(__('Failed to add product to cart.', 'slm-plus'), 'error'); + return; + } + + // Build the redirect URL with a 'redirected' flag + $redirect_url = add_query_arg([ + 'renew_license' => $license_key, + 'product_id' => $product_id, + 'add-to-cart' => $product_id, + 'redirected' => 'true', // Add the flag to prevent loops + ], site_url('/license-cart')); // Update to your cart URL + + // Redirect to the custom cart page + wp_safe_redirect($redirect_url); + exit; + }); +} + +add_action('woocommerce_before_cart', function() { + WC()->cart->get_cart_contents_count(); // Total cart items + error_log(print_r(WC()->cart->get_cart(), true)); // Logs all cart items +}); + + + + + + +// Example usage with enhanced safety checks +// Example usage with enhanced safety checks +add_action('init', function () { + if (isset($_GET['renew_license']) && isset($_GET['product_id'])) { + // Ensure WooCommerce is active + if (!function_exists('wc_get_product')) { + if (!is_plugin_active('woocommerce/woocommerce.php')) { + error_log('WooCommerce is not active. Please enable it to use the SLM Plus plugin.'); + return; + } + } + + // Sanitize input values + $license_key = sanitize_text_field($_GET['renew_license']); + $product_id = absint($_GET['product_id']); + + // Validate required values + if (!empty($license_key) && !empty($product_id)) { + // Start WooCommerce session if not already initialized + if (!WC()->session) { + WC()->initialize_session_handler(); + WC()->session = new WC_Session_Handler(); + WC()->session->init(); + error_log('WooCommerce session initialized.'); + } + + // Store license key in session for later use in checkout + WC()->session->set('renew_license_key', $license_key); + + // Log for debugging + SLM_Helper_Class::write_log("Renew license key set in session during redirect: {$license_key}"); + + // Redirect or process renewal + slm_direct_renew_and_redirect($license_key, $product_id); + } else { + error_log('Missing or invalid license key or product ID for renewal.'); + } + } +}); + + + +// Step 5: Display individual license details +function slm_license_view($encoded_license_id) { + global $wpdb; + $user_email = wp_get_current_user()->user_email; + $user_id = get_current_user_id(); + + // Decode the license key and trim any whitespace + $license_id = trim(base64_decode($encoded_license_id)); + + // Log the decoded license key, user email, and user ID + //SLM_Helper_Class::write_log('Decoded License Key: ' . $license_id); + //SLM_Helper_Class::write_log('User Email: ' . $user_email); + //SLM_Helper_Class::write_log('User ID (subscr_id): ' . $user_id); + + // Construct the query based on whether user ID is available + if ($user_id) { + $query = $wpdb->prepare( + "SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE (email = %s OR subscr_id = %d) AND license_key = %s", + $user_email, $user_id, $license_id + ); + } else { + $query = $wpdb->prepare( + "SELECT * FROM " . SLM_TBL_LICENSE_KEYS . " WHERE email = %s AND license_key = %s", + $user_email, $license_id + ); + } + + // Log the SQL query for debugging + //SLM_Helper_Class::write_log('SQL Query: ' . $query); + + // Execute the query + $license = $wpdb->get_row($query); + + // Check if license was found + if (!$license) { + echo '

    ' . esc_html__('Invalid license or access denied.', 'slm-plus') . '

    '; + //SLM_Helper_Class::write_log('error'); + return; + } + + // Back Button with dynamic URL generation + $back_url = wc_get_account_endpoint_url('my-licenses'); + echo '' . esc_html__('Back to My Licenses', 'slm-plus') . ''; + + // Define the fields and labels for dynamic generation + $slm_license_fields = [ + 'license_key' => __('License Key', 'slm-plus'), + 'lic_status' => __('Status', 'slm-plus'), + 'lic_type' => __('License Type', 'slm-plus'), + 'purchase_id_' => __('Order ID', 'slm-plus'), + 'date_created' => __('Date Created', 'slm-plus'), + 'date_activated' => __('Date Activated', 'slm-plus'), + 'date_renewed' => __('Date Renewed', 'slm-plus'), + 'date_expiry' => __('Date Expiry', 'slm-plus'), + 'product_ref' => __('Product Reference', 'slm-plus'), + 'subscr_id' => __('Subscription ID', 'slm-plus'), + 'subscr_id' => __('Subscription ID', 'slm-plus'), + 'max_allowed_domains' => __('Max Allowed Domains', 'slm-plus'), + 'associated_orders' => __('Associated Orders', 'slm-plus'), + 'company_name' => __('Company Name', 'slm-plus'), + ]; + + // Display license details header + echo '

    ' . esc_html__('License Details', 'slm-plus') . '

    '; + echo ''; + echo ''; + + // Loop through each field and output its label and value dynamically + foreach ($slm_license_fields as $field_key => $field_label) { + // Check if the field is set and get the value + $field_value = isset($license->$field_key) ? esc_html($license->$field_key) : ''; + + + // Special handling for purchase_id_ to link to the order + if ($field_key === 'purchase_id_') { + if (!empty($field_value)) { + // Generate the link to the specific order in the My Account section + $order_link = wc_get_account_endpoint_url('view-order/' . $field_value); + $field_value = '' . esc_html($field_value) . ''; + } else { + $field_value = __('No Order Information Available', 'slm-plus'); + } + } + + + if ($field_key === 'associated_orders') { + if (!empty($field_value)) { + // Fetch the associated orders using the provided function + $associated_orders = SLM_Utility::slm_get_associated_orders($license->license_key); + + // Debugging: Log the retrieved value + SLM_Helper_Class::write_log('Associated Orders Raw Data: ' . print_r($associated_orders, true)); + + if (!empty($associated_orders) && is_array($associated_orders)) { + $links = []; + foreach ($associated_orders as $order_id) { + // Generate a link to the WooCommerce account orders page + $order_url = wc_get_endpoint_url('view-order', $order_id, wc_get_page_permalink('myaccount')); + $links[] = sprintf( + '#%s', + esc_url($order_url), // Sanitize the URL + esc_html($order_id) // Escape and sanitize the order ID + ); + } + + // Join all links with a comma for display + $field_value = implode(', ', $links); + } else { + $field_value = __('No Associated Orders Found (Data Issue)', 'slm-plus'); + } + } else { + $field_value = __('No Order Information Available', 'slm-plus'); + } + } + + + + if ($field_key === 'lic_status') { + $license_key = isset($license->license_key) ? esc_html($license->license_key) : ''; + $purchase_id = isset($license->purchase_id_) ? absint($license->purchase_id_) : 0; + + // Ensure date_expiry is checked safely + $expiration_date = isset($license->date_expiry) ? strtotime($license->date_expiry) : false; + + // Determine if the license is expired + $is_expired = (!empty($expiration_date) && $expiration_date < time()) || $license->lic_status === 'expired'; + + if ($is_expired && !empty($license_key) && !empty($purchase_id)) { + $product_id = 0; // Initialize product_id + + // Ensure WooCommerce functions are available + if (!function_exists('wc_get_order')) { + // Include WooCommerce files to make functions accessible + if (defined('WC_ABSPATH')) { + include_once WC_ABSPATH . 'includes/wc-order-functions.php'; + include_once WC_ABSPATH . 'includes/wc-product-functions.php'; + } else { + // If WooCommerce is not loaded, skip further processing + $field_value = sprintf( + '%s%s', + __('Expired', 'slm-plus'), + __('WooCommerce not loaded.', 'slm-plus') + ); + return; + } + } + + // Retrieve the product ID associated with the license + $order = wc_get_order($purchase_id); + + // Ensure order is valid and contains items + if ($order) { + $items = $order->get_items(); + $product_id = $items ? current($items)->get_product_id() : 0; + } + + if (!empty($product_id)) { + // Generate the renewal URL + $renew_url = add_query_arg([ + 'renew_license' => $license_key, + 'product_id' => $product_id, + ], site_url('/my-account/my-licenses')); + + // Update field value to include Renew button + $field_value = sprintf( + '%s%s', + __('Expired', 'slm-plus'), + esc_url($renew_url), + __('Renew', 'slm-plus') + ); + } else { + // Handle missing product ID case (e.g., display a warning) + $field_value = sprintf( + '%s%s', + __('Expired', 'slm-plus'), + __('Product not found for renewal.', 'slm-plus') + ); + } + } + } + + // Special handling for product_ref to link to the product page + if ($field_key === 'product_ref') { + if (!empty($field_value)) { + // Retrieve the product URL and name by product ID + $product_id = $field_value; + $product_url = get_permalink($product_id); + $product_name = get_the_title($product_id); + + if ($product_url && $product_name) { + // Format as "#ID - ProductName" + $field_value = sprintf( + '#%s - %s', + esc_url($product_url), // Sanitize the URL + esc_html($product_id), // Escape and sanitize the product ID + esc_html($product_name) // Escape and sanitize the product name + ); + } else { + $field_value = __('Product Not Found', 'slm-plus'); + } + } else { + $field_value = __('No Product Information Available', 'slm-plus'); + } + } + + // Special handling for date fields with '0000-00-00' as value + if (($field_key === 'date_activated' || $field_key === 'date_renewed') && $field_value === '0000-00-00') { + $field_value = ($field_key === 'date_activated') ? __('Not activated yet', 'slm-plus') : __('Not renewed yet', 'slm-plus'); + } + + echo ''; + } + echo ''; + echo ''; + + + global $wpdb; + + // Define the license key for querying activations + $license_key = $license->license_key; + + // Fetch all domain and device activations associated with the license key + $domains = $wpdb->get_results($wpdb->prepare( + "SELECT id, 'domain' AS type, registered_domain AS origin FROM " . SLM_TBL_LIC_DOMAIN . " WHERE lic_key = %s", + $license_key + )); + + $devices = $wpdb->get_results($wpdb->prepare( + "SELECT id, 'device' AS type, registered_devices AS origin FROM " . SLM_TBL_LIC_DEVICES . " WHERE lic_key = %s", + $license_key + )); + + // Merge domains and devices into a single array + $activations = array_merge($domains, $devices); + + // Display the "Activations" section header + echo '

    ' . esc_html__('Activations', 'slm-plus') . '

    '; + + // Check if there are any activations + if (empty($activations)) { + echo '

    ' . esc_html__('No activations found.', 'slm-plus') . '

    '; + } else { + // Display the table header if activations exist + echo ''; + echo ''; + echo ''; + + // Loop through each activation and display in the table + foreach ($activations as $activation) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo ''; + echo ''; + + } + + // Handle the deletion request + if (isset($_POST['delete_activation'])) { + $activation_id = intval($_POST['activation_id']); + $activation_type = sanitize_text_field($_POST['activation_type']); + + // Determine the table based on the activation type + $table = ($activation_type === 'domain') ? SLM_TBL_LIC_DOMAIN : SLM_TBL_LIC_DEVICES; + + // Delete the activation record from the relevant table + $deleted = $wpdb->delete($table, ['id' => $activation_id], ['%d']); + + // Display a confirmation or error message + if ($deleted) { + echo '

    ' . esc_html__('Activation successfully deleted. Reload Page.', 'slm-plus') . '

    '; + } else { + echo '

    ' . esc_html__('Failed to delete activation. Please try again.', 'slm-plus') . '

    '; + } + + } +} diff --git a/woocommerce/single-product/add-to-cart/slm_license.php b/woocommerce/single-product/add-to-cart/slm_license.php new file mode 100644 index 0000000..30676eb --- /dev/null +++ b/woocommerce/single-product/add-to-cart/slm_license.php @@ -0,0 +1,63 @@ +is_purchasable()) { + return; +} + +echo wc_get_stock_html($product); // Display stock status if applicable. + +if ($product->is_in_stock()) : + // Determine if this is a renewal (renew_license_key present in URL). + $is_renewal = isset($_GET['renew_license_key']) && !empty($_GET['renew_license_key']); + $renew_license_key = $is_renewal ? sanitize_text_field($_GET['renew_license_key']) : ''; + ?> + + + +
    + + +
    + + +

    + + +

    + + + + + + apply_filters('woocommerce_quantity_input_min', $product->get_min_purchase_quantity(), $product), + 'max_value' => apply_filters('woocommerce_quantity_input_max', $product->get_max_purchase_quantity(), $product), + 'input_value' => isset($_POST['quantity']) ? wc_stock_amount(wp_unslash($_POST['quantity'])) : $product->get_min_purchase_quantity(), // CSRF ok. + ) + ); + do_action('woocommerce_after_add_to_cart_quantity'); + ?> + +
    + + + + +
    + + + +