Post Snippets - Version 4.0.1

Version Description

Download this release

Release Info

Developer wpexpertsio
Plugin Icon 128x128 Post Snippets
Version 4.0.1
Comparing to
See all releases

Code changes from version 4.0 to 4.0.1

Files changed (95) hide show
  1. freemius/assets/css/admin/account.css +1 -1
  2. freemius/assets/css/admin/add-ons.css +1 -2
  3. freemius/assets/css/admin/affiliation.css +1 -1
  4. freemius/assets/css/admin/clone-resolution.css +1 -0
  5. freemius/assets/css/admin/common.css +1 -2
  6. freemius/assets/css/admin/connect.css +1 -1
  7. freemius/assets/css/admin/debug.css +1 -1
  8. freemius/assets/css/admin/dialog-boxes.css +1 -2
  9. freemius/assets/css/admin/gdpr-optin-notice.css +1 -1
  10. freemius/assets/css/admin/optout.css +1 -0
  11. freemius/assets/css/customizer.css +1 -1
  12. freemius/config.php +5 -5
  13. freemius/includes/class-freemius-abstract.php +4 -63
  14. freemius/includes/class-freemius.php +2231 -1055
  15. freemius/includes/class-fs-admin-notices.php +56 -24
  16. freemius/includes/class-fs-api.php +27 -3
  17. freemius/includes/class-fs-lock.php +110 -0
  18. freemius/includes/class-fs-logger.php +2 -1
  19. freemius/includes/class-fs-plugin-updater.php +41 -18
  20. freemius/includes/class-fs-storage.php +106 -80
  21. freemius/includes/class-fs-user-lock.php +10 -46
  22. freemius/includes/customizer/class-fs-customizer-upsell-control.php +2 -2
  23. freemius/includes/entities/class-fs-affiliate-terms.php +4 -0
  24. freemius/includes/entities/class-fs-plugin-tag.php +4 -0
  25. freemius/includes/entities/class-fs-site.php +22 -21
  26. freemius/includes/entities/class-fs-user.php +12 -1
  27. freemius/includes/fs-core-functions.php +10 -22
  28. freemius/includes/fs-essential-functions.php +7 -97
  29. freemius/includes/fs-plugin-info-dialog.php +55 -4
  30. freemius/includes/managers/class-fs-admin-notice-manager.php +75 -20
  31. freemius/includes/managers/class-fs-clone-manager.php +1660 -0
  32. freemius/includes/managers/class-fs-key-value-storage.php +10 -0
  33. freemius/includes/managers/class-fs-option-manager.php +35 -80
  34. freemius/includes/managers/class-fs-permission-manager.php +698 -0
  35. freemius/includes/managers/class-fs-plugin-manager.php +18 -5
  36. freemius/includes/sdk/FreemiusBase.php +169 -171
  37. freemius/includes/sdk/FreemiusWordPress.php +5 -5
  38. freemius/includes/supplements/fs-migration-2.5.1.php +31 -0
  39. freemius/languages/freemius-cs_CZ.mo +0 -0
  40. freemius/languages/freemius-da_DK.mo +0 -0
  41. freemius/languages/freemius-de_DE.mo +0 -0
  42. freemius/languages/freemius-en.mo +0 -0
  43. freemius/languages/freemius-es_ES.mo +0 -0
  44. freemius/languages/freemius-fr_FR.mo +0 -0
  45. freemius/languages/freemius-he_IL.mo +0 -0
  46. freemius/languages/freemius-hu_HU.mo +0 -0
  47. freemius/languages/freemius-it_IT.mo +0 -0
  48. freemius/languages/freemius-ja.mo +0 -0
  49. freemius/languages/freemius-nl_NL.mo +0 -0
  50. freemius/languages/freemius-ru_RU.mo +0 -0
  51. freemius/languages/freemius-ta.mo +0 -0
  52. freemius/languages/freemius-zh_CN.mo +0 -0
  53. freemius/languages/freemius.pot +868 -630
  54. freemius/require.php +2 -0
  55. freemius/start.php +6 -2
  56. freemius/templates/account.php +65 -30
  57. freemius/templates/account/billing.php +1 -1
  58. freemius/templates/account/partials/disconnect-button.php +104 -0
  59. freemius/templates/account/partials/site.php +8 -6
  60. freemius/templates/account/payments.php +10 -11
  61. freemius/templates/admin-notice.php +4 -1
  62. freemius/templates/auto-installation.php +1 -1
  63. freemius/templates/clone-resolution-js.php +79 -0
  64. freemius/templates/connect.php +153 -144
  65. freemius/templates/connect/index.php +3 -0
  66. freemius/templates/connect/permission.php +43 -0
  67. freemius/templates/connect/permissions-group.php +72 -0
  68. freemius/templates/contact.php +1 -1
  69. freemius/templates/debug.php +78 -16
  70. freemius/templates/firewall-issues-js.php +1 -2
  71. freemius/templates/forms/affiliation.php +6 -6
  72. freemius/templates/forms/data-debug-mode.php +1 -1
  73. freemius/templates/forms/deactivation/form.php +174 -51
  74. freemius/templates/forms/email-address-update.php +347 -0
  75. freemius/templates/forms/license-activation.php +38 -18
  76. freemius/templates/forms/optout.php +147 -301
  77. freemius/templates/forms/resend-key.php +5 -2
  78. freemius/templates/forms/trial-start.php +1 -1
  79. freemius/templates/forms/user-change.php +1 -1
  80. freemius/templates/gdpr-optin-js.php +1 -1
  81. freemius/templates/js/permissions.php +546 -0
  82. freemius/templates/partials/network-activation.php +1 -1
  83. freemius/templates/plugin-icon.php +3 -1
  84. freemius/templates/plugin-info/description.php +1 -1
  85. freemius/templates/plugin-info/features.php +1 -1
  86. freemius/templates/powered-by.php +7 -7
  87. freemius/templates/pricing.php +12 -11
  88. freemius/templates/sticky-admin-notice-js.php +1 -2
  89. freemius/templates/tabs-capture-js.php +1 -1
  90. phpunit.xml +14 -0
  91. post-snippets.php +50 -50
  92. readme.txt +16 -6
  93. src/PostSnippets/Edit.php +224 -202
  94. src/PostSnippets/ImportExport.php +35 -55
  95. src/PostSnippets/WPEditor.php +1 -1
freemius/assets/css/admin/account.css CHANGED
@@ -1 +1 @@
1
- label.fs-tag,span.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.fs-notice[data-id="license_not_whitelabeled"].success,.fs-notice[data-id="license_whitelabeled"].success{color:inherit;border-left-color:#00a0d2}.fs-notice[data-id="license_not_whitelabeled"].success label.fs-plugin-title,.fs-notice[data-id="license_whitelabeled"].success label.fs-plugin-title{display:none}#fs_account .postbox,#fs_account .widefat{max-width:800px}#fs_account h3{font-size:1.3em;padding:12px 15px;margin:0 0 12px 0;line-height:1.4;border-bottom:1px solid #F1F1F1}#fs_account h3 .dashicons{width:26px;height:26px;font-size:1.3em}#fs_account i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .dashicons{vertical-align:middle}#fs_account .fs-header-actions{position:absolute;top:17px;right:15px;font-size:0.9em}#fs_account .fs-header-actions ul{margin:0}#fs_account .fs-header-actions li{float:left}#fs_account .fs-header-actions li form{display:inline-block}#fs_account .fs-header-actions li a{text-decoration:none}#fs_account_details .button-group{float:right}.rtl #fs_account .fs-header-actions{left:15px;right:auto}.fs-key-value-table{width:100%}.fs-key-value-table form{display:inline-block}.fs-key-value-table tr td:first-child{text-align:right}.fs-key-value-table tr td:first-child nobr{font-weight:bold}.fs-key-value-table tr td:first-child form{display:block}.fs-key-value-table tr td.fs-right{text-align:right}.fs-key-value-table tr.fs-odd{background:#ebebeb}.fs-key-value-table td,.fs-key-value-table th{padding:10px}.fs-key-value-table code{line-height:28px}.fs-key-value-table var,.fs-key-value-table code,.fs-key-value-table input[type="text"]{color:#0073AA;font-size:16px;background:none}.fs-key-value-table input[type="text"]{width:100%;font-weight:bold}.fs-field-beta_program label{margin-left:7px}label.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error{background:#dc3232}#fs_sites .fs-scrollable-table .fs-table-body{max-height:200px;overflow:auto;border:1px solid #e5e5e5}#fs_sites .fs-scrollable-table .fs-table-body>table.widefat{border:none !important}#fs_sites .fs-scrollable-table .fs-main-column{width:100%}#fs_sites .fs-scrollable-table .fs-site-details td:first-of-type{text-align:right;color:grey;width:1px}#fs_sites .fs-scrollable-table .fs-site-details td:last-of-type{text-align:right}#fs_sites .fs-scrollable-table .fs-install-details table tr td{width:1px;white-space:nowrap}#fs_sites .fs-scrollable-table .fs-install-details table tr td:last-of-type{width:auto}#fs_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#fs_addons thead{white-space:nowrap}#fs_addons td:first-child,#fs_addons th:first-child{text-align:left;font-weight:bold}#fs_addons td:last-child,#fs_addons th:last-child{text-align:right}#fs_addons th{font-weight:bold}#fs_billing_address{width:100%}#fs_billing_address tr td{width:50%;padding:5px}#fs_billing_address tr:first-of-type td{padding-top:0}#fs_billing_address span{font-weight:bold}#fs_billing_address input,#fs_billing_address select{display:block;width:100%;margin-top:5px}#fs_billing_address input::-moz-placeholder,#fs_billing_address select::-moz-placeholder{color:transparent;opacity:1}#fs_billing_address input:-ms-input-placeholder,#fs_billing_address select:-ms-input-placeholder{color:transparent}#fs_billing_address input::-webkit-input-placeholder,#fs_billing_address select::-webkit-input-placeholder{color:transparent}#fs_billing_address input.fs-read-mode,#fs_billing_address select.fs-read-mode{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode td span{display:none}#fs_billing_address.fs-read-mode input,#fs_billing_address.fs-read-mode select{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode input::-moz-placeholder,#fs_billing_address.fs-read-mode select::-moz-placeholder{color:#ccc;opacity:1}#fs_billing_address.fs-read-mode input:-ms-input-placeholder,#fs_billing_address.fs-read-mode select:-ms-input-placeholder{color:#ccc}#fs_billing_address.fs-read-mode input::-webkit-input-placeholder,#fs_billing_address.fs-read-mode select::-webkit-input-placeholder{color:#ccc}#fs_billing_address button{display:block;width:100%}
1
+ label.fs-tag,span.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.fs-notice[data-id=license_not_whitelabeled].success,.fs-notice[data-id=license_whitelabeled].success{color:inherit;border-left-color:#00a0d2}.fs-notice[data-id=license_not_whitelabeled].success label.fs-plugin-title,.fs-notice[data-id=license_whitelabeled].success label.fs-plugin-title{display:none}#fs_account .postbox,#fs_account .widefat{max-width:800px}#fs_account h3{font-size:1.3em;padding:12px 15px;margin:0 0 12px 0;line-height:1.4;border-bottom:1px solid #f1f1f1}#fs_account h3 .dashicons{width:26px;height:26px;font-size:1.3em}#fs_account i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .dashicons{vertical-align:middle}#fs_account .fs-header-actions{position:absolute;top:17px;right:15px;font-size:.9em}#fs_account .fs-header-actions ul{margin:0}#fs_account .fs-header-actions li{float:left}#fs_account .fs-header-actions li form{display:inline-block}#fs_account .fs-header-actions li a{text-decoration:none}#fs_account_details .button-group{float:right}.rtl #fs_account .fs-header-actions{left:15px;right:auto}.fs-key-value-table{width:100%}.fs-key-value-table form{display:inline-block}.fs-key-value-table tr td:first-child{text-align:right}.fs-key-value-table tr td:first-child nobr{font-weight:bold}.fs-key-value-table tr td:first-child form{display:block}.fs-key-value-table tr td.fs-right{text-align:right}.fs-key-value-table tr.fs-odd{background:#ebebeb}.fs-key-value-table td,.fs-key-value-table th{padding:10px}.fs-key-value-table code{line-height:28px}.fs-key-value-table var,.fs-key-value-table code,.fs-key-value-table input[type=text]{color:#0073aa;font-size:16px;background:none}.fs-key-value-table input[type=text]{width:100%;font-weight:bold}.fs-field-beta_program label{margin-left:7px}label.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error{background:#dc3232}#fs_sites .fs-scrollable-table .fs-table-body{max-height:200px;overflow:auto;border:1px solid #e5e5e5}#fs_sites .fs-scrollable-table .fs-table-body>table.widefat{border:none !important}#fs_sites .fs-scrollable-table .fs-main-column{width:100%}#fs_sites .fs-scrollable-table .fs-site-details td:first-of-type{text-align:right;color:gray;width:1px}#fs_sites .fs-scrollable-table .fs-site-details td:last-of-type{text-align:right}#fs_sites .fs-scrollable-table .fs-install-details table tr td{width:1px;white-space:nowrap}#fs_sites .fs-scrollable-table .fs-install-details table tr td:last-of-type{width:auto}#fs_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#fs_addons thead{white-space:nowrap}#fs_addons td:first-child,#fs_addons th:first-child{text-align:left;font-weight:bold}#fs_addons td:last-child,#fs_addons th:last-child{text-align:right}#fs_addons th{font-weight:bold}#fs_billing_address{width:100%}#fs_billing_address tr td{width:50%;padding:5px}#fs_billing_address tr:first-of-type td{padding-top:0}#fs_billing_address span{font-weight:bold}#fs_billing_address input,#fs_billing_address select{display:block;width:100%;margin-top:5px}#fs_billing_address input::-moz-placeholder,#fs_billing_address select::-moz-placeholder{color:transparent;opacity:1}#fs_billing_address input:-ms-input-placeholder,#fs_billing_address select:-ms-input-placeholder{color:transparent}#fs_billing_address input::-webkit-input-placeholder,#fs_billing_address select::-webkit-input-placeholder{color:transparent}#fs_billing_address input.fs-read-mode,#fs_billing_address select.fs-read-mode{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode td span{display:none}#fs_billing_address.fs-read-mode input,#fs_billing_address.fs-read-mode select{border-color:transparent;color:#777;border-bottom:1px dashed #ccc;padding-left:0;background:none}#fs_billing_address.fs-read-mode input::-moz-placeholder,#fs_billing_address.fs-read-mode select::-moz-placeholder{color:#ccc;opacity:1}#fs_billing_address.fs-read-mode input:-ms-input-placeholder,#fs_billing_address.fs-read-mode select:-ms-input-placeholder{color:#ccc}#fs_billing_address.fs-read-mode input::-webkit-input-placeholder,#fs_billing_address.fs-read-mode select::-webkit-input-placeholder{color:#ccc}#fs_billing_address button{display:block;width:100%}@media screen and (max-width: 639px){#fs_account .fs-header-actions{position:static;padding:0 15px 12px 15px;margin:0 0 12px 0}#fs_account .fs-header-actions li{float:none;display:inline-block}#fs_account #fs_account_details{display:block}#fs_account #fs_account_details tbody,#fs_account #fs_account_details tr,#fs_account #fs_account_details td,#fs_account #fs_account_details th{display:block}#fs_account #fs_account_details tr td:first-child{text-align:left}#fs_account #fs_account_details tr td:nth-child(2){padding:0 12px}#fs_account #fs_account_details tr td:nth-child(2) code{margin:0;padding:0}#fs_account #fs_account_details tr td:nth-child(2) label{margin-left:0}#fs_account #fs_account_details tr td:nth-child(3){text-align:left}#fs_account #fs_account_details tr.fs-field-plan td:nth-child(2) .button-group{float:none;margin:12px 0}}
freemius/assets/css/admin/add-ons.css CHANGED
@@ -1,2 +1 @@
1
- .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:white;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);box-shadow:0 2px 1px -1px rgba(0,0,0,0.3)}#fs_addons .fs-cards-list{list-style:none}#fs_addons .fs-cards-list .fs-card{float:left;height:152px;width:310px;padding:0;margin:0 0 30px 30px;font-size:14px;list-style:none;border:1px solid #ddd;cursor:pointer;position:relative}#fs_addons .fs-cards-list .fs-card .fs-overlay{position:absolute;left:0;right:0;bottom:0;top:0;z-index:9}#fs_addons .fs-cards-list .fs-card .fs-inner{background-color:#fff;overflow:hidden;height:100%;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner>ul{-moz-transition:all,0.15s;-o-transition:all,0.15s;-ms-transition:all,0.15s;-webkit-transition:all,0.15s;transition:all,0.15s;left:0;right:0;top:0;position:absolute}#fs_addons .fs-cards-list .fs-card .fs-inner>ul>li{list-style:none;line-height:18px;padding:0 15px;width:100%;display:block;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner{padding:0;margin:0;line-height:0;display:block;height:100px;background-repeat:repeat-x;background-size:100% 100%;-moz-transition:all,0.15s;-o-transition:all,0.15s;-ms-transition:all,0.15s;-webkit-transition:all,0.15s;transition:all,0.15s}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner .fs-badge.fs-installed-addon-badge{font-size:1.02em;line-height:1.3em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-title{margin:10px 0 0 0;height:18px;overflow:hidden;color:#000;white-space:nowrap;text-overflow:ellipsis;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-offer{font-size:0.9em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-description{background-color:#f9f9f9;padding:10px 15px 100px 15px;border-top:1px solid #eee;margin:0 0 10px 0;color:#777}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-tag{position:absolute;top:10px;right:0px;background:greenyellow;display:block;padding:2px 10px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.3);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.3);box-shadow:1px 1px 1px rgba(0,0,0,0.3);text-transform:uppercase;font-size:0.9em;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button,#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button-group{position:absolute;top:112px;right:10px}@media screen and (min-width: 960px){#fs_addons .fs-cards-list .fs-card:hover .fs-overlay{border:2px solid #29abe1;margin-left:-1px;margin-top:-1px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner ul{top:-100px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-title,#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-offer{color:#29abe1}}
2
- #TB_window,#TB_window iframe{width:821px !important}#plugin-information .fyi{width:266px !important}#plugin-information #section-holder{margin-right:299px}#plugin-information #section-description h2,#plugin-information #section-description h3,#plugin-information #section-description p,#plugin-information #section-description b,#plugin-information #section-description i,#plugin-information #section-description blockquote,#plugin-information #section-description li,#plugin-information #section-description ul,#plugin-information #section-description ol{clear:none}#plugin-information #section-description iframe{max-width:100%}#plugin-information #section-description .fs-selling-points{padding-bottom:10px;border-bottom:1px solid #ddd}#plugin-information #section-description .fs-selling-points ul{margin:0}#plugin-information #section-description .fs-selling-points ul li{padding:0;list-style:none outside none}#plugin-information #section-description .fs-selling-points ul li i.dashicons{color:#71ae00;font-size:3em;vertical-align:middle;line-height:30px;float:left;margin:0 0 0 -15px}#plugin-information #section-description .fs-selling-points ul li h3{margin:1em 30px !important}#plugin-information #section-description .fs-screenshots:after{content:"";display:table;clear:both}#plugin-information #section-description .fs-screenshots ul{list-style:none;margin:0}#plugin-information #section-description .fs-screenshots ul li{width:225px;height:225px;float:left;margin-bottom:20px;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}#plugin-information #section-description .fs-screenshots ul li a{display:block;width:100%;height:100%;border:1px solid;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);background-size:cover}#plugin-information #section-description .fs-screenshots ul li.odd{margin-right:20px}#plugin-information .plugin-information-pricing{margin:-16px;border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan h3{margin-top:0;padding:20px;font-size:16px}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper{border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab{cursor:pointer;position:relative;padding:0 10px;font-size:0.9em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab label{text-transform:uppercase;color:green;background:greenyellow;position:absolute;left:-1px;right:-1px;bottom:100%;border:1px solid darkgreen;padding:2px;text-align:center;font-size:0.9em;line-height:1em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab.nav-tab-active{cursor:default;background:#fffeec;border-bottom-color:#fffeec}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle h3{background:#fffeec;margin:0;padding-bottom:0;color:#0073aa}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .nav-tab-wrapper,#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .fs-billing-frequency{display:none}#plugin-information .plugin-information-pricing .fs-plan .fs-pricing-body{background:#fffeec;padding:20px}#plugin-information .plugin-information-pricing .fs-plan .button{width:100%;text-align:center;font-weight:bold;text-transform:uppercase;font-size:1.1em}#plugin-information .plugin-information-pricing .fs-plan label{white-space:nowrap}#plugin-information .plugin-information-pricing .fs-plan var{font-style:normal}#plugin-information .plugin-information-pricing .fs-plan .fs-billing-frequency,#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-align:center;display:block;font-weight:bold;margin-bottom:10px;text-transform:uppercase;background:#F3F3F3;padding:2px;border:1px solid #ccc}#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-transform:none;color:green;background:greenyellow}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms{font-size:0.9em}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms i{float:left;margin:0 0 0 -15px}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms li{margin:10px 0 0 0}#plugin-information #section-features .fs-features{margin:-20px -26px}#plugin-information #section-features table{width:100%;border-spacing:0;border-collapse:separate}#plugin-information #section-features table thead th{padding:10px 0}#plugin-information #section-features table thead .fs-price{color:#71ae00;font-weight:normal;display:block;text-align:center}#plugin-information #section-features table tbody td{border-top:1px solid #ccc;padding:10px 0;text-align:center;width:100px;color:#71ae00}#plugin-information #section-features table tbody td:first-child{text-align:left;width:auto;color:inherit;padding-left:26px}#plugin-information #section-features table tbody tr.fs-odd td{background:#fefefe}#plugin-information #section-features .dashicons-yes{width:30px;height:30px;font-size:30px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button,#plugin-information .fs-dropdown .button-group .button{position:relative;width:auto;top:0;right:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button:focus,#plugin-information .fs-dropdown .button-group .button:focus{z-index:10}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .fs-dropdown-arrow,#plugin-information .fs-dropdown .button-group .fs-dropdown-arrow{border-top:6px solid white;border-right:4px solid transparent;border-left:4px solid transparent;top:12px;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button){border-bottom-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button{border-bottom-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button){border-top-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active.up .fs-dropdown-arrow-button{border-top-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list,#plugin-information .fs-dropdown .fs-dropdown-list{position:absolute;right:-1px;top:100%;margin-left:auto;padding:3px 0;border:1px solid #bfbfbf;background-color:#fff;z-index:1;width:230px;text-align:left;-moz-box-shadow:0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12);-webkit-box-shadow:0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12);box-shadow:0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li,#plugin-information .fs-dropdown .fs-dropdown-list li{margin:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li a,#plugin-information .fs-dropdown .fs-dropdown-list li a{display:block;padding:5px 10px;text-decoration:none;text-shadow:none}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover,#plugin-information .fs-dropdown .fs-dropdown-list li:hover{background-color:#0074a3;color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover a,#plugin-information .fs-dropdown .fs-dropdown-list li:hover a{color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown:not(.up) .fs-dropdown-list,#plugin-information .fs-dropdown:not(.up) .fs-dropdown-list{-moz-border-radius:3px 0 3px 3px;-webkit-border-radius:3px 0 3px 3px;border-radius:3px 0 3px 3px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.up .fs-dropdown-list,#plugin-information .fs-dropdown.up .fs-dropdown-list{-moz-border-radius:3px 3px 0 3px;-webkit-border-radius:3px 3px 0 3px;border-radius:3px 3px 0 3px}#plugin-information .fs-dropdown .button-group{width:100%}#plugin-information .fs-dropdown .button-group .button{float:none;font-size:14px;font-weight:normal;text-transform:none}#plugin-information .fs-dropdown .fs-dropdown-list{margin-top:1px}#plugin-information .fs-dropdown.up .fs-dropdown-list{top:auto;bottom:100%;margin-bottom:2px}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group{text-align:center;display:table}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button{display:table-cell}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button:not(.fs-dropdown-arrow-button){left:1px;width:100%}#plugin-information-footer>.button,#plugin-information-footer .fs-dropdown{position:relative;top:3px}#plugin-information-footer>.button.left,#plugin-information-footer .fs-dropdown.left{float:left}#plugin-information-footer>.right,#plugin-information-footer .fs-dropdown{float:right}@media screen and (max-width: 961px){#fs_addons .fs-cards-list .fs-card{height:265px}}
1
+ .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:#fff;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);box-shadow:0 2px 1px -1px rgba(0,0,0,.3)}#fs_addons .fs-cards-list{list-style:none}#fs_addons .fs-cards-list .fs-card{float:left;height:152px;width:310px;padding:0;margin:0 0 30px 30px;font-size:14px;list-style:none;border:1px solid #ddd;cursor:pointer;position:relative}#fs_addons .fs-cards-list .fs-card .fs-overlay{position:absolute;left:0;right:0;bottom:0;top:0;z-index:9}#fs_addons .fs-cards-list .fs-card .fs-inner{background-color:#fff;overflow:hidden;height:100%;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner>ul{-moz-transition:all,.15s;-o-transition:all,.15s;-ms-transition:all,.15s;-webkit-transition:all,.15s;transition:all,.15s;left:0;right:0;top:0;position:absolute}#fs_addons .fs-cards-list .fs-card .fs-inner>ul>li{list-style:none;line-height:18px;padding:0 15px;width:100%;display:block;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner{padding:0;margin:0;line-height:0;display:block;height:100px;background-repeat:repeat-x;background-size:100% 100%;-moz-transition:all,.15s;-o-transition:all,.15s;-ms-transition:all,.15s;-webkit-transition:all,.15s;transition:all,.15s}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner .fs-badge.fs-installed-addon-badge{font-size:1.02em;line-height:1.3em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-title{margin:10px 0 0 0;height:18px;overflow:hidden;color:#000;white-space:nowrap;text-overflow:ellipsis;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-offer{font-size:.9em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-description{background-color:#f9f9f9;padding:10px 15px 100px 15px;border-top:1px solid #eee;margin:0 0 10px 0;color:#777}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-tag{position:absolute;top:10px;right:0px;background:#adff2f;display:block;padding:2px 10px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.3);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.3);box-shadow:1px 1px 1px rgba(0,0,0,.3);text-transform:uppercase;font-size:.9em;font-weight:bold}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button,#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button-group{position:absolute;top:112px;right:10px}@media screen and (min-width: 960px){#fs_addons .fs-cards-list .fs-card:hover .fs-overlay{border:2px solid #29abe1;margin-left:-1px;margin-top:-1px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner ul{top:-100px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-title,#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-offer{color:#29abe1}}#TB_window,#TB_window iframe{width:821px !important}#plugin-information .fyi{width:266px !important}#plugin-information #section-holder{margin-right:299px;clear:none}#plugin-information #section-description h2,#plugin-information #section-description h3,#plugin-information #section-description p,#plugin-information #section-description b,#plugin-information #section-description i,#plugin-information #section-description blockquote,#plugin-information #section-description li,#plugin-information #section-description ul,#plugin-information #section-description ol{clear:none}#plugin-information #section-description iframe{max-width:100%}#plugin-information #section-description .fs-selling-points{padding-bottom:10px;border-bottom:1px solid #ddd}#plugin-information #section-description .fs-selling-points ul{margin:0}#plugin-information #section-description .fs-selling-points ul li{padding:0;list-style:none outside none}#plugin-information #section-description .fs-selling-points ul li i.dashicons{color:#71ae00;font-size:3em;vertical-align:middle;line-height:30px;float:left;margin:0 0 0 -15px}#plugin-information #section-description .fs-selling-points ul li h3{margin:1em 30px !important}#plugin-information #section-description .fs-screenshots:after{content:"";display:table;clear:both}#plugin-information #section-description .fs-screenshots ul{list-style:none;margin:0}#plugin-information #section-description .fs-screenshots ul li{width:225px;height:225px;float:left;margin-bottom:20px;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}#plugin-information #section-description .fs-screenshots ul li a{display:block;width:100%;height:100%;border:1px solid;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);background-size:cover}#plugin-information #section-description .fs-screenshots ul li.odd{margin-right:20px}#plugin-information .plugin-information-pricing{margin:-16px;border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan h3{margin-top:0;padding:20px;font-size:16px}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper{border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab{cursor:pointer;position:relative;padding:0 10px;font-size:.9em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab label{text-transform:uppercase;color:green;background:#adff2f;position:absolute;left:-1px;right:-1px;bottom:100%;border:1px solid #006400;padding:2px;text-align:center;font-size:.9em;line-height:1em}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab.nav-tab-active{cursor:default;background:#fffeec;border-bottom-color:#fffeec}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle h3{background:#fffeec;margin:0;padding-bottom:0;color:#0073aa}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .nav-tab-wrapper,#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .fs-billing-frequency{display:none}#plugin-information .plugin-information-pricing .fs-plan .fs-pricing-body{background:#fffeec;padding:20px}#plugin-information .plugin-information-pricing .fs-plan .button{width:100%;text-align:center;font-weight:bold;text-transform:uppercase;font-size:1.1em}#plugin-information .plugin-information-pricing .fs-plan label{white-space:nowrap}#plugin-information .plugin-information-pricing .fs-plan var{font-style:normal}#plugin-information .plugin-information-pricing .fs-plan .fs-billing-frequency,#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-align:center;display:block;font-weight:bold;margin-bottom:10px;text-transform:uppercase;background:#f3f3f3;padding:2px;border:1px solid #ccc}#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{text-transform:none;color:green;background:#adff2f}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms{font-size:.9em}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms i{float:left;margin:0 0 0 -15px}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms li{margin:10px 0 0 0}#plugin-information #section-features .fs-features{margin:-20px -26px}#plugin-information #section-features table{width:100%;border-spacing:0;border-collapse:separate}#plugin-information #section-features table thead th{padding:10px 0}#plugin-information #section-features table thead .fs-price{color:#71ae00;font-weight:normal;display:block;text-align:center}#plugin-information #section-features table tbody td{border-top:1px solid #ccc;padding:10px 0;text-align:center;width:100px;color:#71ae00}#plugin-information #section-features table tbody td:first-child{text-align:left;width:auto;color:inherit;padding-left:26px}#plugin-information #section-features table tbody tr.fs-odd td{background:#fefefe}#plugin-information #section-features .dashicons-yes{width:30px;height:30px;font-size:30px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button,#plugin-information .fs-dropdown .button-group .button{position:relative;width:auto;top:0;right:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button:focus,#plugin-information .fs-dropdown .button-group .button:focus{z-index:10}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .fs-dropdown-arrow,#plugin-information .fs-dropdown .button-group .fs-dropdown-arrow{border-top:6px solid #fff;border-right:4px solid transparent;border-left:4px solid transparent;top:12px;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button){border-bottom-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button{border-bottom-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button){border-top-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active.up .fs-dropdown-arrow-button{border-top-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list,#plugin-information .fs-dropdown .fs-dropdown-list{position:absolute;right:-1px;top:100%;margin-left:auto;padding:3px 0;border:1px solid #bfbfbf;background-color:#fff;z-index:1;width:230px;text-align:left;-moz-box-shadow:0px 2px 4px -1px rgba(0,0,0,.2),0px 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px 0px rgba(0,0,0,.12);-webkit-box-shadow:0px 2px 4px -1px rgba(0,0,0,.2),0px 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px 0px rgba(0,0,0,.12);box-shadow:0px 2px 4px -1px rgba(0,0,0,.2),0px 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px 0px rgba(0,0,0,.12)}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li,#plugin-information .fs-dropdown .fs-dropdown-list li{margin:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li a,#plugin-information .fs-dropdown .fs-dropdown-list li a{display:block;padding:5px 10px;text-decoration:none;text-shadow:none}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover,#plugin-information .fs-dropdown .fs-dropdown-list li:hover{background-color:#0074a3;color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover a,#plugin-information .fs-dropdown .fs-dropdown-list li:hover a{color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown:not(.up) .fs-dropdown-list,#plugin-information .fs-dropdown:not(.up) .fs-dropdown-list{-moz-border-radius:3px 0 3px 3px;-webkit-border-radius:3px 0 3px 3px;border-radius:3px 0 3px 3px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.up .fs-dropdown-list,#plugin-information .fs-dropdown.up .fs-dropdown-list{-moz-border-radius:3px 3px 0 3px;-webkit-border-radius:3px 3px 0 3px;border-radius:3px 3px 0 3px}#plugin-information .fs-dropdown .button-group{width:100%}#plugin-information .fs-dropdown .button-group .button{float:none;font-size:14px;font-weight:normal;text-transform:none}#plugin-information .fs-dropdown .fs-dropdown-list{margin-top:1px}#plugin-information .fs-dropdown.up .fs-dropdown-list{top:auto;bottom:100%;margin-bottom:2px}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group{text-align:center;display:table}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button{display:table-cell}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button:not(.fs-dropdown-arrow-button){left:1px;width:100%}#plugin-information-footer>.button,#plugin-information-footer .fs-dropdown{position:relative;top:3px}#plugin-information-footer>.button.left,#plugin-information-footer .fs-dropdown.left{float:left}#plugin-information-footer>.right,#plugin-information-footer .fs-dropdown{float:right}@media screen and (max-width: 961px){#fs_addons .fs-cards-list .fs-card{height:265px}}
 
freemius/assets/css/admin/affiliation.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";#fs_affiliation_content_wrapper #messages{margin-top:25px}#fs_affiliation_content_wrapper h3{font-size:24px;padding:0;margin-left:0}#fs_affiliation_content_wrapper ul li{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;list-style-type:none}#fs_affiliation_content_wrapper ul li:before{content:'';margin-right:10px;font-weight:bold}#fs_affiliation_content_wrapper p:not(.description),#fs_affiliation_content_wrapper li,#fs_affiliation_content_wrapper label{font-size:16px !important;line-height:26px !important}#fs_affiliation_content_wrapper .button{margin-top:20px;margin-bottom:7px;line-height:35px;height:40px;font-size:16px}#fs_affiliation_content_wrapper .button#cancel_button{margin-right:5px}#fs_affiliation_content_wrapper form .input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form .input-container .input-label{font-weight:bold;display:block;width:100%}#fs_affiliation_content_wrapper form .input-container.input-container-text label,#fs_affiliation_content_wrapper form .input-container.input-container-text input,#fs_affiliation_content_wrapper form .input-container.input-container-text textarea{display:block}#fs_affiliation_content_wrapper form .input-container #add_domain,#fs_affiliation_content_wrapper form .input-container .remove-domain{text-decoration:none;display:inline-block;margin-top:3px}#fs_affiliation_content_wrapper form .input-container #add_domain:focus,#fs_affiliation_content_wrapper form .input-container .remove-domain:focus{box-shadow:none}#fs_affiliation_content_wrapper form .input-container #add_domain.disabled,#fs_affiliation_content_wrapper form .input-container .remove-domain.disabled{color:#aaa;cursor:default}#fs_affiliation_content_wrapper form #extra_domains_container .description{margin-top:0;position:relative;top:-4px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain{display:inline-block;margin-right:5px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain:last-of-type{margin-bottom:0}
1
+ #fs_affiliation_content_wrapper #messages{margin-top:25px}#fs_affiliation_content_wrapper h3{font-size:24px;padding:0;margin-left:0}#fs_affiliation_content_wrapper ul li{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;list-style-type:none}#fs_affiliation_content_wrapper ul li:before{content:"";margin-right:10px;font-weight:bold}#fs_affiliation_content_wrapper p:not(.description),#fs_affiliation_content_wrapper li,#fs_affiliation_content_wrapper label{font-size:16px !important;line-height:26px !important}#fs_affiliation_content_wrapper .button{margin-top:20px;margin-bottom:7px;line-height:35px;height:40px;font-size:16px}#fs_affiliation_content_wrapper .button#cancel_button{margin-right:5px}#fs_affiliation_content_wrapper form .input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form .input-container .input-label{font-weight:bold;display:block;width:100%}#fs_affiliation_content_wrapper form .input-container.input-container-text label,#fs_affiliation_content_wrapper form .input-container.input-container-text input,#fs_affiliation_content_wrapper form .input-container.input-container-text textarea{display:block}#fs_affiliation_content_wrapper form .input-container #add_domain,#fs_affiliation_content_wrapper form .input-container .remove-domain{text-decoration:none;display:inline-block;margin-top:3px}#fs_affiliation_content_wrapper form .input-container #add_domain:focus,#fs_affiliation_content_wrapper form .input-container .remove-domain:focus{box-shadow:none}#fs_affiliation_content_wrapper form .input-container #add_domain.disabled,#fs_affiliation_content_wrapper form .input-container .remove-domain.disabled{color:#aaa;cursor:default}#fs_affiliation_content_wrapper form #extra_domains_container .description{margin-top:0;position:relative;top:-4px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain{display:inline-block;margin-right:5px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain:last-of-type{margin-bottom:0}
freemius/assets/css/admin/clone-resolution.css ADDED
@@ -0,0 +1 @@
 
1
+ .fs-notice[data-id^=clone_resolution_options_notice]{padding:0;color:inherit !important}.fs-notice[data-id^=clone_resolution_options_notice] .fs-notice-body{padding:0;margin-bottom:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-notice-header{padding:5px 10px}.fs-notice[data-id^=clone_resolution_options_notice] ol{margin-top:0;margin-bottom:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-options-container{display:flex;flex-direction:row;padding:0 10px 10px}@media(max-width: 750px){.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-options-container{flex-direction:column}}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option{border:1px solid #ccc;padding:10px 10px 15px 10px;flex:auto;margin:5px}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option:first-child{margin-left:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option:last-child{margin-right:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option strong{font-size:1.2em;padding:2px;line-height:1.5em}.fs-notice[data-id^=clone_resolution_options_notice] a{text-decoration:none}.fs-notice[data-id^=clone_resolution_options_notice] .button{margin-right:10px}.rtl .fs-notice[data-id^=clone_resolution_options_notice] .button{margin-right:0;margin-left:10px}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-documentation-container{padding:0 10px 15px}.fs-notice[data-id=temporary_duplicate_notice] #fs_clone_resolution_error_message{border:1px solid #d3135a;background:#fee;color:#d3135a;padding:10px}.fs-notice[data-id=temporary_duplicate_notice] ol{margin-top:0}
freemius/assets/css/admin/common.css CHANGED
@@ -1,2 +1 @@
1
- .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:white;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,0.3);box-shadow:0 2px 1px -1px rgba(0,0,0,0.3)}.theme-browser .theme .fs-premium-theme-badge-container{position:absolute;right:0;top:0}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge{position:relative;top:0;margin-top:10px;text-align:center}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-premium-theme-badge{font-size:1.1em}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-beta-theme-badge{background:#00a0d2}.fs-switch{position:relative;display:inline-block;color:#ccc;text-shadow:0 1px 1px rgba(255,255,255,0.8);height:18px;padding:6px 6px 5px 6px;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);background:#ececec;box-shadow:0 0 4px rgba(0,0,0,0.1),inset 0 1px 3px 0 rgba(0,0,0,0.1);cursor:pointer}.fs-switch span{display:inline-block;width:35px;text-transform:uppercase}.fs-switch .fs-toggle{position:absolute;top:1px;width:37px;height:25px;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.3);border-radius:4px;background:#fff;background-color:#fff;background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ececec), color-stop(1, #fff));background-image:-webkit-linear-gradient(top, #ececec, #fff);background-image:-moz-linear-gradient(top, #ececec, #fff);background-image:-ms-linear-gradient(top, #ececec, #fff);background-image:-o-linear-gradient(top, #ececec, #fff);background-image:linear-gradient(top, bottom, #ececec, #fff);box-shadow:inset 0 1px 0 0 rgba(255,255,255,0.5);z-index:999;-moz-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);-o-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);-ms-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);-webkit-transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1);transition:0.4s cubic-bezier(0.54, 1.6, 0.5, 1)}.fs-switch.fs-off .fs-toggle{left:2%}.fs-switch.fs-on .fs-toggle{left:54%}.fs-switch.fs-round{top:8px;padding:4px 25px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round .fs-toggle{top:0;width:24px;height:24px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round.fs-off .fs-toggle{left:-1px}.fs-switch.fs-round.fs-on{background:#0085ba}.fs-switch.fs-round.fs-on .fs-toggle{left:25px}.fs-switch.fs-small.fs-round{padding:1px 19px}.fs-switch.fs-small.fs-round .fs-toggle{top:0;width:18px;height:18px;-moz-border-radius:18px;-webkit-border-radius:18px;border-radius:18px}.fs-switch.fs-small.fs-round.fs-on .fs-toggle{left:19px}.fs-switch-feedback{margin-left:10px}.fs-switch-feedback.success{color:#71ae00}.rtl .fs-switch-feedback{margin-left:0;margin-right:10px}#fs_frame{line-height:0;font-size:0}.fs-full-size-wrapper{margin:40px 0 -65px -20px}@media (max-width: 600px){.fs-full-size-wrapper{margin:0 0 -65px -10px}}
2
- .fs-notice{position:relative}.fs-notice.fs-has-title{margin-bottom:30px !important}.fs-notice.success{color:green}.fs-notice.promotion{border-color:#00a0d2 !important;background-color:#f2fcff !important}.fs-notice .fs-notice-body{margin:.5em 0;padding:2px}.fs-notice .fs-close{cursor:pointer;color:#aaa;float:right}.fs-notice .fs-close:hover{color:#666}.fs-notice .fs-close>*{margin-top:7px;display:inline-block}.fs-notice label.fs-plugin-title{background:rgba(0,0,0,0.3);color:#fff;padding:2px 10px;position:absolute;top:100%;bottom:auto;right:auto;-moz-border-radius:0 0 3px 3px;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;left:10px;font-size:12px;font-weight:bold;cursor:auto}div.fs-notice.updated,div.fs-notice.success,div.fs-notice.promotion{display:block !important}.rtl .fs-notice .fs-close{float:left}.fs-secure-notice{position:fixed;top:32px;left:160px;right:0;background:#ebfdeb;padding:10px 20px;color:green;z-index:9999;-moz-box-shadow:0 2px 2px rgba(6,113,6,0.3);-webkit-box-shadow:0 2px 2px rgba(6,113,6,0.3);box-shadow:0 2px 2px rgba(6,113,6,0.3);opacity:0.95;filter:alpha(opacity=95)}.fs-secure-notice:hover{opacity:1;filter:alpha(opacity=100)}.fs-secure-notice a.fs-security-proof{color:green;text-decoration:none}@media screen and (max-width: 960px){.fs-secure-notice{left:36px}}@media screen and (max-width: 600px){.fs-secure-notice{display:none}}@media screen and (max-width: 1250px){#fs_promo_tab{display:none}}@media screen and (max-width: 782px){.fs-secure-notice{left:0;top:46px;text-align:center}}span.fs-submenu-item.fs-sub:before{content:'\21B3';padding:0 5px}.rtl span.fs-submenu-item.fs-sub:before{content:'\21B2'}.fs-submenu-item.pricing.upgrade-mode{color:greenyellow}.fs-submenu-item.pricing.trial-mode{color:#83e2ff}#adminmenu .update-plugins.fs-trial{background-color:#00b9eb}.fs-ajax-spinner{border:0;width:20px;height:20px;margin-right:5px;vertical-align:sub;display:inline-block;background:url("/wp-admin/images/wpspin_light-2x.gif");background-size:contain;margin-bottom:-2px}.wrap.fs-section h2{text-align:left}.plugins p.fs-upgrade-notice{border:0;background-color:#d54e21;padding:10px;color:#f9f9f9;margin-top:10px}
1
+ .fs-badge{position:absolute;top:10px;right:0;background:#71ae00;color:#fff;text-transform:uppercase;padding:5px 10px;-moz-border-radius:3px 0 0 3px;-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;font-weight:bold;border-right:0;-moz-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,.3);box-shadow:0 2px 1px -1px rgba(0,0,0,.3)}.theme-browser .theme .fs-premium-theme-badge-container{position:absolute;right:0;top:0}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge{position:relative;top:0;margin-top:10px;text-align:center}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-premium-theme-badge{font-size:1.1em}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-beta-theme-badge{background:#00a0d2}.fs-switch{position:relative;display:inline-block;color:#ccc;text-shadow:0 1px 1px rgba(255,255,255,.8);height:18px;padding:6px 6px 5px 6px;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);background:#ececec;box-shadow:0 0 4px rgba(0,0,0,.1),inset 0 1px 3px 0 rgba(0,0,0,.1);cursor:pointer}.fs-switch span{display:inline-block;width:35px;text-transform:uppercase}.fs-switch .fs-toggle{position:absolute;top:1px;width:37px;height:25px;border:1px solid #ccc;border:1px solid rgba(0,0,0,.3);border-radius:4px;background:#fff;background-color:#fff;background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ececec), color-stop(1, #fff));background-image:-webkit-linear-gradient(top, #ececec, #fff);background-image:-moz-linear-gradient(top, #ececec, #fff);background-image:-ms-linear-gradient(top, #ececec, #fff);background-image:-o-linear-gradient(top, #ececec, #fff);background-image:linear-gradient(top, bottom, #ececec, #fff);box-shadow:inset 0 1px 0 0 rgba(255,255,255,.5);z-index:999;-moz-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);-o-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);-ms-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);-webkit-transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1);transition:.4s cubic-bezier(0.54, 1.6, 0.5, 1)}.fs-switch.fs-off .fs-toggle{left:2%}.fs-switch.fs-on .fs-toggle{left:54%}.fs-switch.fs-round{top:8px;padding:4px 25px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round .fs-toggle{top:0;width:24px;height:24px;-moz-border-radius:24px;-webkit-border-radius:24px;border-radius:24px}.fs-switch.fs-round.fs-off .fs-toggle{left:-1px}.fs-switch.fs-round.fs-on{background:#0085ba}.fs-switch.fs-round.fs-on .fs-toggle{left:25px}.fs-switch.fs-small.fs-round{padding:1px 19px}.fs-switch.fs-small.fs-round .fs-toggle{top:0;width:18px;height:18px;-moz-border-radius:18px;-webkit-border-radius:18px;border-radius:18px}.fs-switch.fs-small.fs-round.fs-on .fs-toggle{left:19px}body.fs-loading,body.fs-loading *{cursor:wait !important}#fs_frame{line-height:0;font-size:0}.fs-full-size-wrapper{margin:40px 0 -65px -20px}@media(max-width: 600px){.fs-full-size-wrapper{margin:0 0 -65px -10px}}.fs-notice{position:relative}.fs-notice.fs-has-title{margin-bottom:30px !important}.fs-notice.success{color:green}.fs-notice.promotion{border-color:#00a0d2 !important;background-color:#f2fcff !important}.fs-notice .fs-notice-body{margin:.5em 0;padding:2px}.fs-notice .fs-close{cursor:pointer;color:#aaa;float:right}.fs-notice .fs-close:hover{color:#666}.fs-notice .fs-close>*{margin-top:7px;display:inline-block}.fs-notice label.fs-plugin-title{background:rgba(0,0,0,.3);color:#fff;padding:2px 10px;position:absolute;top:100%;bottom:auto;right:auto;-moz-border-radius:0 0 3px 3px;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;left:10px;font-size:12px;font-weight:bold;cursor:auto}div.fs-notice.updated,div.fs-notice.success,div.fs-notice.promotion{display:block !important}.rtl .fs-notice .fs-close{float:left}.fs-secure-notice{position:fixed;top:32px;left:160px;right:0;background:#ebfdeb;padding:10px 20px;color:green;z-index:9999;-moz-box-shadow:0 2px 2px rgba(6,113,6,.3);-webkit-box-shadow:0 2px 2px rgba(6,113,6,.3);box-shadow:0 2px 2px rgba(6,113,6,.3);opacity:.95;filter:alpha(opacity=95)}.fs-secure-notice:hover{opacity:1;filter:alpha(opacity=100)}.fs-secure-notice a.fs-security-proof{color:green;text-decoration:none}@media screen and (max-width: 960px){.fs-secure-notice{left:36px}}@media screen and (max-width: 600px){.fs-secure-notice{display:none}}@media screen and (max-width: 1250px){#fs_promo_tab{display:none}}@media screen and (max-width: 782px){.fs-secure-notice{left:0;top:46px;text-align:center}}span.fs-submenu-item.fs-sub:before{content:"↳";padding:0 5px}.rtl span.fs-submenu-item.fs-sub:before{content:"↲"}.fs-submenu-item.pricing.upgrade-mode{color:#adff2f}.fs-submenu-item.pricing.trial-mode{color:#83e2ff}#adminmenu .update-plugins.fs-trial{background-color:#00b9eb}.fs-ajax-spinner{border:0;width:20px;height:20px;margin-right:5px;vertical-align:sub;display:inline-block;background:url("/wp-admin/images/wpspin_light-2x.gif");background-size:contain;margin-bottom:-2px}.wrap.fs-section h2{text-align:left}.plugins p.fs-upgrade-notice{border:0;background-color:#d54e21;padding:10px;color:#f9f9f9;margin-top:10px}
 
freemius/assets/css/admin/connect.css CHANGED
@@ -1 +1 @@
1
- #fs_connect{width:480px;-moz-box-shadow:0px 1px 2px rgba(0,0,0,0.3);-webkit-box-shadow:0px 1px 2px rgba(0,0,0,0.3);box-shadow:0px 1px 2px rgba(0,0,0,0.3);margin:20px 0}@media screen and (max-width: 479px){#fs_connect{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;width:auto;margin:0 0 0 -10px}}#fs_connect .fs-content{background:#fff;padding:15px 20px}#fs_connect .fs-content .fs-error{background:snow;color:#d3135a;border:1px solid #d3135a;-moz-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);text-align:center;padding:5px;margin-bottom:10px}#fs_connect .fs-content p{margin:0;padding:0;font-size:1.2em}#fs_connect .fs-license-key-container{position:relative;width:280px;margin:10px auto 0 auto}#fs_connect .fs-license-key-container input{width:100%}#fs_connect .fs-license-key-container .dashicons{position:absolute;top:5px;right:5px}#fs_connect.require-license-key .fs-sites-list-container td{cursor:pointer}#fs_connect #delegate_to_site_admins{margin-right:15px;float:right;height:26px;vertical-align:middle;line-height:37px;font-weight:bold;border-bottom:1px dashed;text-decoration:none}#fs_connect #delegate_to_site_admins.rtl{margin-left:15px;margin-right:0}#fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}#fs_connect .fs-actions .button{padding:0 10px 1px;line-height:35px;height:37px;font-size:16px;margin-bottom:0}#fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}#fs_connect .fs-actions .button.button-primary{padding-right:15px;padding-left:15px}#fs_connect .fs-actions .button.button-primary:after{content:' \279C'}#fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}#fs_connect .fs-actions .button.button-secondary{float:right}#fs_connect.fs-anonymous-disabled .fs-actions .button.button-primary{width:100%}#fs_connect .fs-permissions{padding:10px 20px;background:#FEFEFE;-moz-transition:background 0.5s ease;-o-transition:background 0.5s ease;-ms-transition:background 0.5s ease;-webkit-transition:background 0.5s ease;transition:background 0.5s ease}#fs_connect .fs-permissions .fs-license-sync-disclaimer{text-align:center;margin-top:0}#fs_connect .fs-permissions>.fs-trigger{font-size:0.9em;text-decoration:none;text-align:center;display:block}#fs_connect .fs-permissions ul{height:0;overflow:hidden;margin:0}#fs_connect .fs-permissions ul li{margin-bottom:12px}#fs_connect .fs-permissions ul li:last-child{margin-bottom:0}#fs_connect .fs-permissions ul li>i.dashicons{float:left;font-size:40px;width:40px;height:40px}#fs_connect .fs-permissions ul li .fs-switch{float:right}#fs_connect .fs-permissions ul li .fs-permission-description{margin-left:55px}#fs_connect .fs-permissions ul li .fs-permission-description span{font-weight:bold;text-transform:uppercase;color:#23282d}#fs_connect .fs-permissions ul li .fs-permission-description p{margin:2px 0 0 0}#fs_connect .fs-permissions.fs-open{background:#fff}#fs_connect .fs-permissions.fs-open ul{overflow:initial;height:auto;margin:20px 20px 10px 20px}@media screen and (max-width: 479px){#fs_connect .fs-permissions{background:#fff}#fs_connect .fs-permissions .fs-trigger{display:none}#fs_connect .fs-permissions ul{height:auto;margin:20px}}#fs_connect .fs-freemium-licensing{padding:8px;background:#777;color:#fff}#fs_connect .fs-freemium-licensing p{text-align:center;display:block;margin:0;padding:0}#fs_connect .fs-freemium-licensing a{color:#C2EEFF;text-decoration:underline}#fs_connect .fs-visual{padding:12px;line-height:0;background:#fafafa;height:80px;position:relative}#fs_connect .fs-visual .fs-site-icon{position:absolute;left:20px;top:10px}#fs_connect .fs-visual .fs-connect-logo{position:absolute;right:20px;top:10px}#fs_connect .fs-visual .fs-plugin-icon{position:absolute;top:10px;left:50%;margin-left:-40px}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-site-icon,#fs_connect .fs-visual img,#fs_connect .fs-visual object{width:80px;height:80px}#fs_connect .fs-visual .dashicons-wordpress{font-size:64px;background:#01749a;color:#fff;width:64px;height:64px;padding:8px}#fs_connect .fs-visual .dashicons-plus{position:absolute;top:50%;font-size:30px;margin-top:-10px;color:#bbb}#fs_connect .fs-visual .dashicons-plus.fs-first{left:28%}#fs_connect .fs-visual .dashicons-plus.fs-second{left:65%}#fs_connect .fs-visual .fs-plugin-icon,#fs_connect .fs-visual .fs-connect-logo,#fs_connect .fs-visual .fs-site-icon{border:1px solid #ccc;padding:1px;background:#fff}#fs_connect .fs-terms{text-align:center;font-size:0.85em;padding:5px;background:rgba(0,0,0,0.05)}#fs_connect .fs-terms,#fs_connect .fs-terms a{color:#999}#fs_connect .fs-terms a{text-decoration:none}.fs-multisite-options-container{margin-top:10px;border:1px solid #ccc;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:bold}.fs-multisite-options-container.fs-apply-on-all-sites{border:0 none;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}.fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .fs-tooltip{opacity:0;visibility:hidden;-moz-transition:opacity 0.3s ease-in-out;-o-transition:opacity 0.3s ease-in-out;-ms-transition:opacity 0.3s ease-in-out;-webkit-transition:opacity 0.3s ease-in-out;transition:opacity 0.3s ease-in-out;position:absolute;background:rgba(0,0,0,0.8);color:#fff !important;font-family:'arial', serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:-17px;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);line-height:1.3em;font-weight:bold;text-align:left;text-transform:none !important}.rtl .fs-tooltip-trigger .fs-tooltip{text-align:right;left:auto;right:-17px}.fs-tooltip-trigger .fs-tooltip::after{content:' ';display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:rgba(0,0,0,0.8) transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl .fs-tooltip-trigger .fs-tooltip::after{right:21px;left:auto}.fs-tooltip-trigger:hover .fs-tooltip{visibility:visible;opacity:1}#fs_marketing_optin{display:none;margin-top:10px;border:1px solid #ccc;padding:10px;line-height:1.5em}#fs_marketing_optin .fs-message{display:block;margin-bottom:5px;font-size:1.05em;font-weight:600}#fs_marketing_optin.error{border:1px solid #d3135a;background:#fee}#fs_marketing_optin.error .fs-message{color:#d3135a}#fs_marketing_optin .fs-input-container{margin-top:5px}#fs_marketing_optin .fs-input-container label{margin-top:5px;display:block}#fs_marketing_optin .fs-input-container label input{float:left;margin:1px 0 0 0}#fs_marketing_optin .fs-input-container label:first-child{display:block;margin-bottom:2px}#fs_marketing_optin .fs-input-label{display:block;margin-left:20px}#fs_marketing_optin .fs-input-label .underlined{text-decoration:underline}.rtl #fs_marketing_optin .fs-input-container label input{float:right}.rtl #fs_marketing_optin .fs-input-label{margin-left:0;margin-right:20px}.rtl #fs_connect .fs-actions{padding:10px 20px;background:#C0C7CA}.rtl #fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}.rtl #fs_connect .fs-actions .button.button-primary:after{content:' \000bb'}.rtl #fs_connect .fs-actions .button.button-primary.fs-loading:after{content:''}.rtl #fs_connect .fs-actions .button.button-secondary{float:left}.rtl #fs_connect .fs-permissions ul li .fs-permission-description{margin-right:55px;margin-left:0}.rtl #fs_connect .fs-permissions ul li .fs-switch{float:left}.rtl #fs_connect .fs-permissions ul li i.dashicons{float:right}.rtl #fs_connect .fs-visual .fs-site-icon{right:20px;left:auto}.rtl #fs_connect .fs-visual .fs-connect-logo{right:auto;left:20px}#fs_theme_connect_wrapper{position:fixed;top:0;height:100%;width:100%;z-index:99990;background:rgba(0,0,0,0.75);text-align:center;overflow-y:auto}#fs_theme_connect_wrapper:before{content:"";display:inline-block;vertical-align:middle;height:100%}#fs_theme_connect_wrapper>button.close{color:white;cursor:pointer;height:40px;width:40px;position:absolute;right:0;border:0;background-color:transparent;top:32px}#fs_theme_connect_wrapper #fs_connect{top:0;text-align:left;display:inline-block;vertical-align:middle;margin-top:52px;margin-bottom:20px}#fs_theme_connect_wrapper #fs_connect .fs-terms{background:rgba(140,140,140,0.64)}#fs_theme_connect_wrapper #fs_connect .fs-terms,#fs_theme_connect_wrapper #fs_connect .fs-terms a{color:#c5c5c5}.wp-pointer-content #fs_connect{margin:0;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.fs-opt-in-pointer .wp-pointer-content{padding:0}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow{border-bottom-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow-inner{border-bottom-color:#fafafa}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow{border-top-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow-inner{border-top-color:#fafafa}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow{border-right-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow-inner{border-right-color:#fafafa}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow{border-left-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow-inner{border-left-color:#fafafa}#license_issues_link{display:block;text-align:center;font-size:0.9em;margin-top:10px}
1
+ .fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .dashicons{float:none !important}.fs-tooltip-trigger .fs-tooltip{opacity:0;visibility:hidden;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;position:absolute;background:rgba(0,0,0,.8);color:#fff !important;font-family:"arial",serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:-17px;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);line-height:1.3em;font-weight:bold;text-align:left;text-transform:none !important}.rtl .fs-tooltip-trigger .fs-tooltip{text-align:right;left:auto;right:-17px}.fs-tooltip-trigger .fs-tooltip::after{content:" ";display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:rgba(0,0,0,.8) transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl .fs-tooltip-trigger .fs-tooltip::after{right:21px;left:auto}.fs-tooltip-trigger:hover .fs-tooltip{visibility:visible;opacity:1}#fs_connect{width:484px;margin:60px auto 20px auto}#fs_connect a{color:inherit}#fs_connect a:not(.button){text-decoration:underline}#fs_connect .fs-box-container{box-shadow:0 1px 2px rgba(0,0,0,.3);border-radius:3px;overflow:hidden;padding-top:40px;background:#f0f0f1}@media screen and (max-width: 483px){#fs_connect{width:auto;margin:30px 0 0 -10px}#fs_connect .fs-box-container{-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}}#fs_connect .fs-content{background:#fff;padding:30px 20px}#fs_connect .fs-content .fs-error{background:snow;color:#d3135a;border:1px solid #d3135a;-moz-box-shadow:0 1px 1px 0 rgba(0,0,0,.1);-webkit-box-shadow:0 1px 1px 0 rgba(0,0,0,.1);box-shadow:0 1px 1px 0 rgba(0,0,0,.1);text-align:center;padding:5px;margin-bottom:10px}#fs_connect .fs-content h2{line-height:1.5em}#fs_connect .fs-content p{margin:0;padding:0;font-size:1.2em}#fs_connect .fs-license-key-container{position:relative;width:280px;margin:10px auto 0 auto}#fs_connect .fs-license-key-container input{width:100%}#fs_connect .fs-license-key-container .dashicons{position:absolute;top:5px;right:5px}#fs_connect.require-license-key .fs-content{padding-bottom:10px}#fs_connect.require-license-key .fs-actions{border-top:none}#fs_connect.require-license-key .fs-sites-list-container td{cursor:pointer}#fs_connect #delegate_to_site_admins{margin-right:15px;float:right;height:26px;vertical-align:middle;line-height:37px;font-weight:bold;border-bottom:1px dashed;text-decoration:none}#fs_connect #delegate_to_site_admins.rtl{margin-left:15px;margin-right:0}#fs_connect .fs-actions{padding:10px 20px;background:#fff;border-width:1px 0;border-style:solid;border-color:#f1f1f1}#fs_connect .fs-actions .button{padding:0 10px 1px;line-height:35px;height:37px;font-size:16px;margin-bottom:0}#fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}#fs_connect .fs-actions .button.button-primary{padding-right:15px;padding-left:15px}#fs_connect .fs-actions .button.button-primary:after{content:" ➜"}#fs_connect .fs-actions .button.button-primary.fs-loading:after{content:""}#fs_connect .fs-actions .button.button-secondary{float:right}#fs_connect.fs-anonymous-disabled .fs-actions .button.button-primary{width:100%}#fs_connect .fs-permissions{padding:10px 20px;background:#fff;-moz-transition:background .5s ease;-o-transition:background .5s ease;-ms-transition:background .5s ease;-webkit-transition:background .5s ease;transition:background .5s ease}#fs_connect .fs-permissions .fs-license-sync-disclaimer{text-align:center;margin-top:0}#fs_connect .fs-permissions>.fs-trigger{font-size:.9em;text-decoration:none;text-align:center;display:block}#fs_connect .fs-permissions>.fs-trigger .fs-arrow::after{content:"→";width:20px;display:inline-block}#fs_connect .fs-permissions.fs-open>.fs-trigger .fs-arrow::after{content:"↓" !important}#fs_connect .fs-permissions ul li{padding-left:0;padding-right:0}@media screen and (max-width: 483px){#fs_connect .fs-permissions ul{height:auto;margin:20px}}#fs_connect .fs-freemium-licensing{padding:8px;background:#777;color:#fff}#fs_connect .fs-freemium-licensing p{text-align:center;display:block;margin:0;padding:0}#fs_connect .fs-freemium-licensing a{color:inherit;text-decoration:underline}#fs_connect .fs-header{padding:0;line-height:0;height:0;position:relative}#fs_connect .fs-header .fs-site-icon,#fs_connect .fs-header .fs-connect-logo{position:absolute;top:-8px;border-radius:50%}#fs_connect .fs-header .fs-site-icon{left:152px}#fs_connect .fs-header .fs-connect-logo{right:152px}#fs_connect .fs-header .fs-site-icon,#fs_connect .fs-header img,#fs_connect .fs-header object{width:50px;height:50px;border-radius:50%}#fs_connect .fs-header .fs-plugin-icon{position:absolute;overflow:hidden;top:-23px;left:50%;margin-left:-44px;border-radius:50%;z-index:1}#fs_connect .fs-header .fs-plugin-icon,#fs_connect .fs-header .fs-plugin-icon img{width:80px;height:80px}#fs_connect .fs-header .dashicons-wordpress-alt{font-size:40px;background:#01749a;color:#fff;width:40px;height:40px;padding:5px;border-radius:50%}#fs_connect .fs-header .dashicons-plus{position:absolute;top:50%;font-size:30px;margin-top:-10px;color:#bbb}#fs_connect .fs-header .dashicons-plus.fs-first{left:28%}#fs_connect .fs-header .dashicons-plus.fs-second{left:65%}#fs_connect .fs-header .fs-plugin-icon,#fs_connect .fs-header .fs-connect-logo,#fs_connect .fs-header .fs-site-icon{border:1px solid #efefef;padding:3px;background:#fff}#fs_connect .fs-terms{text-align:center;font-size:.85em;padding:10px 5px}#fs_connect .fs-terms,#fs_connect .fs-terms a{color:#999}#fs_connect .fs-terms a{text-decoration:none}.fs-multisite-options-container{margin-top:20px;border:1px solid #ccc;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:bold}.fs-multisite-options-container.fs-apply-on-all-sites{border:0 none;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}#fs_marketing_optin{display:none;margin-top:10px;border:1px solid #ccc;padding:10px;line-height:1.5em}#fs_marketing_optin .fs-message{display:block;margin-bottom:5px;font-size:1.05em;font-weight:600}#fs_marketing_optin.error{border:1px solid #d3135a;background:#fee}#fs_marketing_optin.error .fs-message{color:#d3135a}#fs_marketing_optin .fs-input-container{margin-top:5px}#fs_marketing_optin .fs-input-container label{margin-top:5px;display:block}#fs_marketing_optin .fs-input-container label input{float:left;margin:1px 0 0 0}#fs_marketing_optin .fs-input-container label:first-child{display:block;margin-bottom:2px}#fs_marketing_optin .fs-input-label{display:block;margin-left:20px}#fs_marketing_optin .fs-input-label .underlined{text-decoration:underline}.rtl #fs_marketing_optin .fs-input-container label input{float:right}.rtl #fs_marketing_optin .fs-input-label{margin-left:0;margin-right:20px}.rtl #fs_connect{border-radius:3px}.rtl #fs_connect .fs-actions{padding:10px 20px;background:#c0c7ca}.rtl #fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}.rtl #fs_connect .fs-actions .button.button-primary:after{content:" »"}.rtl #fs_connect .fs-actions .button.button-primary.fs-loading:after{content:""}.rtl #fs_connect .fs-actions .button.button-secondary{float:left}.rtl #fs_connect .fs-header .fs-site-icon{right:20px;left:auto}.rtl #fs_connect .fs-header .fs-connect-logo{right:auto;left:20px}.rtl #fs_connect .fs-permissions>.fs-trigger .fs-arrow::after{content:"←"}#fs_theme_connect_wrapper{position:fixed;top:0;height:100%;width:100%;z-index:99990;background:rgba(0,0,0,.75);text-align:center;overflow-y:auto}#fs_theme_connect_wrapper:before{content:"";display:inline-block;vertical-align:middle;height:100%}#fs_theme_connect_wrapper>button.close{color:#fff;cursor:pointer;height:40px;width:40px;position:absolute;right:0;border:0;background-color:transparent;top:32px}#fs_theme_connect_wrapper #fs_connect{top:0;text-align:left;display:inline-block;vertical-align:middle;margin-top:0;margin-bottom:20px}#fs_theme_connect_wrapper #fs_connect .fs-terms,#fs_theme_connect_wrapper #fs_connect .fs-terms a{color:#c5c5c5}.wp-pointer-content #fs_connect{margin:0;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.fs-opt-in-pointer .wp-pointer-content{padding:0}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow{border-bottom-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow-inner{border-bottom-color:#fafafa}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow{border-top-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow-inner{border-top-color:#fafafa}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow{border-right-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow-inner{border-right-color:#fafafa}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow{border-left-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow-inner{border-left-color:#fafafa}#license_issues_link{display:block;text-align:center;font-size:.9em;margin-top:10px}.fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .dashicons{float:none !important}.fs-tooltip-trigger .fs-tooltip{opacity:0;visibility:hidden;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;position:absolute;background:rgba(0,0,0,.8);color:#fff !important;font-family:"arial",serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:-17px;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);line-height:1.3em;font-weight:bold;text-align:left;text-transform:none !important}.rtl .fs-tooltip-trigger .fs-tooltip{text-align:right;left:auto;right:-17px}.fs-tooltip-trigger .fs-tooltip::after{content:" ";display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:rgba(0,0,0,.8) transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl .fs-tooltip-trigger .fs-tooltip::after{right:21px;left:auto}.fs-tooltip-trigger:hover .fs-tooltip{visibility:visible;opacity:1}.fs-permissions .fs-permission.fs-disabled{color:#aaa}.fs-permissions .fs-permission.fs-disabled .fs-permission-description span{color:#aaa}.fs-permissions .fs-permission .fs-switch-feedback{position:absolute;right:15px;top:52px}.fs-permissions ul{height:0;overflow:hidden;margin:0}.fs-permissions ul li{padding:17px 15px;margin:0;position:relative}.fs-permissions ul li>i.dashicons{float:left;font-size:30px;width:30px;height:30px;padding:5px}.fs-permissions ul li .fs-switch{float:right}.fs-permissions ul li .fs-permission-description{margin-left:55px}.fs-permissions ul li .fs-permission-description span{font-size:14px;font-weight:500;color:#23282d}.fs-permissions ul li .fs-permission-description .fs-tooltip{font-size:13px;font-weight:bold}.fs-permissions ul li .fs-permission-description .fs-tooltip-trigger .dashicons{margin:-1px 2px 0 2px}.fs-permissions ul li .fs-permission-description p{margin:2px 0 0 0}.fs-permissions.fs-open{background:#fff}.fs-permissions.fs-open ul{overflow:initial;height:auto;margin:20px 0 10px 0}.fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-right:10px}.fs-permissions .fs-switch-feedback.success{color:#71ae00}.rtl .fs-permissions .fs-switch-feedback{right:auto;left:15px}.rtl .fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-left:10px;margin-right:0}.rtl .fs-permissions ul li .fs-permission-description{margin-right:55px;margin-left:0}.rtl .fs-permissions ul li .fs-switch{float:left}.rtl .fs-permissions ul li i.dashicons{float:right}
freemius/assets/css/admin/debug.css CHANGED
@@ -1 +1 @@
1
- .fs-switch-label{font-size:20px;line-height:31px;margin:0 5px}#fs_log_book table{font-family:Consolas,Monaco,monospace;font-size:12px}#fs_log_book table th{color:#ccc}#fs_log_book table tr{background:#232525}#fs_log_book table tr.alternate{background:#2b2b2b}#fs_log_book table tr td.fs-col--logger{color:#5a7435}#fs_log_book table tr td.fs-col--type{color:#ffc861}#fs_log_book table tr td.fs-col--function{color:#a7b7b1;font-weight:bold}#fs_log_book table tr td.fs-col--message,#fs_log_book table tr td.fs-col--message a{color:#9a73ac !important}#fs_log_book table tr td.fs-col--file{color:#d07922}#fs_log_book table tr td.fs-col--timestamp{color:#6596be}
1
+ label.fs-tag,span.fs-tag{background:#ffba00;color:#fff;display:inline-block;border-radius:3px;padding:5px;font-size:11px;line-height:11px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.fs-switch-label{font-size:20px;line-height:31px;margin:0 5px}#fs_log_book table{font-family:Consolas,Monaco,monospace;font-size:12px}#fs_log_book table th{color:#ccc}#fs_log_book table tr{background:#232525}#fs_log_book table tr.alternate{background:#2b2b2b}#fs_log_book table tr td.fs-col--logger{color:#5a7435}#fs_log_book table tr td.fs-col--type{color:#ffc861}#fs_log_book table tr td.fs-col--function{color:#a7b7b1;font-weight:bold}#fs_log_book table tr td.fs-col--message,#fs_log_book table tr td.fs-col--message a{color:#9a73ac !important}#fs_log_book table tr td.fs-col--file{color:#d07922}#fs_log_book table tr td.fs-col--timestamp{color:#6596be}
freemius/assets/css/admin/dialog-boxes.css CHANGED
@@ -1,2 +1 @@
1
- .fs-modal{position:fixed;overflow:auto;height:100%;width:100%;top:0;z-index:100000;display:none;background:rgba(0,0,0,0.6)}.fs-modal .dashicons{vertical-align:middle}.fs-modal .fs-modal-dialog{background:transparent;position:absolute;left:50%;margin-left:-298px;padding-bottom:30px;top:-100%;z-index:100001;width:596px}@media (max-width: 650px){.fs-modal .fs-modal-dialog{margin-left:-50%;box-sizing:border-box;padding-left:10px;padding-right:10px;width:100%}.fs-modal .fs-modal-dialog .fs-modal-panel>h3>strong{font-size:1.3em}}.fs-modal.active{display:block}.fs-modal.active:before{display:block}.fs-modal.active .fs-modal-dialog{top:10%}.fs-modal.fs-success .fs-modal-header{border-bottom-color:#46b450}.fs-modal.fs-success .fs-modal-body{background-color:#f7fff7}.fs-modal.fs-warn .fs-modal-header{border-bottom-color:#ffb900}.fs-modal.fs-warn .fs-modal-body{background-color:#fff8e5}.fs-modal.fs-error .fs-modal-header{border-bottom-color:#dc3232}.fs-modal.fs-error .fs-modal-body{background-color:#ffeaea}.fs-modal .fs-modal-body,.fs-modal .fs-modal-footer{border:0;background:#fefefe;padding:20px}.fs-modal .fs-modal-header{border-bottom:#eeeeee solid 1px;background:#fbfbfb;padding:15px 20px;position:relative;margin-bottom:-10px}.fs-modal .fs-modal-header h4{margin:0;padding:0;text-transform:uppercase;font-size:1.2em;font-weight:bold;color:#cacaca;text-shadow:1px 1px 1px #fff;letter-spacing:0.6px;-webkit-font-smoothing:antialiased}.fs-modal .fs-modal-header .fs-close{position:absolute;right:10px;top:12px;cursor:pointer;color:#bbb;-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;padding:3px;-moz-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.fs-modal .fs-modal-header .fs-close:hover{color:#fff;background:#aaa}.fs-modal .fs-modal-header .fs-close .dashicons,.fs-modal .fs-modal-header .fs-close:hover .dashicons{text-decoration:none}.fs-modal .fs-modal-body{border-bottom:0}.fs-modal .fs-modal-body p{font-size:14px}.fs-modal .fs-modal-body h2{font-size:20px;line-height:1.5em}.fs-modal .fs-modal-body>div{margin-top:10px}.fs-modal .fs-modal-body>div h2{font-weight:bold;font-size:20px;margin-top:0}.fs-modal .fs-modal-footer{border-top:#eeeeee solid 1px;text-align:right}.fs-modal .fs-modal-footer>.button{margin:0 7px}.fs-modal .fs-modal-footer>.button:first-child{margin:0}.fs-modal .fs-modal-panel>.notice.inline{margin:0;display:none}.fs-modal .fs-modal-panel:not(.active){display:none}.rtl .fs-modal .fs-modal-header .fs-close{right:auto;left:20px}body.has-fs-modal{overflow:hidden}.fs-modal.fs-modal-deactivation-feedback .reason-input,.fs-modal.fs-modal-deactivation-feedback .internal-message{margin:3px 0 3px 22px}.fs-modal.fs-modal-deactivation-feedback .reason-input input,.fs-modal.fs-modal-deactivation-feedback .reason-input textarea,.fs-modal.fs-modal-deactivation-feedback .internal-message input,.fs-modal.fs-modal-deactivation-feedback .internal-message textarea{width:100%}.fs-modal.fs-modal-deactivation-feedback li.reason.has-internal-message .internal-message{border:1px solid #ccc;padding:7px;display:none}@media (max-width: 650px){.fs-modal.fs-modal-deactivation-feedback li.reason li.reason{margin-bottom:10px}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason .reason-input,.fs-modal.fs-modal-deactivation-feedback li.reason li.reason .internal-message{margin-left:29px}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason label{display:table}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason label>span{display:table-cell;font-size:1.3em}}.fs-modal.fs-modal-deactivation-feedback .anonymous-feedback-label{float:left}.fs-modal.fs-modal-deactivation-feedback .fs-modal-panel{margin-top:0 !important}.fs-modal.fs-modal-deactivation-feedback .fs-modal-panel h3{margin-top:0;line-height:1.5em}#the-list .deactivate>.fs-slug{display:none}.fs-modal.fs-modal-subscription-cancellation .fs-price-increase-warning{color:red;font-weight:bold;padding:0 25px;margin-bottom:0}.fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label input{float:left;top:5px;position:relative}.rtl .fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label input{float:right}.fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label span{display:block;margin-left:24px}.rtl .fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label span{margin-left:0;margin-right:24px}.fs-modal.fs-modal-license-activation .fs-modal-body input.fs-license-key{width:100%}.fs-license-options-container table,.fs-license-options-container table select,.fs-license-options-container table .fs-available-license-key{width:100%}.fs-license-options-container table td:first-child{width:1%}.fs-license-options-container table .fs-other-license-key-container label{position:relative;top:6px;float:left;margin-right:5px}.fs-license-options-container table .fs-other-license-key-container div{overflow:hidden;width:auto;height:30px;display:block;top:2px;position:relative}.fs-license-options-container table .fs-other-license-key-container div input{margin:0}.fs-sites-list-container td{cursor:pointer}.fs-modal.fs-modal-user-change .fs-modal-body input#fs_other_email_address{width:100%}.fs-user-change-options-container table{width:100%;border-collapse:collapse}.fs-user-change-options-container table tr{display:block;margin-bottom:2px}.fs-user-change-options-container table .fs-email-address-container td{display:inline-block}.fs-user-change-options-container table .fs-email-address-container input[type="radio"]{margin-bottom:0;margin-top:0}.fs-user-change-options-container table .fs-other-email-address-container{width:100%}.fs-user-change-options-container table .fs-other-email-address-container>div{display:table;width:100%}.fs-user-change-options-container table .fs-other-email-address-container>div label,.fs-user-change-options-container table .fs-other-email-address-container>div>div{display:table-cell}.fs-user-change-options-container table .fs-other-email-address-container>div label{width:1%;padding-left:3px;padding-right:3px}.fs-user-change-options-container table .fs-other-email-address-container>div>div{width:auto}.fs-user-change-options-container table .fs-other-email-address-container>div>div input{width:100%}.fs-modal.fs-modal-developer-license-debug-mode .fs-modal-body input.fs-license-or-user-key{width:100%}.fs-multisite-options-container{margin-top:10px;border:1px solid #ccc;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:bold}.fs-multisite-options-container.fs-apply-on-all-sites{border:0 none;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}.fs-modal.fs-modal-license-key-resend .email-address-container{overflow:hidden;padding-right:2px}.fs-modal.fs-modal-license-key-resend.fs-freemium input.email-address{width:300px}.fs-modal.fs-modal-license-key-resend.fs-freemium label{display:block;margin-bottom:10px}.fs-modal.fs-modal-license-key-resend.fs-premium input.email-address{width:100%}.fs-modal.fs-modal-license-key-resend.fs-premium .button-container{float:right;margin-left:7px}@media (max-width: 650px){.fs-modal.fs-modal-license-key-resend.fs-premium .button-container{margin-top:2px}}
2
- .rtl .fs-modal.fs-modal-license-key-resend .fs-modal-body .input-container>.email-address-container{padding-left:2px;padding-right:0}.rtl .fs-modal.fs-modal-license-key-resend .fs-modal-body .button-container{float:left;margin-right:7px;margin-left:0}a.show-license-resend-modal{margin-top:4px;display:inline-block}.fs-ajax-loader{position:relative;width:170px;height:20px;margin:auto}.fs-ajax-loader .fs-ajax-loader-bar{position:absolute;top:0;background-color:#0074a3;width:20px;height:20px;-webkit-animation-name:bounce_ajaxLoader;-moz-animation-name:bounce_ajaxLoader;-ms-animation-name:bounce_ajaxLoader;-o-animation-name:bounce_ajaxLoader;animation-name:bounce_ajaxLoader;-webkit-animation-duration:1.5s;-moz-animation-duration:1.5s;-ms-animation-duration:1.5s;-o-animation-duration:1.5s;animation-duration:1.5s;animation-iteration-count:infinite;-o-animation-iteration-count:infinite;-ms-animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite;-moz-animation-iteration-count:infinite;-webkit-animation-direction:normal;-moz-animation-direction:normal;-ms-animation-direction:normal;-o-animation-direction:normal;animation-direction:normal;-moz-transform:0.3;-o-transform:0.3;-ms-transform:0.3;-webkit-transform:0.3;transform:0.3}.fs-ajax-loader .fs-ajax-loader-bar-1{left:0px;animation-delay:0.6s;-o-animation-delay:0.6s;-ms-animation-delay:0.6s;-webkit-animation-delay:0.6s;-moz-animation-delay:0.6s}.fs-ajax-loader .fs-ajax-loader-bar-2{left:19px;animation-delay:0.75s;-o-animation-delay:0.75s;-ms-animation-delay:0.75s;-webkit-animation-delay:0.75s;-moz-animation-delay:0.75s}.fs-ajax-loader .fs-ajax-loader-bar-3{left:38px;animation-delay:0.9s;-o-animation-delay:0.9s;-ms-animation-delay:0.9s;-webkit-animation-delay:0.9s;-moz-animation-delay:0.9s}.fs-ajax-loader .fs-ajax-loader-bar-4{left:57px;animation-delay:1.05s;-o-animation-delay:1.05s;-ms-animation-delay:1.05s;-webkit-animation-delay:1.05s;-moz-animation-delay:1.05s}.fs-ajax-loader .fs-ajax-loader-bar-5{left:76px;animation-delay:1.2s;-o-animation-delay:1.2s;-ms-animation-delay:1.2s;-webkit-animation-delay:1.2s;-moz-animation-delay:1.2s}.fs-ajax-loader .fs-ajax-loader-bar-6{left:95px;animation-delay:1.35s;-o-animation-delay:1.35s;-ms-animation-delay:1.35s;-webkit-animation-delay:1.35s;-moz-animation-delay:1.35s}.fs-ajax-loader .fs-ajax-loader-bar-7{left:114px;animation-delay:1.5s;-o-animation-delay:1.5s;-ms-animation-delay:1.5s;-webkit-animation-delay:1.5s;-moz-animation-delay:1.5s}.fs-ajax-loader .fs-ajax-loader-bar-8{left:133px;animation-delay:1.65s;-o-animation-delay:1.65s;-ms-animation-delay:1.65s;-webkit-animation-delay:1.65s;-moz-animation-delay:1.65s}@-moz-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@-ms-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@-o-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@-webkit-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}.fs-modal-auto-install #request-filesystem-credentials-form h2,.fs-modal-auto-install #request-filesystem-credentials-form .request-filesystem-credentials-action-buttons{display:none}.fs-modal-auto-install #request-filesystem-credentials-form input[type=password],.fs-modal-auto-install #request-filesystem-credentials-form input[type=email],.fs-modal-auto-install #request-filesystem-credentials-form input[type=text]{-webkit-appearance:none;padding:10px 10px 5px 10px;width:300px;max-width:100%}.fs-modal-auto-install #request-filesystem-credentials-form>div,.fs-modal-auto-install #request-filesystem-credentials-form label,.fs-modal-auto-install #request-filesystem-credentials-form fieldset{width:300px;max-width:100%;margin:0 auto;display:block}.button-primary.warn{box-shadow:0 1px 0 #d2593c;text-shadow:0 -1px 1px #d2593c,1px 0 1px #d2593c,0 1px 1px #d2593c,-1px 0 1px #d2593c;background:#f56a48;border-color:#ec6544 #d2593c #d2593c}.button-primary.warn:hover{background:#fd6d4a;border-color:#d2593c}.button-primary.warn:focus{box-shadow:0 1px 0 #dd6041,0 0 2px 1px #e4a796}.button-primary.warn:active{background:#dd6041;border-color:#d2593c;box-shadow:inset 0 2px 0 #d2593c}.button-primary.warn.disabled{color:#f5b3a1 !important;background:#e76444 !important;border-color:#d85e40 !important;text-shadow:0 -1px 0 rgba(0,0,0,0.1) !important}
1
+ .fs-modal{position:fixed;overflow:auto;height:100%;width:100%;top:0;z-index:100000;display:none;background:rgba(0,0,0,.6)}@media(min-width: 961px){.fs-modal{padding-left:160px}.rtl .fs-modal{padding-left:0;padding-right:160px}}.fs-modal .dashicons{vertical-align:middle}.fs-modal .fs-modal-dialog{background:transparent;position:absolute;left:50%;margin-left:-298px;padding-bottom:30px;top:-100%;z-index:100001;width:596px}@media(max-width: 650px){.fs-modal .fs-modal-dialog{margin-left:-50%;box-sizing:border-box;padding-left:10px;padding-right:10px;width:100%}.fs-modal .fs-modal-dialog .fs-modal-panel>h3>strong{font-size:1.3em}}.fs-modal.active{display:block}.fs-modal.active:before{display:block}.fs-modal.active .fs-modal-dialog{top:10%}.fs-modal.fs-success .fs-modal-header{border-bottom-color:#46b450}.fs-modal.fs-success .fs-modal-body{background-color:#f7fff7}.fs-modal.fs-warn .fs-modal-header{border-bottom-color:#ffb900}.fs-modal.fs-warn .fs-modal-body{background-color:#fff8e5}.fs-modal.fs-error .fs-modal-header{border-bottom-color:#dc3232}.fs-modal.fs-error .fs-modal-body{background-color:#ffeaea}.fs-modal .fs-modal-body,.fs-modal .fs-modal-footer{border:0;background:#fefefe;padding:20px}.fs-modal .fs-modal-header{border-bottom:#eee solid 1px;background:#fbfbfb;padding:15px 20px;position:relative;margin-bottom:-10px}.fs-modal .fs-modal-header h4{margin:0;padding:0;text-transform:uppercase;font-size:1.2em;font-weight:bold;color:#cacaca;text-shadow:1px 1px 1px #fff;letter-spacing:.6px;-webkit-font-smoothing:antialiased}.fs-modal .fs-modal-header .fs-close{position:absolute;right:10px;top:12px;cursor:pointer;color:#bbb;-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;padding:3px;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;-ms-transition:all .2s ease-in-out;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.fs-modal .fs-modal-header .fs-close:hover{color:#fff;background:#aaa}.fs-modal .fs-modal-header .fs-close .dashicons,.fs-modal .fs-modal-header .fs-close:hover .dashicons{text-decoration:none}.fs-modal .fs-modal-body{border-bottom:0}.fs-modal .fs-modal-body p{font-size:14px}.fs-modal .fs-modal-body h2{font-size:20px;line-height:1.5em}.fs-modal .fs-modal-body>div{margin-top:10px}.fs-modal .fs-modal-body>div h2{font-weight:bold;font-size:20px;margin-top:0}.fs-modal .fs-modal-footer{border-top:#eee solid 1px;text-align:right}.fs-modal .fs-modal-footer>.button{margin:0 7px}.fs-modal .fs-modal-footer>.button:last-of-type{margin:0}.fs-modal .fs-modal-panel>.notice.inline{margin:0;display:none}.fs-modal .fs-modal-panel:not(.active){display:none}.rtl .fs-modal .fs-modal-header .fs-close{right:auto;left:20px}.rtl .fs-modal .fs-modal-footer{text-align:left}body.has-fs-modal{overflow:hidden}.fs-modal.fs-modal-deactivation-feedback .reason-input,.fs-modal.fs-modal-deactivation-feedback .internal-message{margin:3px 0 3px 22px}.fs-modal.fs-modal-deactivation-feedback .reason-input input,.fs-modal.fs-modal-deactivation-feedback .reason-input textarea,.fs-modal.fs-modal-deactivation-feedback .internal-message input,.fs-modal.fs-modal-deactivation-feedback .internal-message textarea{width:100%}.fs-modal.fs-modal-deactivation-feedback li.reason.has-internal-message .internal-message{border:1px solid #ccc;padding:7px;display:none}@media(max-width: 650px){.fs-modal.fs-modal-deactivation-feedback li.reason li.reason{margin-bottom:10px}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason .reason-input,.fs-modal.fs-modal-deactivation-feedback li.reason li.reason .internal-message{margin-left:29px}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason label{display:table}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason label>span{display:table-cell;font-size:1.3em}}.fs-modal.fs-modal-deactivation-feedback .anonymous-feedback-label,.fs-modal.fs-modal-deactivation-feedback .feedback-from-snooze-label{float:left;line-height:30px}.rtl .fs-modal.fs-modal-deactivation-feedback .anonymous-feedback-label,.rtl .fs-modal.fs-modal-deactivation-feedback .feedback-from-snooze-label{float:right}.fs-modal.fs-modal-deactivation-feedback .fs-modal-panel{margin-top:0 !important}.fs-modal.fs-modal-deactivation-feedback .fs-modal-panel h3{margin-top:0;line-height:1.5em}#the-list .deactivate>.fs-slug{display:none}.fs-modal.fs-modal-subscription-cancellation .fs-price-increase-warning{color:red;font-weight:bold;padding:0 25px;margin-bottom:0}.fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label input{float:left;top:5px;position:relative}.rtl .fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label input{float:right}.fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label span{display:block;margin-left:24px}.rtl .fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label span{margin-left:0;margin-right:24px}.fs-modal.fs-modal-license-activation .fs-modal-body input.fs-license-key{width:100%}.fs-license-options-container table,.fs-license-options-container table select,.fs-license-options-container table .fs-available-license-key{width:100%}.fs-license-options-container table td:first-child{width:1%}.fs-license-options-container table .fs-other-license-key-container label{position:relative;top:6px;float:left;margin-right:5px}.fs-license-options-container table .fs-other-license-key-container div{overflow:hidden;width:auto;height:30px;display:block;top:2px;position:relative}.fs-license-options-container table .fs-other-license-key-container div input{margin:0}.fs-sites-list-container td{cursor:pointer}.fs-modal.fs-modal-user-change .fs-modal-body input#fs_other_email_address{width:100%}.fs-user-change-options-container table{width:100%;border-collapse:collapse}.fs-user-change-options-container table tr{display:block;margin-bottom:2px}.fs-user-change-options-container table .fs-email-address-container td{display:inline-block}.fs-user-change-options-container table .fs-email-address-container input[type=radio]{margin-bottom:0;margin-top:0}.fs-user-change-options-container table .fs-other-email-address-container{width:100%}.fs-user-change-options-container table .fs-other-email-address-container>div{display:table;width:100%}.fs-user-change-options-container table .fs-other-email-address-container>div label,.fs-user-change-options-container table .fs-other-email-address-container>div>div{display:table-cell}.fs-user-change-options-container table .fs-other-email-address-container>div label{width:1%;padding-left:3px;padding-right:3px}.fs-user-change-options-container table .fs-other-email-address-container>div>div{width:auto}.fs-user-change-options-container table .fs-other-email-address-container>div>div input{width:100%}.fs-modal.fs-modal-developer-license-debug-mode .fs-modal-body input.fs-license-or-user-key{width:100%}.fs-multisite-options-container{margin-top:20px;border:1px solid #ccc;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:bold}.fs-multisite-options-container.fs-apply-on-all-sites{border:0 none;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}.fs-modal.fs-modal-license-key-resend .email-address-container{overflow:hidden;padding-right:2px}.fs-modal.fs-modal-license-key-resend.fs-freemium input.email-address{width:300px}.fs-modal.fs-modal-license-key-resend.fs-freemium label{display:block;margin-bottom:10px}.fs-modal.fs-modal-license-key-resend.fs-premium input.email-address{width:100%}.fs-modal.fs-modal-license-key-resend.fs-premium .button-container{float:right;margin-left:7px}@media(max-width: 650px){.fs-modal.fs-modal-license-key-resend.fs-premium .button-container{margin-top:2px}}.rtl .fs-modal.fs-modal-license-key-resend .fs-modal-body .input-container>.email-address-container{padding-left:2px;padding-right:0}.rtl .fs-modal.fs-modal-license-key-resend .fs-modal-body .button-container{float:left;margin-right:7px;margin-left:0}a.show-license-resend-modal{margin-top:4px;display:inline-block}.fs-modal.fs-modal-email-address-update .fs-modal-body input[type=text]{width:100%}.fs-modal.fs-modal-email-address-update p{margin-bottom:0}.fs-modal.fs-modal-email-address-update ul{margin:1em .5em}.fs-modal.fs-modal-email-address-update ul li label span{float:left;margin-top:0}.fs-modal.fs-modal-email-address-update ul li label span:last-child{display:block;float:none;margin-left:20px}.fs-ajax-loader{position:relative;width:170px;height:20px;margin:auto}.fs-ajax-loader .fs-ajax-loader-bar{position:absolute;top:0;background-color:#0074a3;width:20px;height:20px;-webkit-animation-name:bounce_ajaxLoader;-moz-animation-name:bounce_ajaxLoader;-ms-animation-name:bounce_ajaxLoader;-o-animation-name:bounce_ajaxLoader;animation-name:bounce_ajaxLoader;-webkit-animation-duration:1.5s;-moz-animation-duration:1.5s;-ms-animation-duration:1.5s;-o-animation-duration:1.5s;animation-duration:1.5s;animation-iteration-count:infinite;-o-animation-iteration-count:infinite;-ms-animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite;-moz-animation-iteration-count:infinite;-webkit-animation-direction:normal;-moz-animation-direction:normal;-ms-animation-direction:normal;-o-animation-direction:normal;animation-direction:normal;-moz-transform:.3;-o-transform:.3;-ms-transform:.3;-webkit-transform:.3;transform:.3}.fs-ajax-loader .fs-ajax-loader-bar-1{left:0px;animation-delay:0.6s;-o-animation-delay:0.6s;-ms-animation-delay:0.6s;-webkit-animation-delay:0.6s;-moz-animation-delay:0.6s}.fs-ajax-loader .fs-ajax-loader-bar-2{left:19px;animation-delay:0.75s;-o-animation-delay:0.75s;-ms-animation-delay:0.75s;-webkit-animation-delay:0.75s;-moz-animation-delay:0.75s}.fs-ajax-loader .fs-ajax-loader-bar-3{left:38px;animation-delay:0.9s;-o-animation-delay:0.9s;-ms-animation-delay:0.9s;-webkit-animation-delay:0.9s;-moz-animation-delay:0.9s}.fs-ajax-loader .fs-ajax-loader-bar-4{left:57px;animation-delay:1.05s;-o-animation-delay:1.05s;-ms-animation-delay:1.05s;-webkit-animation-delay:1.05s;-moz-animation-delay:1.05s}.fs-ajax-loader .fs-ajax-loader-bar-5{left:76px;animation-delay:1.2s;-o-animation-delay:1.2s;-ms-animation-delay:1.2s;-webkit-animation-delay:1.2s;-moz-animation-delay:1.2s}.fs-ajax-loader .fs-ajax-loader-bar-6{left:95px;animation-delay:1.35s;-o-animation-delay:1.35s;-ms-animation-delay:1.35s;-webkit-animation-delay:1.35s;-moz-animation-delay:1.35s}.fs-ajax-loader .fs-ajax-loader-bar-7{left:114px;animation-delay:1.5s;-o-animation-delay:1.5s;-ms-animation-delay:1.5s;-webkit-animation-delay:1.5s;-moz-animation-delay:1.5s}.fs-ajax-loader .fs-ajax-loader-bar-8{left:133px;animation-delay:1.65s;-o-animation-delay:1.65s;-ms-animation-delay:1.65s;-webkit-animation-delay:1.65s;-moz-animation-delay:1.65s}@-moz-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@-ms-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@-o-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@-webkit-keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}@keyframes bounce_ajaxLoader{0%{-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);background-color:#0074a3}100%{-moz-transform:scale(0.3);-o-transform:scale(0.3);-ms-transform:scale(0.3);-webkit-transform:scale(0.3);transform:scale(0.3);background-color:#fff}}.fs-modal-auto-install #request-filesystem-credentials-form h2,.fs-modal-auto-install #request-filesystem-credentials-form .request-filesystem-credentials-action-buttons{display:none}.fs-modal-auto-install #request-filesystem-credentials-form input[type=password],.fs-modal-auto-install #request-filesystem-credentials-form input[type=email],.fs-modal-auto-install #request-filesystem-credentials-form input[type=text]{-webkit-appearance:none;padding:10px 10px 5px 10px;width:300px;max-width:100%}.fs-modal-auto-install #request-filesystem-credentials-form>div,.fs-modal-auto-install #request-filesystem-credentials-form label,.fs-modal-auto-install #request-filesystem-credentials-form fieldset{width:300px;max-width:100%;margin:0 auto;display:block}.button-primary.warn{box-shadow:0 1px 0 #d2593c;text-shadow:0 -1px 1px #d2593c,1px 0 1px #d2593c,0 1px 1px #d2593c,-1px 0 1px #d2593c;background:#f56a48;border-color:#ec6544 #d2593c #d2593c}.button-primary.warn:hover{background:#fd6d4a;border-color:#d2593c}.button-primary.warn:focus{box-shadow:0 1px 0 #dd6041,0 0 2px 1px #e4a796}.button-primary.warn:active{background:#dd6041;border-color:#d2593c;box-shadow:inset 0 2px 0 #d2593c}.button-primary.warn.disabled{color:#f5b3a1 !important;background:#e76444 !important;border-color:#d85e40 !important;text-shadow:0 -1px 0 rgba(0,0,0,.1) !important}
 
freemius/assets/css/admin/gdpr-optin-notice.css CHANGED
@@ -1 +1 @@
1
- .fs-notice[data-id^="gdpr_optin_actions"] .underlined{text-decoration:underline}.fs-notice[data-id^="gdpr_optin_actions"] ul .button,.fs-notice[data-id^="gdpr_optin_actions"] ul .action-description{vertical-align:middle}.fs-notice[data-id^="gdpr_optin_actions"] ul .action-description{display:inline-block;margin-left:3px}
1
+ .fs-notice[data-id^=gdpr_optin_actions] .underlined{text-decoration:underline}.fs-notice[data-id^=gdpr_optin_actions] ul .button,.fs-notice[data-id^=gdpr_optin_actions] ul .action-description{vertical-align:middle}.fs-notice[data-id^=gdpr_optin_actions] ul .action-description{display:inline-block;margin-left:3px}
freemius/assets/css/admin/optout.css ADDED
@@ -0,0 +1 @@
 
1
+ .fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .dashicons{float:none !important}.fs-tooltip-trigger .fs-tooltip{opacity:0;visibility:hidden;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;position:absolute;background:rgba(0,0,0,.8);color:#fff !important;font-family:"arial",serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:-17px;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);line-height:1.3em;font-weight:bold;text-align:left;text-transform:none !important}.rtl .fs-tooltip-trigger .fs-tooltip{text-align:right;left:auto;right:-17px}.fs-tooltip-trigger .fs-tooltip::after{content:" ";display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:rgba(0,0,0,.8) transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl .fs-tooltip-trigger .fs-tooltip::after{right:21px;left:auto}.fs-tooltip-trigger:hover .fs-tooltip{visibility:visible;opacity:1}.fs-permissions .fs-permission.fs-disabled{color:#aaa}.fs-permissions .fs-permission.fs-disabled .fs-permission-description span{color:#aaa}.fs-permissions .fs-permission .fs-switch-feedback{position:absolute;right:15px;top:52px}.fs-permissions ul{height:0;overflow:hidden;margin:0}.fs-permissions ul li{padding:17px 15px;margin:0;position:relative}.fs-permissions ul li>i.dashicons{float:left;font-size:30px;width:30px;height:30px;padding:5px}.fs-permissions ul li .fs-switch{float:right}.fs-permissions ul li .fs-permission-description{margin-left:55px}.fs-permissions ul li .fs-permission-description span{font-size:14px;font-weight:500;color:#23282d}.fs-permissions ul li .fs-permission-description .fs-tooltip{font-size:13px;font-weight:bold}.fs-permissions ul li .fs-permission-description .fs-tooltip-trigger .dashicons{margin:-1px 2px 0 2px}.fs-permissions ul li .fs-permission-description p{margin:2px 0 0 0}.fs-permissions.fs-open{background:#fff}.fs-permissions.fs-open ul{overflow:initial;height:auto;margin:20px 0 10px 0}.fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-right:10px}.fs-permissions .fs-switch-feedback.success{color:#71ae00}.rtl .fs-permissions .fs-switch-feedback{right:auto;left:15px}.rtl .fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-left:10px;margin-right:0}.rtl .fs-permissions ul li .fs-permission-description{margin-right:55px;margin-left:0}.rtl .fs-permissions ul li .fs-switch{float:left}.rtl .fs-permissions ul li i.dashicons{float:right}.fs-modal-opt-out .fs-modal-footer .fs-opt-out-button{line-height:30px;margin-right:10px}.fs-modal-opt-out .fs-permissions{margin-top:0 !important}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header .fs-group-opt-out-button{float:right;line-height:1.1em}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header .fs-switch-feedback{float:right;line-height:1.1em;margin-right:10px}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header .fs-switch-feedback .fs-ajax-spinner{margin:-2px 0 0}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header-title{font-size:1.1em;font-weight:600;text-transform:uppercase;display:block;line-height:1.1em;margin:.5em 0}.fs-modal-opt-out .fs-permissions .fs-permissions-section--desc{margin-top:0}.fs-modal-opt-out .fs-permissions hr{border:0;border-top:#eee solid 1px;margin:25px 0 20px 0}.fs-modal-opt-out .fs-permissions ul{border:1px solid #c3c4c7;border-radius:3px;margin:10px 0 0 0;box-shadow:0 1px 1px rgba(0,0,0,.04)}.fs-modal-opt-out .fs-permissions ul li{border-bottom:1px solid #d7dde1;border-left:4px solid #72aee6}.rtl .fs-modal-opt-out .fs-permissions ul li{border-left:none;border-right:4px solid #72aee6}.fs-modal-opt-out .fs-permissions ul li.fs-disabled{border-left-color:rgba(114,174,230,0)}.fs-modal-opt-out .fs-permissions ul li:last-child{border-bottom:none}
freemius/assets/css/customizer.css CHANGED
@@ -1 +1 @@
1
- #fs_customizer_upsell .fs-customizer-plan{padding:10px 20px 20px 20px;border-radius:3px;background:#fff}#fs_customizer_upsell .fs-customizer-plan h2{position:relative;margin:0;line-height:2em;text-transform:uppercase}#fs_customizer_upsell .fs-customizer-plan h2 .button-link{top:-2px}#fs_customizer_upsell .fs-feature{position:relative}#fs_customizer_upsell .dashicons-yes{color:#0085ba;font-size:2em;vertical-align:bottom;margin-left:-7px;margin-right:10px}.rtl #fs_customizer_upsell .dashicons-yes{margin-left:10px;margin-right:-7px}#fs_customizer_upsell .dashicons-editor-help{color:#bbb;cursor:help}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{opacity:0;visibility:hidden;-moz-transition:opacity 0.3s ease-in-out;-o-transition:opacity 0.3s ease-in-out;-ms-transition:opacity 0.3s ease-in-out;-webkit-transition:opacity 0.3s ease-in-out;transition:opacity 0.3s ease-in-out;position:absolute;background:#000;color:#fff;font-family:'arial', serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:0;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.2);box-shadow:1px 1px 1px rgba(0,0,0,0.2);line-height:1.3em;font-weight:bold;text-align:left}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{text-align:right}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{content:' ';display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:#000 transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{right:21px;left:auto}#fs_customizer_upsell .dashicons-editor-help:hover .fs-feature-desc{visibility:visible;opacity:1}#fs_customizer_upsell .button-primary{display:block;text-align:center;margin-top:10px}#fs_customizer_support{display:block !important}#fs_customizer_support .button{float:right}#fs_customizer_support .button-group{width:100%;display:block;margin-top:10px}#fs_customizer_support .button-group .button{float:none;width:50%;text-align:center}#customize-theme-controls #accordion-section-freemius_upsell{border-top:1px solid #0085ba !important;border-bottom:1px solid #0085ba !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title{color:#fff;background-color:#0085ba;border-left:4px solid #0085ba;transition:.15s background-color ease-in-out, .15s border-color ease-in-out;outline:none;border-bottom:none !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:hover{background-color:#008ec2;border-left-color:#0073aa}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:after{color:#fff}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title{border-left:none;border-right:4px solid #0085ba}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title:hover{border-right-color:#0073aa}
1
+ #fs_customizer_upsell .fs-customizer-plan{padding:10px 20px 20px 20px;border-radius:3px;background:#fff}#fs_customizer_upsell .fs-customizer-plan h2{position:relative;margin:0;line-height:2em;text-transform:uppercase}#fs_customizer_upsell .fs-customizer-plan h2 .button-link{top:-2px}#fs_customizer_upsell .fs-feature{position:relative}#fs_customizer_upsell .dashicons-yes{color:#0085ba;font-size:2em;vertical-align:bottom;margin-left:-7px;margin-right:10px}.rtl #fs_customizer_upsell .dashicons-yes{margin-left:10px;margin-right:-7px}#fs_customizer_upsell .dashicons-editor-help{color:#bbb;cursor:help}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{opacity:0;visibility:hidden;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;position:absolute;background:#000;color:#fff;font-family:"arial",serif;font-size:12px;padding:10px;z-index:999999;bottom:100%;margin-bottom:5px;left:0;right:0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;-moz-box-shadow:1px 1px 1px rgba(0,0,0,.2);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.2);box-shadow:1px 1px 1px rgba(0,0,0,.2);line-height:1.3em;font-weight:bold;text-align:left}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc{text-align:right}#fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{content:" ";display:block;width:0;height:0;border-style:solid;border-width:5px 5px 0 5px;border-color:#000 transparent transparent transparent;position:absolute;top:100%;left:21px}.rtl #fs_customizer_upsell .dashicons-editor-help .fs-feature-desc::after{right:21px;left:auto}#fs_customizer_upsell .dashicons-editor-help:hover .fs-feature-desc{visibility:visible;opacity:1}#fs_customizer_upsell .button-primary{display:block;text-align:center;margin-top:10px}#fs_customizer_support{display:block !important}#fs_customizer_support .button{float:right}#fs_customizer_support .button-group{width:100%;display:block;margin-top:10px}#fs_customizer_support .button-group .button{float:none;width:50%;text-align:center}#customize-theme-controls #accordion-section-freemius_upsell{border-top:1px solid #0085ba !important;border-bottom:1px solid #0085ba !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title{color:#fff;background-color:#0085ba;border-left:4px solid #0085ba;transition:.15s background-color ease-in-out,.15s border-color ease-in-out;outline:none;border-bottom:none !important}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:hover{background-color:#008ec2;border-left-color:#0073aa}#customize-theme-controls #accordion-section-freemius_upsell h3.accordion-section-title:after{color:#fff}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title{border-left:none;border-right:4px solid #0085ba}#customize-theme-controls #accordion-section-freemius_upsell .rtl h3.accordion-section-title:hover{border-right-color:#0073aa}
freemius/config.php CHANGED
@@ -354,7 +354,7 @@
354
  is_multisite() &&
355
  ( is_network_admin() ||
356
  ( ( defined( 'DOING_AJAX' ) && DOING_AJAX &&
357
- ( isset( $_REQUEST['_fs_network_admin'] ) /*||
358
  ( ! empty( $_REQUEST['action'] ) && 'delete-plugin' === $_REQUEST['action'] )*/ )
359
  ) ||
360
  // Plugin uninstall.
@@ -385,7 +385,7 @@
385
 
386
  if ( ! defined( 'WP_FS__DEMO_MODE' ) ) {
387
  define( 'WP_FS__DEMO_MODE', false );
388
- }
389
- if ( ! defined( 'FS_SDK__SSLVERIFY' ) ) {
390
- define( 'FS_SDK__SSLVERIFY', false );
391
- }
354
  is_multisite() &&
355
  ( is_network_admin() ||
356
  ( ( defined( 'DOING_AJAX' ) && DOING_AJAX &&
357
+ ( isset( $_REQUEST['_fs_network_admin'] ) && 'true' === $_REQUEST['_fs_network_admin'] /*||
358
  ( ! empty( $_REQUEST['action'] ) && 'delete-plugin' === $_REQUEST['action'] )*/ )
359
  ) ||
360
  // Plugin uninstall.
385
 
386
  if ( ! defined( 'WP_FS__DEMO_MODE' ) ) {
387
  define( 'WP_FS__DEMO_MODE', false );
388
+ }
389
+ if ( ! defined( 'FS_SDK__SSLVERIFY' ) ) {
390
+ define( 'FS_SDK__SSLVERIFY', false );
391
+ }
freemius/includes/class-freemius-abstract.php CHANGED
@@ -37,9 +37,12 @@
37
  * `$fs->is_registered() && $fs->is_tracking_allowed()`
38
  *
39
  * @since 1.0.1
 
 
 
40
  * @return bool
41
  */
42
- abstract function is_registered();
43
 
44
  /**
45
  * Check if the user skipped connecting the account with Freemius.
@@ -61,68 +64,6 @@
61
 
62
  #endregion
63
 
64
- #----------------------------------------------------------------------------------
65
- #region Usage Tracking
66
- #----------------------------------------------------------------------------------
67
-
68
- /**
69
- * Returns TRUE if the user opted-in and didn't disconnect (opt-out).
70
- *
71
- * @author Leo Fajardo (@leorw)
72
- * @since 1.2.1.5
73
- *
74
- * @return bool
75
- */
76
- abstract function is_tracking_allowed();
77
-
78
- /**
79
- * Returns TRUE if the user never opted-in or manually opted-out.
80
- *
81
- * @author Vova Feldman (@svovaf)
82
- * @since 1.2.1.5
83
- *
84
- * @return bool
85
- */
86
- function is_tracking_prohibited() {
87
- return ! $this->is_registered() || ! $this->is_tracking_allowed();
88
- }
89
-
90
- /**
91
- * Opt-out from usage tracking.
92
- *
93
- * Note: This will not delete the account information but will stop all tracking.
94
- *
95
- * Returns:
96
- * 1. FALSE - If the user never opted-in.
97
- * 2. TRUE - If successfully opted-out.
98
- * 3. object - API Result on failure.
99
- *
100
- * @author Leo Fajardo (@leorw)
101
- * @since 1.2.1.5
102
- *
103
- * @return bool|object
104
- */
105
- abstract function stop_tracking();
106
-
107
- /**
108
- * Opt-in back into usage tracking.
109
- *
110
- * Note: This will only work if the user opted-in previously.
111
- *
112
- * Returns:
113
- * 1. FALSE - If the user never opted-in.
114
- * 2. TRUE - If successfully opted-in back to usage tracking.
115
- * 3. object - API result on failure.
116
- *
117
- * @author Leo Fajardo (@leorw)
118
- * @since 1.2.1.5
119
- *
120
- * @return bool|object
121
- */
122
- abstract function allow_tracking();
123
-
124
- #endregion
125
-
126
  #----------------------------------------------------------------------------------
127
  #region Module Type
128
  #----------------------------------------------------------------------------------
37
  * `$fs->is_registered() && $fs->is_tracking_allowed()`
38
  *
39
  * @since 1.0.1
40
+ *
41
+ * @param bool $ignore_anonymous_state Since 2.5.1
42
+ *
43
  * @return bool
44
  */
45
+ abstract function is_registered( $ignore_anonymous_state = false );
46
 
47
  /**
48
  * Check if the user skipped connecting the account with Freemius.
64
 
65
  #endregion
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  #----------------------------------------------------------------------------------
68
  #region Module Type
69
  #----------------------------------------------------------------------------------
freemius/includes/class-freemius.php CHANGED
@@ -377,6 +377,8 @@
377
  const REASON_DIDNT_WORK_AS_EXPECTED = 14;
378
  const REASON_TEMPORARY_DEACTIVATION = 15;
379
 
 
 
380
  /**
381
  * @author Leo Fajardo (@leorw)
382
  * @since 2.3.1
@@ -392,7 +394,8 @@
392
  */
393
  private $_pricing_js_path = null;
394
 
395
- #endregion
 
396
 
397
  /* Ctor
398
  ------------------------------------------------------------------------------------------------------------------*/
@@ -408,8 +411,10 @@
408
  * @param bool $is_init Since 1.2.1 Is initiation sequence.
409
  */
410
  private function __construct( $module_id, $slug = false, $is_init = false ) {
 
 
411
  if ( $is_init && is_numeric( $module_id ) && is_string( $slug ) ) {
412
- $this->store_id_slug_type_path_map( $module_id, $slug );
413
  }
414
 
415
  $this->_module_id = $module_id;
@@ -424,7 +429,7 @@
424
 
425
  $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $this->get_unique_affix(), WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
426
 
427
- $this->_plugin_main_file_path = $this->_find_caller_plugin_file( $is_init );
428
  $this->_plugin_dir_path = plugin_dir_path( $this->_plugin_main_file_path );
429
  $this->_plugin_basename = $this->get_plugin_basename();
430
  $this->_free_plugin_basename = str_replace( '-premium/', '/', $this->_plugin_basename );
@@ -519,7 +524,10 @@
519
  * @author Leo Fajardo (@leorw)
520
  * @since 1.2.2
521
  */
522
- ( is_object( $this->_plugin ) ? $this->_plugin->title : $this->get_plugin_name() ),
 
 
 
523
  $this->get_unique_affix()
524
  );
525
 
@@ -933,82 +941,27 @@
933
  * @param string $sdk_version
934
  */
935
  function _sdk_version_update( $sdk_prev_version, $sdk_version ) {
936
- /**
937
- * @since 1.1.7.3 Fixed unwanted connectivity test cleanup.
938
- */
939
  if ( empty( $sdk_prev_version ) ) {
940
  return;
941
  }
942
 
943
- if ( version_compare( $sdk_prev_version, '2.1.0', '<' ) &&
944
- version_compare( $sdk_version, '2.1.0', '>=' )
945
- ) {
946
- $this->_storage->handle_gdpr_admin_notice = true;
947
- }
948
-
949
- if ( version_compare( $sdk_prev_version, '2.0.0', '<' ) &&
950
- version_compare( $sdk_version, '2.0.0', '>=' )
951
- ) {
952
- $this->migrate_to_subscriptions_collection();
953
-
954
- $this->consolidate_licenses();
955
-
956
- // Clear trial_plan since it's now loaded from the plans collection when needed.
957
- $this->_storage->remove( 'trial_plan', true, false );
958
- }
959
-
960
- if ( version_compare( $sdk_prev_version, '1.2.3', '<' ) &&
961
- version_compare( $sdk_version, '1.2.3', '>=' )
962
- ) {
963
- /**
964
- * Starting from version 1.2.3, paths are stored as relative instead of absolute and some of them can be
965
- * invalid.
966
- *
967
- * @author Leo Fajardo (@leorw)
968
- */
969
- $this->remove_invalid_paths();
970
- }
971
-
972
- if ( version_compare( $sdk_prev_version, '1.1.5', '<' ) &&
973
- version_compare( $sdk_version, '1.1.5', '>=' )
974
  ) {
975
- // On version 1.1.5 merged connectivity and is_on data.
976
- if ( isset( $this->_storage->connectivity_test ) ) {
977
- if ( ! isset( $this->_storage->is_on ) ) {
978
- unset( $this->_storage->connectivity_test );
979
- } else {
980
- $connectivity_data = $this->_storage->connectivity_test;
981
- $connectivity_data['is_active'] = $this->_storage->is_on['is_active'];
982
- $connectivity_data['timestamp'] = $this->_storage->is_on['timestamp'];
983
-
984
- // Override.
985
- $this->_storage->connectivity_test = $connectivity_data;
986
 
987
- // Remove previous structure.
988
- unset( $this->_storage->is_on );
989
- }
990
 
 
991
  }
992
  }
993
-
994
- if (
995
- version_compare( $sdk_prev_version, '2.2.1', '<' ) &&
996
- version_compare( $sdk_version, '2.2.1', '>=' )
997
- ) {
998
- /**
999
- * Clear the file cache without storing the previous path since it could be a wrong path. For example,
1000
- * in the versions of the SDK lower than 2.2.1, it's possible for the path of an add-on to be the same
1001
- * as the parent plugin's when the add-on was auto-installed since the relevant method names were not
1002
- * skipped in the logic that determines the right path in the `get_caller_main_file_and_type` method
1003
- * (e.g. `try_activate_plugin`). Since it was an auto-installation, the caller was the parent plugin
1004
- * and so its path was used. In case the stored path is wrong, clearing the cache will resolve issues
1005
- * related to data mix-up between plugins (e.g. titles and versions of an add-on and its parent plugin).
1006
- *
1007
- * @author Leo Fajardo (@leorw)
1008
- * @since 2.2.1
1009
- */
1010
- $this->clear_module_main_file_cache( false );
1011
- }
1012
  }
1013
 
1014
  /**
@@ -1059,102 +1012,6 @@
1059
  }
1060
  }
1061
 
1062
- /**
1063
- * @author Leo Fajardo (@leorw)
1064
- * @since 2.0.0
1065
- */
1066
- private function migrate_to_subscriptions_collection() {
1067
- if ( ! is_object( $this->_site ) ) {
1068
- return;
1069
- }
1070
-
1071
- if ( isset( $this->_storage->subscription ) && is_object( $this->_storage->subscription ) ) {
1072
- $this->_storage->subscriptions = array( fs_get_entity( $this->_storage->subscription, FS_Subscription::get_class_name() ) );
1073
- }
1074
- }
1075
-
1076
- /**
1077
- * @author Leo Fajardo (@leorw)
1078
- * @since 2.0.0
1079
- */
1080
- private function consolidate_licenses() {
1081
- $plugin_licenses = self::get_account_option( 'licenses', WP_FS__MODULE_TYPE_PLUGIN );
1082
- if ( isset( $plugin_licenses[ $this->_slug ] ) ) {
1083
- $plugin_licenses = $plugin_licenses[ $this->_slug ];
1084
- } else {
1085
- $plugin_licenses = array();
1086
- }
1087
-
1088
- $theme_licenses = self::get_account_option( 'licenses', WP_FS__MODULE_TYPE_THEME );
1089
- if ( isset( $theme_licenses[ $this->_slug ] ) ) {
1090
- $theme_licenses = $theme_licenses[ $this->_slug ];
1091
- } else {
1092
- $theme_licenses = array();
1093
- }
1094
-
1095
- if ( empty( $plugin_licenses ) && empty( $theme_licenses ) ) {
1096
- return;
1097
- }
1098
-
1099
- $all_licenses = array();
1100
- $user_id_license_ids_map = array();
1101
-
1102
- foreach ( $plugin_licenses as $user_id => $user_licenses ) {
1103
- if ( is_array( $user_licenses ) ) {
1104
- if ( ! isset( $user_license_ids[ $user_id ] ) ) {
1105
- $user_id_license_ids_map[ $user_id ] = array();
1106
- }
1107
-
1108
- foreach ( $user_licenses as $user_license ) {
1109
- $all_licenses[] = $user_license;
1110
- $user_id_license_ids_map[ $user_id ][] = $user_license->id;
1111
- }
1112
- }
1113
- }
1114
-
1115
- foreach ( $theme_licenses as $user_id => $user_licenses ) {
1116
- if ( is_array( $user_licenses ) ) {
1117
- if ( ! isset( $user_license_ids[ $user_id ] ) ) {
1118
- $user_id_license_ids_map[ $user_id ] = array();
1119
- }
1120
-
1121
- foreach ( $user_licenses as $user_license ) {
1122
- $all_licenses[] = $user_license;
1123
- $user_id_license_ids_map[ $user_id ][] = $user_license->id;
1124
- }
1125
- }
1126
- }
1127
-
1128
- self::store_user_id_license_ids_map(
1129
- $user_id_license_ids_map,
1130
- $this->_module_id
1131
- );
1132
-
1133
- $this->_store_licenses( true, $this->_module_id, $all_licenses );
1134
- }
1135
-
1136
- /**
1137
- * Remove invalid paths.
1138
- *
1139
- * @author Leo Fajardo (@leorw)
1140
- * @since 1.2.3
1141
- */
1142
- private function remove_invalid_paths() {
1143
- // Remove invalid path that is still associated with the current slug if there's any.
1144
- $file_slug_map = self::$_accounts->get_option( 'file_slug_map', array() );
1145
- foreach ( $file_slug_map as $plugin_basename => $slug ) {
1146
- if ( $slug === $this->_slug &&
1147
- $plugin_basename !== $this->_plugin_basename &&
1148
- ! file_exists( $this->get_absolute_path( $plugin_basename ) )
1149
- ) {
1150
- unset( $file_slug_map[ $plugin_basename ] );
1151
- self::$_accounts->set_option( 'file_slug_map', $file_slug_map, true );
1152
-
1153
- break;
1154
- }
1155
- }
1156
- }
1157
-
1158
  /**
1159
  * @author Vova Feldman (@svovaf)
1160
  * @since 1.2.2.7
@@ -1491,46 +1348,6 @@
1491
  }
1492
  }
1493
 
1494
- /**
1495
- * Add special parameter to WP admin AJAX calls so when we
1496
- * process AJAX calls we can identify its source properly.
1497
- *
1498
- * @author Leo Fajardo (@leorw)
1499
- * @since 2.0.0
1500
- */
1501
- static function _enrich_ajax_url() {
1502
- $admin_param = is_network_admin() ?
1503
- '_fs_network_admin' :
1504
- '_fs_blog_admin';
1505
- ?>
1506
- <script type="text/javascript">
1507
- (function ($) {
1508
- $(document).ajaxSend(function (event, jqxhr, settings) {
1509
- if (settings.url &&
1510
- -1 < settings.url.indexOf('admin-ajax.php') &&
1511
- ! ( settings.url.indexOf( '<?php echo $admin_param ?>' ) > 0 )
1512
- ) {
1513
- if (
1514
- 'string' === typeof settings.data &&
1515
- settings.data.indexOf( 'action=heartbeat' ) > 0
1516
- ) {
1517
- return;
1518
- }
1519
-
1520
- if (settings.url.indexOf('?') > 0) {
1521
- settings.url += '&';
1522
- } else {
1523
- settings.url += '?';
1524
- }
1525
-
1526
- settings.url += '<?php echo $admin_param ?>=true';
1527
- }
1528
- });
1529
- })(jQuery);
1530
- </script>
1531
- <?php
1532
- }
1533
-
1534
  /**
1535
  * Opens the support forum subemenu item in a new browser page.
1536
  *
@@ -1641,8 +1458,10 @@
1641
  }
1642
 
1643
  if ( $this->is_plugin() ) {
1644
- if ( $this->_is_network_active ) {
1645
  add_action( 'wpmu_new_blog', array( $this, '_after_new_blog_callback' ), 10, 6 );
 
 
1646
  }
1647
 
1648
  register_deactivation_hook( $this->_plugin_main_file_path, array( &$this, '_deactivate_plugin_hook' ) );
@@ -1652,7 +1471,12 @@
1652
  add_action( 'deactivate_blog', array( &$this, '_after_site_deactivated_callback' ) );
1653
  add_action( 'archive_blog', array( &$this, '_after_site_deactivated_callback' ) );
1654
  add_action( 'make_spam_blog', array( &$this, '_after_site_deactivated_callback' ) );
1655
- add_action( 'deleted_blog', array( &$this, '_after_site_deleted_callback' ), 10, 2 );
 
 
 
 
 
1656
 
1657
  add_action( 'activate_blog', array( &$this, '_after_site_reactivated_callback' ) );
1658
  add_action( 'unarchive_blog', array( &$this, '_after_site_reactivated_callback' ) );
@@ -1677,6 +1501,7 @@
1677
  add_action( 'admin_init', array( &$this, '_add_premium_version_upgrade_selection' ) );
1678
  add_action( 'admin_init', array( &$this, '_add_beta_mode_update_handler' ) );
1679
  add_action( 'admin_init', array( &$this, '_add_user_change_option' ) );
 
1680
 
1681
  $this->add_ajax_action( 'update_billing', array( &$this, '_update_billing_ajax_action' ) );
1682
  $this->add_ajax_action( 'start_trial', array( &$this, '_start_trial_ajax_action' ) );
@@ -2048,7 +1873,13 @@
2048
  return;
2049
  }
2050
 
2051
- $this->_add_tracking_links();
 
 
 
 
 
 
2052
 
2053
  if ( self::is_plugins_page() && $this->is_plugin() ) {
2054
  $this->hook_plugin_action_links();
@@ -2094,20 +1925,27 @@
2094
  /**
2095
  * Leverage backtrace to find caller plugin file path.
2096
  *
2097
- * @author Vova Feldman (@svovaf)
2098
- * @since 1.0.6
2099
- *
2100
- * @param bool $is_init Is initiation sequence.
2101
  *
2102
  * @return string
 
 
 
2103
  */
2104
- private function _find_caller_plugin_file( $is_init = false ) {
2105
  // Try to load the cached value of the file path.
2106
  if ( isset( $this->_storage->plugin_main_file ) ) {
2107
  $plugin_main_file = $this->_storage->plugin_main_file;
2108
  if ( ! empty( $plugin_main_file->path ) ) {
2109
  $absolute_path = $this->get_absolute_path( $plugin_main_file->path );
2110
  if ( file_exists( $absolute_path ) ) {
 
 
 
 
 
 
2111
  return $absolute_path;
2112
  }
2113
  }
@@ -2148,12 +1986,11 @@
2148
  * Only the original instantiator that calls dynamic_init can modify the module's path.
2149
  */
2150
  // Find caller module.
2151
- $id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map', array() );
2152
  $this->_storage->plugin_main_file = (object) array(
2153
- 'path' => $id_slug_type_path_map[ $this->_module_id ]['path'],
2154
  );
2155
 
2156
- return $this->get_absolute_path( $id_slug_type_path_map[ $this->_module_id ]['path'] );
2157
  }
2158
 
2159
  /**
@@ -2215,6 +2052,8 @@
2215
  * @param number $module_id
2216
  * @param string $slug
2217
  *
 
 
2218
  * @since 1.2.2
2219
  */
2220
  private function store_id_slug_type_path_map( $module_id, $slug ) {
@@ -2236,20 +2075,52 @@
2236
  $store_option = true;
2237
  }
2238
 
2239
- if ( empty( $id_slug_type_path_map[ $module_id ]['path'] ) ||
2240
- /**
2241
- * This verification is for cases when suddenly the same module
2242
- * is installed but with a different folder name.
2243
- *
2244
- * @author Vova Feldman (@svovaf)
2245
- * @since 1.2.3
2246
- */
2247
- ! file_exists( $this->get_absolute_path(
2248
- $id_slug_type_path_map[ $module_id ]['path'],
2249
- $id_slug_type_path_map[ $module_id ]['type']
2250
- ) )
2251
- ) {
2252
- $caller_main_file_and_type = $this->get_caller_main_file_and_type();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2253
 
2254
  $id_slug_type_path_map[ $module_id ]['type'] = $caller_main_file_and_type->module_type;
2255
  $id_slug_type_path_map[ $module_id ]['path'] = $caller_main_file_and_type->path;
@@ -2260,6 +2131,8 @@
2260
  if ( $store_option ) {
2261
  self::$_accounts->set_option( 'id_slug_type_path_map', $id_slug_type_path_map, true );
2262
  }
 
 
2263
  }
2264
 
2265
  /**
@@ -2273,8 +2146,10 @@
2273
  * add-ons are relying on loading the SDK from the parent module, and also allows themes including the
2274
  * SDK an internal file instead of directly from functions.php.
2275
  * @since 1.2.1.7 Knows how to handle cases when an add-on includes the parent module logic.
 
 
2276
  */
2277
- private function get_caller_main_file_and_type() {
2278
  self::require_plugin_essentials();
2279
 
2280
  $all_plugins = fs_get_plugins( true );
@@ -2413,10 +2288,12 @@
2413
  }
2414
  }
2415
 
2416
- return (object) array(
2417
  'module_type' => $module_type,
2418
  'path' => $caller_file_candidate
2419
  );
 
 
2420
  }
2421
 
2422
  #----------------------------------------------------------------------------------
@@ -2433,6 +2310,13 @@
2433
  * @since 1.1.2
2434
  */
2435
  function _add_deactivation_feedback_dialog_box() {
 
 
 
 
 
 
 
2436
  $subscription_cancellation_dialog_box_template_params = $this->apply_filters( 'show_deactivation_subscription_cancellation', true ) ?
2437
  $this->_get_subscription_cancellation_dialog_box_template_params() :
2438
  array();
@@ -2440,7 +2324,7 @@
2440
  /**
2441
  * @since 2.3.0 Developers can optionally hide the deactivation feedback form using the 'show_deactivation_feedback_form' filter.
2442
  */
2443
- $show_deactivation_feedback_form = true;
2444
  if ( $this->has_filter( 'show_deactivation_feedback_form' ) ) {
2445
  $show_deactivation_feedback_form = $this->apply_filters( 'show_deactivation_feedback_form', true );
2446
  } else if ( $this->is_addon() ) {
@@ -2545,7 +2429,7 @@
2545
  $reason_temporary_deactivation = array(
2546
  'id' => self::REASON_TEMPORARY_DEACTIVATION,
2547
  'text' => sprintf(
2548
- $this->get_text_inline( "It's a temporary %s. I'm just debugging an issue.", 'reason-temporary-x' ),
2549
  strtolower( $this->is_plugin() ?
2550
  $this->get_text_inline( 'Deactivation', 'deactivation' ) :
2551
  $this->get_text_inline( 'Theme Switch', 'theme-switch' )
@@ -2710,6 +2594,14 @@
2710
 
2711
  $this->_storage->store( 'uninstall_reason', $reason );
2712
 
 
 
 
 
 
 
 
 
2713
  /**
2714
  * If the module type is "theme", trigger the uninstall event here (on theme deactivation) since themes do
2715
  * not support uninstall hook.
@@ -2731,6 +2623,73 @@
2731
  exit;
2732
  }
2733
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2734
  /**
2735
  * @author Leo Fajardo (@leorw)
2736
  * @since 2.1.4
@@ -2927,6 +2886,13 @@
2927
  return self::instance( $addon_id );
2928
  }
2929
 
 
 
 
 
 
 
 
2930
  #endregion ------------------------------------------------------------------
2931
 
2932
  /**
@@ -3437,7 +3403,9 @@
3437
  add_action( 'plugins_loaded', array( 'Freemius', '_load_textdomain' ), 1 );
3438
  }
3439
 
3440
- add_action( 'admin_footer', array( 'Freemius', '_enrich_ajax_url' ) );
 
 
3441
  add_action( 'admin_footer', array( 'Freemius', '_open_support_forum_in_new_page' ) );
3442
 
3443
  if ( self::is_plugins_page() || self::is_themes_page() ) {
@@ -3456,48 +3424,197 @@
3456
  self::$_statics_loaded = true;
3457
  }
3458
 
 
 
 
 
3459
  /**
3460
  * @author Leo Fajardo (@leorw)
 
3461
  *
3462
- * @since 2.1.3
 
 
3463
  */
3464
- private static function migrate_options_to_network() {
3465
- self::migrate_accounts_to_network();
3466
-
3467
- // Migrate API options from site level to network level.
3468
- $api_network_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true, true );
3469
- $api_network_options->migrate_to_network();
3470
-
3471
- // Migrate API cache to network level storage.
3472
- FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME )->migrate_to_network();
3473
 
3474
- self::$_accounts->set_option( 'ms_migration_complete', true, true );
3475
  }
3476
 
3477
- #----------------------------------------------------------------------------------
3478
- #region Localization
3479
- #----------------------------------------------------------------------------------
3480
-
3481
  /**
3482
- * Load framework's text domain.
 
3483
  *
3484
- * @author Vova Feldman (@svovaf)
3485
- * @since 1.2.1
3486
  */
3487
- static function _load_textdomain() {
3488
- if ( ! is_admin() ) {
3489
- return;
3490
  }
3491
 
3492
- global $fs_active_plugins;
3493
 
3494
- // Works both for plugins and themes.
3495
- load_plugin_textdomain(
3496
- 'freemius',
3497
- false,
3498
- $fs_active_plugins->newest->sdk_path . '/languages/'
3499
- );
3500
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3501
 
3502
  #endregion
3503
 
@@ -3531,7 +3648,7 @@
3531
  } else {
3532
  // Add hidden debug page.
3533
  $hook = FS_Admin_Menu_Manager::add_subpage(
3534
- null,
3535
  $title,
3536
  $title,
3537
  'manage_options',
@@ -3692,6 +3809,10 @@
3692
 
3693
  switch_to_blog( $current_blog_id );
3694
  }
 
 
 
 
3695
  } else if ( fs_request_is_action( 'simulate_trial' ) ) {
3696
  check_admin_referer( 'simulate_trial' );
3697
 
@@ -3740,55 +3861,69 @@
3740
  }
3741
 
3742
  /**
3743
- * @author Vova Feldman (@svovaf)
3744
- * @since 1.0.8
 
 
3745
  */
3746
- static function _debug_page_render() {
3747
  self::$_static_logger->entrance();
3748
 
 
 
 
 
 
 
 
3749
  if ( ! is_multisite() ) {
3750
- $all_plugins_installs = self::get_all_sites( WP_FS__MODULE_TYPE_PLUGIN );
3751
- $all_themes_installs = self::get_all_sites( WP_FS__MODULE_TYPE_THEME );
 
 
 
 
 
3752
  } else {
3753
  $sites = self::get_sites();
3754
 
3755
- $all_plugins_installs = array();
3756
- $all_themes_installs = array();
3757
-
3758
  foreach ( $sites as $site ) {
3759
  $blog_id = self::get_site_blog_id( $site );
3760
 
3761
- $plugins_installs = self::get_all_sites( WP_FS__MODULE_TYPE_PLUGIN, $blog_id );
3762
-
3763
- foreach ( $plugins_installs as $slug => $install ) {
3764
- if ( ! isset( $all_plugins_installs[ $slug ] ) ) {
3765
- $all_plugins_installs[ $slug ] = array();
3766
- }
3767
-
3768
- $install->blog_id = $blog_id;
3769
 
3770
- $all_plugins_installs[ $slug ][] = $install;
3771
- }
 
 
3772
 
3773
- $themes_installs = self::get_all_sites( WP_FS__MODULE_TYPE_THEME, $blog_id );
3774
 
3775
- foreach ( $themes_installs as $slug => $install ) {
3776
- if ( ! isset( $all_themes_installs[ $slug ] ) ) {
3777
- $all_themes_installs[ $slug ] = array();
3778
  }
3779
 
3780
- $install->blog_id = $blog_id;
3781
-
3782
- $all_themes_installs[ $slug ][] = $install;
3783
  }
3784
  }
3785
  }
3786
 
 
 
 
 
 
 
 
 
 
 
 
 
3787
  $licenses_by_module_type = self::get_all_licenses_by_module_type();
3788
 
3789
  $vars = array(
3790
- 'plugin_sites' => $all_plugins_installs,
3791
- 'theme_sites' => $all_themes_installs,
3792
  'users' => self::get_all_users(),
3793
  'addons' => self::get_all_addons(),
3794
  'account_addons' => self::get_all_account_addons(),
@@ -3820,6 +3955,10 @@
3820
  function is_on() {
3821
  self::$_static_logger->entrance();
3822
 
 
 
 
 
3823
  if ( isset( $this->_is_on ) ) {
3824
  return $this->_is_on;
3825
  }
@@ -3908,18 +4047,24 @@
3908
 
3909
  $is_update = $this->apply_filters( 'is_plugin_update', $this->is_plugin_update() );
3910
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3911
  return $this->get_api_plugin_scope()->ping(
3912
  $this->get_anonymous_id( $blog_id ),
3913
- array(
3914
- 'is_update' => json_encode( $is_update ),
3915
- 'version' => $version,
3916
- 'sdk' => $this->version,
3917
- 'is_admin' => json_encode( is_admin() ),
3918
- 'is_ajax' => json_encode( self::is_ajax() ),
3919
- 'is_cron' => json_encode( self::is_cron() ),
3920
- 'is_gdpr_test' => $is_gdpr_test,
3921
- 'is_http' => json_encode( WP_FS__IS_HTTP_REQUEST ),
3922
- )
3923
  );
3924
  }
3925
 
@@ -4056,7 +4201,7 @@
4056
  $unique_id = self::$_accounts->get_option( 'unique_id', null, $blog_id );
4057
 
4058
  if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
4059
- $key = fs_strip_url_protocol( get_site_url( $blog_id ) );
4060
 
4061
  $secure_auth = defined( 'SECURE_AUTH_KEY' ) ? SECURE_AUTH_KEY : '';
4062
  if ( empty( $secure_auth ) ||
@@ -4085,6 +4230,17 @@
4085
  return $unique_id;
4086
  }
4087
 
 
 
 
 
 
 
 
 
 
 
 
4088
  /**
4089
  * @author Vova Feldman (@svovaf)
4090
  * @since 1.1.7.4
@@ -4233,11 +4389,16 @@
4233
  $install_previous_desc = $this->esc_html_inline( 'Uninstall this version and install the previous one.', 'install-previous-desc' );
4234
  $fix_issue_title = $this->esc_html_inline( 'Yes - I\'m giving you a chance to fix it', 'fix-issue-title' );
4235
  $fix_issue_desc = $this->esc_html_inline( 'We will do our best to whitelist your server and resolve this issue ASAP. You will get a follow-up email to %s once we have an update.', 'fix-issue-desc' );
4236
- /* translators: %s: product title (e.g. "Awesome Plugin" requires an access to...) */
4237
- $x_requires_access_to_api = $this->esc_html_inline( '%s requires an access to our API.', 'x-requires-access-to-api' );
4238
  $sysadmin_title = $this->esc_html_inline( 'I\'m a system administrator', 'sysadmin-title' );
4239
  $happy_to_resolve_issue_asap = $this->esc_html_inline( 'We are sure it\'s an issue on our side and more than happy to resolve it for you ASAP if you give us a chance.', 'happy-to-resolve-issue-asap' );
4240
 
 
 
 
 
 
4241
  $message = false;
4242
  if ( is_object( $api_result ) &&
4243
  isset( $api_result->error ) &&
@@ -4869,7 +5030,7 @@
4869
 
4870
  /**
4871
  * This should be executed even if Freemius is off for the core module,
4872
- * otherwise, the add-ons dialogbox won't work properly. This is esepcially
4873
  * relevant when the developer decided to turn FS off for existing users.
4874
  *
4875
  * @author Vova Feldman (@svovaf)
@@ -4907,22 +5068,25 @@
4907
  * @since 1.1.7.3
4908
  *
4909
  */
4910
- if ( $this->is_registered() ) {
4911
- if ( ! $this->is_sync_cron_on() && $this->is_tracking_allowed() ) {
4912
- $this->schedule_sync_cron();
4913
- }
4914
  }
4915
 
4916
  /**
4917
  * Check if requested for manual blocking background sync.
4918
  */
4919
  if ( fs_request_has( 'background_sync' ) ) {
 
 
 
4920
  $this->run_manual_sync();
4921
  }
4922
  }
4923
  }
4924
 
4925
  if ( $this->is_registered() ) {
 
 
4926
  $this->hook_callback_to_install_sync();
4927
  }
4928
 
@@ -4937,6 +5101,28 @@
4937
  }
4938
 
4939
  if ( $this->is_user_in_admin() ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4940
  if ( $this->is_addon() ) {
4941
  if ( ! $this->is_parent_plugin_installed() ) {
4942
  $parent_name = $this->get_option( $plugin_info, 'parent_name', null );
@@ -5041,7 +5227,7 @@
5041
  * because the updater has some logic that needs to be executed
5042
  * during AJAX calls.
5043
  *
5044
- * Currently we need to hook to the `http_request_host_is_external` filter.
5045
  * In the future, there might be additional logic added.
5046
  *
5047
  * @author Vova Feldman
@@ -5060,7 +5246,8 @@
5060
  */
5061
  ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->premium_plugin_basename() ) ) )
5062
  ) &&
5063
- $this->has_release_on_freemius()
 
5064
  ) {
5065
  FS_Plugin_Updater::instance( $this );
5066
  }
@@ -5129,150 +5316,70 @@
5129
  }
5130
 
5131
  /**
5132
- * @author Leo Fajardo (@leorw)
 
 
5133
  *
5134
- * @since 1.2.1.5
5135
  */
5136
- function _stop_tracking_callback() {
5137
  $this->_logger->entrance();
5138
 
5139
- $this->check_ajax_referer( 'stop_tracking' );
5140
-
5141
- $result = $this->stop_tracking( fs_is_network_admin() );
5142
-
5143
- if ( true === $result ) {
5144
- self::shoot_ajax_success();
5145
- }
5146
-
5147
- $this->_logger->api_error( $result );
5148
-
5149
- self::shoot_ajax_failure(
5150
- sprintf( $this->get_text_inline( 'Unexpected API error. Please contact the %s\'s author with the following error.', 'unexpected-api-error' ), $this->_module_type ) .
5151
- ( $this->is_api_error( $result ) && isset( $result->error ) ?
5152
- $result->error->message :
5153
- var_export( $result, true ) )
5154
  );
5155
- }
5156
-
5157
- /**
5158
- * @author Leo Fajardo (@leorw)
5159
- * @since 1.2.1.5
5160
- */
5161
- function _allow_tracking_callback() {
5162
- $this->_logger->entrance();
5163
-
5164
- $this->check_ajax_referer( 'allow_tracking' );
5165
-
5166
- $result = $this->allow_tracking( fs_is_network_admin() );
5167
 
5168
- if ( true === $result ) {
5169
- self::shoot_ajax_success();
 
 
5170
  }
5171
 
5172
- $this->_logger->api_error( $result );
5173
-
5174
- self::shoot_ajax_failure(
5175
- sprintf( $this->get_text_inline( 'Unexpected API error. Please contact the %s\'s author with the following error.', 'unexpected-api-error' ), $this->_module_type ) .
5176
- ( $this->is_api_error( $result ) && isset( $result->error ) ?
5177
- $result->error->message :
5178
- var_export( $result, true ) )
5179
- );
5180
- }
5181
-
5182
- /**
5183
- * Opt-out from usage tracking.
5184
- *
5185
- * Note: This will not delete the account information but will stop all tracking.
5186
- *
5187
- * Returns:
5188
- * 1. FALSE - If the user never opted-in.
5189
- * 2. TRUE - If successfully opted-out.
5190
- * 3. object - API result on failure.
5191
- *
5192
- * @author Leo Fajardo (@leorw)
5193
- * @since 1.2.1.5
5194
- *
5195
- * @return bool|object
5196
- */
5197
- function stop_site_tracking() {
5198
- $this->_logger->entrance();
5199
-
5200
- if ( ! $this->is_registered() ) {
5201
- // User never opted-in.
5202
- return false;
5203
- }
5204
 
5205
- if ( $this->is_tracking_prohibited() ) {
5206
- // Already disconnected.
5207
- return true;
5208
  }
5209
 
5210
- // Send update to FS.
5211
- $result = $this->get_api_site_scope()->call( '/?fields=is_disconnected', 'put', array(
5212
- 'is_disconnected' => true
5213
- ) );
5214
-
5215
- if ( ! $this->is_api_result_entity( $result ) ||
5216
- ! isset( $result->is_disconnected ) ||
5217
- ! $result->is_disconnected
5218
  ) {
5219
  $this->_logger->api_error( $result );
5220
 
5221
  return $result;
5222
  }
5223
 
5224
- $this->_site->is_disconnected = $result->is_disconnected;
5225
- $this->_store_site();
5226
-
5227
- $this->clear_sync_cron();
5228
-
5229
- // Successfully disconnected.
5230
  return true;
5231
  }
5232
 
5233
  /**
5234
- * Opt-out network from usage tracking.
5235
- *
5236
- * Note: This will not delete the account information but will stop all tracking.
5237
  *
5238
- * Returns:
5239
- * 1. FALSE - If the user never opted-in.
5240
- * 2. TRUE - If successfully opted-out.
5241
- * 3. object - API result on failure.
5242
- *
5243
- * @author Leo Fajardo (@leorw)
5244
- * @since 1.2.1.5
5245
- *
5246
- * @return bool|object
5247
  */
5248
- function stop_network_tracking() {
 
 
 
 
5249
  $this->_logger->entrance();
5250
 
5251
- if ( ! $this->is_registered() ) {
5252
- // User never opted-in.
5253
- return false;
5254
- }
5255
-
5256
  $install_id_2_blog_id = array();
5257
- $installs_map = $this->get_blog_install_map();
5258
 
5259
- $opt_out_all = true;
5260
-
5261
- $params = array();
5262
- foreach ( $installs_map as $blog_id => $install ) {
5263
- if ( $install->is_tracking_prohibited() ) {
5264
- // Already opted-out.
5265
- continue;
5266
- }
5267
 
 
5268
  if ( $this->is_site_delegated_connection( $blog_id ) ) {
5269
- // Opt-out only from non-delegated installs.
5270
- $opt_out_all = false;
5271
  continue;
5272
  }
5273
 
5274
- $params[] = array( 'id' => $install->id );
5275
-
5276
  $install_id_2_blog_id[ $install->id ] = $blog_id;
5277
  }
5278
 
@@ -5280,171 +5387,188 @@
5280
  return true;
5281
  }
5282
 
5283
- $params[] = array( 'is_disconnected' => true );
 
 
 
 
5284
 
5285
  // Send update to FS.
5286
- $result = $this->get_current_or_network_user_api_scope()->call( "/plugins/{$this->_module_id}/installs.json", 'put', $params );
 
 
 
 
5287
 
5288
- if ( ! $this->is_api_result_object( $result, 'installs' ) ) {
5289
  $this->_logger->api_error( $result );
5290
 
5291
  return $result;
5292
  }
5293
 
5294
- foreach ( $result->installs as $r_install ) {
5295
- $blog_id = $install_id_2_blog_id[ $r_install->id ];
5296
- $install = $installs_map[ $blog_id ];
5297
- $install->is_disconnected = $r_install->is_disconnected;
5298
- $this->_store_site( true, $blog_id, $install );
5299
- }
5300
-
5301
- $this->clear_sync_cron( $opt_out_all );
5302
-
5303
- // Successfully disconnected.
5304
  return true;
5305
  }
5306
 
5307
  /**
5308
- * Opt-out from usage tracking.
5309
- *
5310
- * Note: This will not delete the account information but will stop all tracking.
5311
- *
5312
- * Returns:
5313
- * 1. FALSE - If the user never opted-in.
5314
- * 2. TRUE - If successfully opted-out.
5315
- * 3. object - API result on failure.
5316
- *
5317
- * @author Leo Fajardo (@leorw)
5318
- * @since 1.2.1.5
5319
- *
5320
- * @param bool $is_network_action
5321
  *
5322
- * @return bool|object
5323
  */
5324
- function stop_tracking( $is_network_action = false ) {
5325
- $this->_logger->entrance();
 
 
 
 
 
 
 
 
 
 
 
 
5326
 
5327
- return $is_network_action ?
5328
- $this->stop_network_tracking() :
5329
- $this->stop_site_tracking();
5330
  }
5331
 
5332
  /**
5333
- * Opt-in back into usage tracking.
5334
- *
5335
- * Note: This will only work if the user opted-in previously.
5336
- *
5337
- * Returns:
5338
- * 1. FALSE - If the user never opted-in.
5339
- * 2. TRUE - If successfully opted-in back to usage tracking.
5340
- * 3. object - API result on failure.
5341
- *
5342
- * @author Leo Fajardo (@leorw)
5343
- * @since 1.2.1.5
5344
- *
5345
- * @return bool|object
5346
  */
5347
- function allow_site_tracking() {
5348
  $this->_logger->entrance();
5349
 
5350
- if ( ! $this->is_registered() ) {
5351
- // User never opted-in.
5352
- return false;
5353
- }
5354
 
5355
- if ( $this->is_tracking_allowed() ) {
5356
- // Tracking already allowed.
5357
- return true;
5358
  }
5359
 
5360
- $result = $this->get_api_site_scope()->call( '/?is_disconnected', 'put', array(
5361
- 'is_disconnected' => false
5362
- ) );
5363
-
5364
- if ( ! $this->is_api_result_entity( $result ) ||
5365
- ! isset( $result->is_disconnected ) ||
5366
- $result->is_disconnected
5367
- ) {
5368
- $this->_logger->api_error( $result );
5369
 
5370
- return $result;
 
5371
  }
5372
 
5373
- $this->_site->is_disconnected = $result->is_disconnected;
5374
- $this->_store_site();
5375
 
5376
- $this->schedule_sync_cron();
5377
 
5378
- // Successfully reconnected.
5379
- return true;
 
 
 
5380
  }
5381
 
5382
  /**
5383
- * Opt-in network back into usage tracking.
5384
- *
5385
- * Note: This will only work if the user opted-in previously.
5386
- *
5387
- * Returns:
5388
- * 1. FALSE - If the user never opted-in.
5389
- * 2. TRUE - If successfully opted-in back to usage tracking.
5390
- * 3. object - API result on failure.
5391
- *
5392
- * @author Leo Fajardo (@leorw)
5393
- * @since 1.2.1.5
5394
  *
5395
- * @return bool|object
5396
  */
5397
- function allow_network_tracking() {
5398
- $this->_logger->entrance();
5399
-
5400
- if ( ! $this->is_registered() ) {
5401
  // User never opted-in.
5402
  return false;
5403
  }
5404
 
5405
- $install_id_2_blog_id = array();
5406
- $installs_map = $this->get_blog_install_map();
5407
-
5408
- $params = array();
5409
- foreach ( $installs_map as $blog_id => $install ) {
5410
- if ( $install->is_tracking_allowed() ) {
5411
- continue;
5412
- }
5413
-
5414
- $params[] = array( 'id' => $install->id );
5415
-
5416
- $install_id_2_blog_id[ $install->id ] = $blog_id;
5417
  }
5418
 
5419
- if ( empty( $install_id_2_blog_id ) ) {
5420
- return true;
 
 
 
 
 
 
 
 
5421
  }
5422
 
5423
- $params[] = array( 'is_disconnected' => false );
 
5424
 
5425
- // Send update to FS.
5426
- $result = $this->get_current_or_network_user_api_scope()->call( "/plugins/{$this->_module_id}/installs.json", 'put', $params );
 
 
 
 
 
 
 
 
 
 
5427
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5428
 
5429
- if ( ! $this->is_api_result_object( $result, 'installs' ) ) {
5430
- $this->_logger->api_error( $result );
 
5431
 
5432
- return $result;
5433
- }
 
 
 
 
 
5434
 
5435
- foreach ( $result->installs as $r_install ) {
5436
- $blog_id = $install_id_2_blog_id[ $r_install->id ];
5437
- $install = $installs_map[ $blog_id ];
5438
- $install->is_disconnected = $r_install->is_disconnected;
5439
- $this->_store_site( true, $blog_id, $install );
5440
  }
5441
 
5442
- $this->schedule_sync_cron();
 
 
 
 
5443
 
5444
- // Successfully reconnected.
5445
  return true;
5446
  }
5447
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5448
  /**
5449
  * Opt-in back into usage tracking.
5450
  *
@@ -5458,16 +5582,18 @@
5458
  * @author Leo Fajardo (@leorw)
5459
  * @since 1.2.1.5
5460
  *
5461
- * @param bool $is_network_action
5462
  *
5463
  * @return bool|object
5464
  */
5465
- function allow_tracking( $is_network_action = false ) {
5466
  $this->_logger->entrance();
5467
 
5468
- return $is_network_action ?
5469
- $this->allow_network_tracking() :
5470
- $this->allow_site_tracking();
 
 
5471
  }
5472
 
5473
  /**
@@ -5488,8 +5614,7 @@
5488
 
5489
  if ( ! fs_is_network_admin() || $is_context_single_site ) {
5490
  if ( $this->is_tracking_prohibited() ) {
5491
- $this->_site->is_disconnected = false;
5492
- $this->_store_site();
5493
  }
5494
  } else {
5495
  $installs_map = $this->get_blog_install_map();
@@ -5497,73 +5622,69 @@
5497
  /**
5498
  * @var FS_Site $install
5499
  */
5500
- if ( $install->is_tracking_prohibited() ) {
5501
- $install->is_disconnected = false;
5502
- $this->_store_site( true, $blog_id, $install );
5503
  }
5504
  }
5505
  }
5506
  }
5507
 
5508
  /**
5509
- * @author Vova Feldman (@svovaf)
5510
- * @since 2.3.2
5511
  *
5512
- * @return bool
 
 
 
 
5513
  */
5514
- function is_extensions_tracking_allowed() {
5515
- return ( true === $this->apply_filters(
5516
- 'is_extensions_tracking_allowed',
5517
- $this->_storage->get( 'is_extensions_tracking_allowed', null )
5518
- ) );
5519
- }
5520
 
5521
- /**
5522
- * @author Vova Feldman (@svovaf)
5523
- * @since 2.3.2
5524
- */
5525
- function _update_tracking_permission_callback() {
5526
- $this->_logger->entrance();
5527
 
5528
- $this->check_ajax_referer( 'update_tracking_permission' );
 
 
5529
 
5530
- $is_enabled = fs_request_get_bool( 'is_enabled', null );
 
 
 
 
 
 
 
5531
 
5532
- if ( ! is_bool( $is_enabled ) ) {
5533
- self::shoot_ajax_failure();
 
5534
  }
5535
 
5536
- $permission = fs_request_get( 'permission' );
 
 
 
 
 
 
 
 
 
 
 
5537
 
5538
- switch ( $permission ) {
5539
- case 'extensions':
5540
- $this->update_extensions_tracking_flag( $is_enabled );
5541
- break;
5542
- default:
5543
  $permission = 'no_match';
5544
- }
5545
 
5546
- if ( 'no_match' === $permission ) {
5547
- self::shoot_ajax_failure();
5548
  }
5549
 
5550
- self::shoot_ajax_success( array(
5551
- 'permissions' => array(
5552
- $permission => $is_enabled,
5553
- )
5554
- ) );
5555
- }
5556
-
5557
- /**
5558
- * @author Leo Fajardo (@leorw)
5559
- * @since 2.3.2
5560
- *
5561
- * @param bool|null $is_enabled
5562
- */
5563
- function update_extensions_tracking_flag( $is_enabled ) {
5564
- if ( is_bool( $is_enabled ) ) {
5565
- $this->_storage->store( 'is_extensions_tracking_allowed', $is_enabled );
5566
- }
5567
  }
5568
 
5569
  /**
@@ -5769,8 +5890,7 @@
5769
  if ( $this->is_activation_mode() ) {
5770
  if ( ! is_admin() ) {
5771
  /**
5772
- * If in activation mode, don't execute Freemius outside of the
5773
- * admin dashboard.
5774
  *
5775
  * @author Vova Feldman (@svovaf)
5776
  * @since 1.1.7.3
@@ -6317,7 +6437,25 @@
6317
  if ( ! isset( $this->_is_anonymous ) ) {
6318
  if ( $this->is_network_anonymous() ) {
6319
  $this->_is_anonymous = true;
6320
- } else if ( ! fs_is_network_admin() ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6321
  if ( ! isset( $this->_storage->is_anonymous ) ) {
6322
  // Not skipped.
6323
  $this->_is_anonymous = false;
@@ -6373,6 +6511,18 @@
6373
  return $this->_storage->get( 'is_pending_activation', false );
6374
  }
6375
 
 
 
 
 
 
 
 
 
 
 
 
 
6376
  /**
6377
  * Check if plugin must be WordPress.org compliant.
6378
  *
@@ -6449,9 +6599,11 @@
6449
  private function get_cron_blog_id( $name ) {
6450
  $this->_logger->entrance( $name );
6451
 
6452
- /**
6453
- * @var object $cron_data
6454
- */
 
 
6455
  $cron_data = $this->get_cron_data( $name );
6456
 
6457
  return ( is_object( $cron_data ) && is_numeric( $cron_data->blog_id ) ) ?
@@ -6562,18 +6714,23 @@
6562
  return 0;
6563
  }
6564
 
6565
- if ( $this->_is_network_active &&
6566
- is_numeric( $this->_storage->network_install_blog_id ) &&
6567
- $except_blog_id != $this->_storage->network_install_blog_id &&
6568
- self::is_site_active( $this->_storage->network_install_blog_id )
6569
- ) {
6570
- // Try to run cron from the main network blog.
6571
- $install = $this->get_install_by_blog_id( $this->_storage->network_install_blog_id );
6572
 
6573
- if ( is_object( $install ) &&
6574
- ( $this->is_premium() || $install->is_tracking_allowed() )
 
 
6575
  ) {
6576
- return $this->_storage->network_install_blog_id;
 
 
 
 
 
 
 
 
6577
  }
6578
  }
6579
 
@@ -6582,7 +6739,7 @@
6582
  foreach ( $installs as $blog_id => $install ) {
6583
  if ( $except_blog_id != $blog_id &&
6584
  self::is_site_active( $blog_id ) &&
6585
- ( $this->is_premium() || $install->is_tracking_allowed() )
6586
  ) {
6587
  return $blog_id;
6588
  }
@@ -6614,7 +6771,7 @@
6614
  /**
6615
  * @var FS_Site $install
6616
  */
6617
- if ( $install->is_tracking_allowed() ) {
6618
  $clear_cron = false;
6619
  break;
6620
  }
@@ -6625,14 +6782,7 @@
6625
  return;
6626
  }
6627
 
6628
- /**
6629
- * @var object $cron_data
6630
- */
6631
- $cron_data = $this->get_cron_data( $name );
6632
-
6633
- $cron_blog_id = is_object( $cron_data ) && isset( $cron_data->blog_id ) ?
6634
- $cron_data->blog_id :
6635
- 0;
6636
 
6637
  $this->clear_cron_data( $name );
6638
 
@@ -6669,14 +6819,7 @@
6669
  return false;
6670
  }
6671
 
6672
- /**
6673
- * @var object $cron_data
6674
- */
6675
- $cron_data = $this->get_cron_data( $name );
6676
-
6677
- $cron_blog_id = is_object( $cron_data ) && isset( $cron_data->blog_id ) ?
6678
- $cron_data->blog_id :
6679
- 0;
6680
 
6681
  if ( 0 < $cron_blog_id ) {
6682
  switch_to_blog( $cron_blog_id );
@@ -6787,7 +6930,7 @@
6787
  } else {
6788
  $installs = $this->get_blog_install_map();
6789
  foreach ( $installs as $blog_id => $install ) {
6790
- if ( $this->is_premium() || $install->is_tracking_allowed() ) {
6791
  if ( ! isset( $users_2_blog_ids[ $install->user_id ] ) ) {
6792
  $users_2_blog_ids[ $install->user_id ] = array();
6793
  }
@@ -6852,8 +6995,6 @@
6852
  * @since 1.1.7.3
6853
  */
6854
  private function run_manual_sync() {
6855
- self::require_pluggable_essentials();
6856
-
6857
  if ( ! $this->is_user_admin() ) {
6858
  return;
6859
  }
@@ -6937,6 +7078,24 @@
6937
  return $this->is_cron_on( 'sync' );
6938
  }
6939
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6940
  /**
6941
  * @author Vova Feldman (@svovaf)
6942
  * @since 1.1.7.3
@@ -7043,6 +7202,10 @@
7043
  * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding excluded specified blog ID from being the cron executor.
7044
  */
7045
  private function schedule_install_sync( $except_blog_id = 0 ) {
 
 
 
 
7046
  $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id );
7047
  }
7048
 
@@ -7131,13 +7294,20 @@
7131
  /**
7132
  * Show a notice that activation is currently pending.
7133
  *
 
 
7134
  * @author Vova Feldman (@svovaf)
7135
  * @since 1.0.7
7136
  *
7137
  * @param bool|string $email
7138
  * @param bool $is_pending_trial Since 1.2.1.5
 
7139
  */
7140
- function _add_pending_activation_notice( $email = false, $is_pending_trial = false ) {
 
 
 
 
7141
  if ( ! is_string( $email ) ) {
7142
  $current_user = self::_get_current_wp_user();
7143
  $email = $current_user->user_email;
@@ -7145,12 +7315,12 @@
7145
 
7146
  $this->_admin_notices->add_sticky(
7147
  sprintf(
7148
- $this->get_text_inline( 'You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s.', 'pending-activation-message' ),
7149
  '<b>' . $this->get_plugin_name() . '</b>',
7150
  '<b>' . $email . '</b>',
7151
  ( $is_pending_trial ?
7152
  $this->get_text_inline( 'start the trial', 'start-the-trial' ) :
7153
- $this->get_text_inline( 'complete the install', 'complete-the-install' ) )
7154
  ),
7155
  'activation_pending',
7156
  'Thanks!'
@@ -7193,6 +7363,20 @@
7193
  /**
7194
  * Don't redirect if activating multiple plugins at once (bulk activation).
7195
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7196
  } else if ( ! $is_migration ) {
7197
  $this->_redirect_on_activation_hook();
7198
  return;
@@ -7206,7 +7390,7 @@
7206
  if ( fs_request_is_action( $this->get_unique_affix() . '_skip_activation' ) ) {
7207
  check_admin_referer( $this->get_unique_affix() . '_skip_activation' );
7208
 
7209
- $this->skip_connection( null, fs_is_network_admin() );
7210
 
7211
  fs_redirect( $this->get_after_activation_url( 'after_skip_url' ) );
7212
  }
@@ -7379,8 +7563,6 @@
7379
 
7380
  fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.min.js' );
7381
  fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' );
7382
-
7383
- fs_enqueue_local_style( 'fs_connect', '/admin/connect.css' );
7384
  }
7385
 
7386
  /**
@@ -7416,14 +7598,14 @@
7416
  <?php
7417
  echo $this->apply_filters( 'optin_pointer_execute', "
7418
 
7419
- optin.pointer('open');
7420
 
7421
- // Tag the opt-in pointer with custom class.
7422
- $('.wp-pointer #fs_connect')
7423
- .parents('.wp-pointer.wp-pointer-top')
7424
- .addClass('fs-opt-in-pointer');
7425
 
7426
- ", 'element', 'optin' ) ?>
7427
  }
7428
  }
7429
  });
@@ -7439,7 +7621,7 @@
7439
  *
7440
  * @return string
7441
  */
7442
- function current_page_url() {
7443
  $url = 'http';
7444
 
7445
  if ( isset( $_SERVER["HTTPS"] ) ) {
@@ -7471,7 +7653,7 @@
7471
  }
7472
 
7473
  /* Events
7474
- ------------------------------------------------------------------------------------------------------------------*/
7475
  /**
7476
  * Delete site install from Database.
7477
  *
@@ -7637,10 +7819,19 @@
7637
  * @author Leo Fajardo (@leorw)
7638
  * @since 1.2.2
7639
  *
7640
- * @return string
 
 
 
 
 
 
 
 
 
 
7641
  */
7642
- private function can_activate_previous_theme() {
7643
- $slug = $this->get_previous_theme_slug();
7644
  if ( false !== $slug && current_user_can( 'switch_themes' ) ) {
7645
  $theme_instance = wp_get_theme( $slug );
7646
 
@@ -7737,6 +7928,10 @@
7737
  ( current_filter() !== ( 'activate_' . $this->_free_plugin_basename ) ) :
7738
  $this->is_premium();
7739
 
 
 
 
 
7740
  $this->_logger->info( 'Activating ' . ( $is_premium_version_activation ? 'premium' : 'free' ) . ' plugin version.' );
7741
 
7742
  if ( $this->is_plugin() ) {
@@ -7803,7 +7998,9 @@
7803
  $plugin_version = $this->_storage->is_anonymous_ms['version'];
7804
  $network = true;
7805
  } else {
7806
- $plugin_version = $this->_storage->is_anonymous['version'];
 
 
7807
  $network = false;
7808
  }
7809
 
@@ -7951,7 +8148,7 @@
7951
  );
7952
  } else {
7953
  // Activate the license.
7954
- $install = $this->get_api_site_scope()->call(
7955
  '/',
7956
  'put',
7957
  array( 'license_key' => $this->apply_filters( 'license_key', $license->secret_key ) )
@@ -8610,6 +8807,20 @@
8610
  $this->_is_anonymous = $is_anonymous;
8611
  }
8612
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8613
  /**
8614
  * @author Vova Feldman (@svovaf)
8615
  * @since 2.0.0
@@ -8626,9 +8837,17 @@
8626
  * @uses Freemius::is_network_anonymous() to check if the super-admin network skipped.
8627
  * @uses Freemius::is_network_delegated_connection() to check if the super-admin network delegated the connection to the site admins.
8628
  */
8629
- function _after_new_blog_callback( $blog_id, $user_id, $domain, $path, $network_id, $meta ) {
8630
  $this->_logger->entrance();
8631
 
 
 
 
 
 
 
 
 
8632
  if ( $this->is_premium() &&
8633
  $this->is_network_connected() &&
8634
  is_object( $this->_license ) &&
@@ -8662,9 +8881,13 @@
8662
  }
8663
  }
8664
 
 
 
8665
  $this->switch_to_blog( $current_blog_id );
8666
 
8667
- if ( is_object( $this->_site ) ) {
 
 
8668
  // Already connected (with or without a license), so no need to continue.
8669
  return;
8670
  }
@@ -8697,6 +8920,8 @@
8697
  false
8698
  );
8699
 
 
 
8700
  $this->switch_to_blog( $current_blog_id );
8701
  } else {
8702
  /**
@@ -8707,8 +8932,8 @@
8707
  $has_delegated_site = false;
8708
 
8709
  $sites = self::get_sites();
8710
- foreach ( $sites as $site ) {
8711
- $blog_id = self::get_site_blog_id( $site );
8712
 
8713
  if ( $this->is_site_delegated_connection( $blog_id ) ) {
8714
  $has_delegated_site = true;
@@ -8722,19 +8947,74 @@
8722
  $this->skip_site_connection( $blog_id );
8723
  }
8724
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8725
  }
8726
 
8727
  /**
8728
  * @author Vova Feldman (@svovaf)
8729
  * @since 1.1.3
8730
  *
8731
- * @param bool|int $network_or_blog_id Since 2.0.0.
8732
  */
8733
- private function reset_anonymous_mode( $network_or_blog_id = 0 ) {
8734
- if ( true === $network_or_blog_id ) {
8735
- unset( $this->_storage->is_anonymous_ms );
8736
- } else {
8737
- $this->_storage->remove( 'is_anonymous', true, $network_or_blog_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8738
  }
8739
 
8740
  /**
@@ -8745,11 +9025,7 @@
8745
  * @author Leo Fajardo (@leorw)
8746
  * @since 1.2.2
8747
  */
8748
- if ( ! $this->_is_network_active ||
8749
- 0 === $network_or_blog_id ||
8750
- get_current_blog_id() == $network_or_blog_id ||
8751
- ( true === $network_or_blog_id && fs_is_network_admin() )
8752
- ) {
8753
  $this->_is_anonymous = null;
8754
  }
8755
  }
@@ -8795,44 +9071,42 @@
8795
  * @author Vova Feldman (@svovaf)
8796
  * @since 1.1.1
8797
  *
8798
- * @param array|null $sites Since 2.0.0. Specific sites.
8799
- * @param bool $skip_all_network Since 2.0.0. If true, skip connection for all sites.
8800
  */
8801
- function skip_connection( $sites = null, $skip_all_network = false ) {
8802
  $this->_logger->entrance();
8803
 
8804
  $this->_admin_notices->remove_sticky( 'connect_account' );
8805
 
8806
- if ( $skip_all_network ) {
8807
  $this->set_anonymous_mode( true, true );
8808
- }
8809
 
8810
- if ( ! $skip_all_network && empty( $sites ) ) {
8811
- $this->skip_site_connection();
8812
- } else {
8813
- $uids = array();
8814
 
8815
- if ( $skip_all_network ) {
8816
- $this->set_anonymous_mode( true, true );
 
 
 
 
 
 
8817
 
8818
- $sites = self::get_sites();
8819
- foreach ( $sites as $site ) {
8820
- $blog_id = self::get_site_blog_id( $site );
8821
- $this->skip_site_connection( $blog_id, false );
8822
- $uids[] = $this->get_anonymous_id( $blog_id );
8823
- }
8824
- } else if ( ! empty( $sites ) ) {
8825
- foreach ( $sites as $site ) {
8826
- $uids[] = $site['uid'];
8827
- $this->skip_site_connection( $site['blog_id'], false );
8828
  }
8829
  }
 
8830
 
8831
- // Send anonymous skip event.
8832
- // No user identified info nor any tracking will be sent after the user skips the opt-in.
8833
- $this->get_api_plugin_scope()->call( 'skip.json', 'put', array(
8834
- 'uids' => $uids,
8835
- ) );
8836
  }
8837
 
8838
  $this->network_upgrade_mode_completed();
@@ -8847,18 +9121,12 @@
8847
  * @param int|null $blog_id
8848
  * @param bool $send_skip
8849
  */
8850
- private function skip_site_connection( $blog_id = null, $send_skip = true ) {
8851
  $this->_logger->entrance();
8852
 
8853
  $this->_admin_notices->remove_sticky( 'connect_account', $blog_id );
8854
 
8855
  $this->set_anonymous_mode( true, $blog_id );
8856
-
8857
- if ( $send_skip ) {
8858
- $this->get_api_plugin_scope()->call( 'skip.json', 'put', array(
8859
- 'uids' => array( $this->get_anonymous_id( $blog_id ) ),
8860
- ) );
8861
- }
8862
  }
8863
 
8864
  /**
@@ -9230,7 +9498,7 @@
9230
  * @param string[] $override
9231
  * @param bool $include_plugins Since 1.1.8 by default include plugin changes.
9232
  * @param bool $include_themes Since 1.1.8 by default include plugin changes.
9233
- * @param bool $include_blog_data Since 2.3.0 by default include the current blog's data (language, charset, title, and URL).
9234
  *
9235
  * @return array
9236
  */
@@ -9240,7 +9508,10 @@
9240
  $include_themes = true,
9241
  $include_blog_data = true
9242
  ) {
9243
- if ( $this->is_extensions_tracking_allowed() ) {
 
 
 
9244
  if ( ! defined( 'WP_FS__TRACK_PLUGINS' ) || false !== WP_FS__TRACK_PLUGINS ) {
9245
  /**
9246
  * @since 1.1.8 Also send plugin updates.
@@ -9268,21 +9539,23 @@
9268
 
9269
  $versions = $this->get_versions();
9270
 
9271
- $blog_data = $include_blog_data ?
9272
- array(
9273
- 'language' => get_bloginfo( 'language' ),
9274
- 'charset' => get_bloginfo( 'charset' ),
9275
- 'title' => get_bloginfo( 'name' ),
9276
- 'url' => get_site_url(),
9277
- ) :
9278
- array();
 
 
 
9279
 
9280
  return array_merge( $versions, $blog_data, array(
9281
  'version' => $this->get_plugin_version(),
9282
  'is_premium' => $this->is_premium(),
9283
  // Special params.
9284
  'is_active' => true,
9285
- 'is_disconnected' => $this->is_tracking_prohibited(),
9286
  'is_uninstalled' => false,
9287
  ), $override );
9288
  }
@@ -9297,6 +9570,7 @@
9297
  *
9298
  * @param string[] string $override
9299
  * @param bool $only_diff
 
9300
  * @param bool $include_plugins Since 1.1.8 by default include plugin changes.
9301
  * @param bool $include_themes Since 1.1.8 by default include plugin changes.
9302
  *
@@ -9305,6 +9579,7 @@
9305
  private function get_installs_data_for_api(
9306
  array $override,
9307
  $only_diff = false,
 
9308
  $include_plugins = true,
9309
  $include_themes = true
9310
  ) {
@@ -9342,6 +9617,10 @@
9342
 
9343
  $sites = self::get_sites();
9344
 
 
 
 
 
9345
  foreach ( $sites as $site ) {
9346
  $blog_id = self::get_site_blog_id( $site );
9347
 
@@ -9353,19 +9632,64 @@
9353
  continue;
9354
  }
9355
 
9356
- if ( ! $this->is_premium() && $install->is_tracking_prohibited() ) {
9357
  // Don't send updates regarding opted-out installs.
9358
  continue;
9359
  }
9360
 
9361
- $install_data = $this->get_site_info( $site );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9362
 
9363
- $uid = $install_data['uid'];
 
 
 
 
 
9364
 
9365
  unset( $install_data['blog_id'] );
9366
  unset( $install_data['uid'] );
 
 
9367
 
9368
- $install_data['is_disconnected'] = $install->is_disconnected;
9369
  $install_data['is_active'] = $this->is_active_for_site( $blog_id );
9370
  $install_data['is_uninstalled'] = $install->is_uninstalled;
9371
 
@@ -9388,18 +9712,26 @@
9388
  $is_common_diff_for_any_site = $is_common_diff_for_any_site || $is_common_diff;
9389
  }
9390
 
9391
- if ( ! empty( $install_data ) || $is_common_diff ) {
9392
  // Add install ID and site unique ID.
9393
  $install_data['id'] = $install->id;
9394
  $install_data['uid'] = $uid;
 
9395
 
9396
- $installs_data[] = $install_data;
 
 
9397
  }
9398
  }
9399
  }
9400
 
9401
  restore_current_blog();
9402
 
 
 
 
 
 
9403
  if ( 0 < count( $installs_data ) && ( $is_common_diff_for_any_site || ! $only_diff ) ) {
9404
  if ( ! $only_diff ) {
9405
  $installs_data[] = $common;
@@ -9437,8 +9769,12 @@
9437
  if ( ( is_bool( $install->{$p} ) || ! empty( $install->{$p} ) ) &&
9438
  $install->{$p} != $v
9439
  ) {
9440
- $install->{$p} = $v;
9441
- $diff[ $p ] = $v;
 
 
 
 
9442
  }
9443
  } else {
9444
  $special[ $p ] = $v;
@@ -9463,6 +9799,78 @@
9463
  return $diff;
9464
  }
9465
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9466
  /**
9467
  * Update install only if changed.
9468
  *
@@ -9471,10 +9879,11 @@
9471
  *
9472
  * @param string[] string $override
9473
  * @param bool $flush
 
9474
  *
9475
  * @return false|object|string
9476
  */
9477
- private function send_install_update( $override = array(), $flush = false ) {
9478
  $this->_logger->entrance();
9479
 
9480
  $check_properties = $this->get_install_data_for_api( $override );
@@ -9485,7 +9894,6 @@
9485
  $params = $this->get_install_diff_for_api( $check_properties, $this->_site, $override );
9486
  }
9487
 
9488
- $keepalive_only_update = false;
9489
  if ( empty( $params ) ) {
9490
  $keepalive_only_update = $this->should_send_keepalive_update();
9491
 
@@ -9500,10 +9908,9 @@
9500
  }
9501
  }
9502
 
9503
- if ( ! $keepalive_only_update ) {
9504
  /**
9505
- * Do not update the last install sync timestamp after a keepalive-only call since there were no actual
9506
- * updates sent.
9507
  *
9508
  * @author Leo Fajardo (@leorw)
9509
  * @since 2.2.3
@@ -9519,11 +9926,11 @@
9519
  $this->set_keepalive_timestamp();
9520
 
9521
  // Send updated values to FS.
9522
- $site = $this->get_api_site_scope()->call( '/', 'put', $params );
9523
 
9524
- if ( ! $keepalive_only_update && $this->is_api_result_entity( $site ) ) {
9525
  /**
9526
- * Do not clear scheduled sync after a keepalive-only call since there were no actual updates sent.
9527
  *
9528
  * @author Leo Fajardo (@leorw)
9529
  * @since 2.2.3
@@ -9545,37 +9952,29 @@
9545
  *
9546
  * @param string[] string $override
9547
  * @param bool $flush
 
9548
  *
9549
  * @return false|object|string
9550
  */
9551
- private function send_installs_update( $override = array(), $flush = false ) {
9552
  $this->_logger->entrance();
9553
 
9554
- $installs_data = $this->get_installs_data_for_api( $override, ! $flush );
 
 
 
 
 
 
9555
 
9556
- $keepalive_only_update = false;
9557
- if ( empty( $installs_data ) ) {
9558
- /**
9559
- * Pass `true` to use the network level storage since the update is for many installs.
9560
- *
9561
- * @author Leo Fajardo (@leorw)
9562
- * @since 2.2.3
9563
- */
9564
- $keepalive_only_update = $this->should_send_keepalive_update( true );
9565
 
9566
- if ( ! $keepalive_only_update ) {
9567
- /**
9568
- * There are no updates to send including keepalive.
9569
- *
9570
- * @author Leo Fajardo (@leorw)
9571
- * @since 2.2.3
9572
- */
9573
- return false;
9574
- }
9575
  }
9576
 
9577
- if ( ! $keepalive_only_update ) {
9578
- // Update last install sync timestamp if there were actual updates sent (i.e., not a keepalive-only call).
9579
  $this->set_cron_execution_timestamp( 'install_sync' );
9580
  }
9581
 
@@ -9590,8 +9989,8 @@
9590
  // Send updated values to FS.
9591
  $result = $this->get_api_user_scope()->call( "/plugins/{$this->_plugin->id}/installs.json", 'put', $installs_data );
9592
 
9593
- if ( ! $keepalive_only_update && $this->is_api_result_object( $result, 'installs' ) ) {
9594
- // I successfully sent installs update (there was an actual update sent and it's not just a keepalive-only call), clear scheduled sync if exist.
9595
  $this->clear_install_sync_cron();
9596
  }
9597
 
@@ -9641,10 +10040,10 @@
9641
  * @param string[] string $override
9642
  * @param bool $flush
9643
  */
9644
- private function sync_install( $override = array(), $flush = false ) {
9645
  $this->_logger->entrance();
9646
 
9647
- $site = $this->send_install_update( $override, $flush );
9648
 
9649
  if ( false === $site ) {
9650
  // No sync required.
@@ -9673,7 +10072,7 @@
9673
  private function sync_installs( $override = array(), $flush = false ) {
9674
  $this->_logger->entrance();
9675
 
9676
- $result = $this->send_installs_update( $override, $flush );
9677
 
9678
  if ( false === $result ) {
9679
  // No sync required.
@@ -9828,8 +10227,8 @@
9828
  // Send uninstall event.
9829
  $this->send_installs_update( $params );
9830
  } else {
9831
- // Send uninstall event.
9832
- $this->send_install_update( $params );
9833
  }
9834
  }
9835
 
@@ -9926,7 +10325,17 @@
9926
  return;
9927
  }
9928
 
9929
- $fs->_uninstall_plugin_event();
 
 
 
 
 
 
 
 
 
 
9930
 
9931
  $fs->do_action( 'after_uninstall' );
9932
  }
@@ -10037,7 +10446,7 @@
10037
  * @return string
10038
  */
10039
  function get_premium_slug() {
10040
- return is_object( $this->_plugin ) ?
10041
  $this->_plugin->premium_slug :
10042
  "{$this->_slug}-premium";
10043
  }
@@ -10090,6 +10499,28 @@
10090
  null;
10091
  }
10092
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10093
  /**
10094
  * @author Vova Feldman (@svovaf)
10095
  * @since 1.2.1.5
@@ -10134,7 +10565,7 @@
10134
  function get_eula_url() {
10135
  return $this->apply_filters(
10136
  'eula_url',
10137
- "https://freemius.com/terms/{$this->_plugin->id}/{$this->_slug}/"
10138
  );
10139
  }
10140
 
@@ -10322,7 +10753,7 @@
10322
  #endregion ------------------------------------------------------------------
10323
 
10324
  /* Account
10325
- ------------------------------------------------------------------------------------------------------------------*/
10326
 
10327
  /**
10328
  * Find plugin's slug by plugin's basename.
@@ -10386,9 +10817,14 @@
10386
  */
10387
  private static function get_all_sites(
10388
  $module_type = WP_FS__MODULE_TYPE_PLUGIN,
10389
- $blog_id = null
 
10390
  ) {
10391
- $sites = self::get_account_option( 'sites', $module_type, $blog_id );
 
 
 
 
10392
 
10393
  if ( ! is_array( $sites ) ) {
10394
  $sites = array();
@@ -10748,10 +11184,20 @@
10748
  *
10749
  * @author Vova Feldman (@svovaf)
10750
  * @since 1.0.1
 
 
 
10751
  * @return bool
10752
  */
10753
- function is_registered() {
10754
- return is_object( $this->_user );
 
 
 
 
 
 
 
10755
  }
10756
 
10757
  /**
@@ -10762,8 +11208,34 @@
10762
  *
10763
  * @return bool
10764
  */
10765
- function is_tracking_allowed() {
10766
- return ( is_object( $this->_site ) && $this->_site->is_tracking_allowed() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10767
  }
10768
 
10769
  /**
@@ -10799,13 +11271,59 @@
10799
  }
10800
 
10801
  /**
10802
- * @author Vova Feldman (@svovaf)
10803
- * @since 1.0.3
10804
- *
10805
- * @return FS_Site
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10806
  */
10807
- function get_site() {
10808
- return $this->_site;
 
 
 
 
 
 
10809
  }
10810
 
10811
  /**
@@ -12089,7 +12607,7 @@
12089
  } else {
12090
  $url = is_object( $site ) ?
12091
  $site->siteurl :
12092
- get_site_url( $blog_id );
12093
 
12094
  $disconnected_site_ids[] = $blog_id;
12095
  }
@@ -12462,7 +12980,21 @@
12462
  } else if ( $is_whitelabeled_flag ) {
12463
  $is_whitelabeled = true;
12464
  } else {
12465
- $addon_ids = $this->get_updated_account_addons();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12466
  $installed_addons = $this->get_installed_addons();
12467
  foreach ( $installed_addons as $fs_addon ) {
12468
  $addon_ids[] = $fs_addon->get_id();
@@ -12891,6 +13423,75 @@
12891
  fs_require_template( 'forms/resend-key.php', $vars );
12892
  }
12893
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12894
  /**
12895
  * Returns a collection of IDs of installs that are associated with the context product and its add-ons, and activated with foreign licenses.
12896
  *
@@ -13098,10 +13699,15 @@
13098
  ( $is_network_admin && $this->is_network_active() && ! $this->is_network_delegated_connection() ) ||
13099
  ( ! $is_network_admin && ( ! $this->is_network_active() || $this->is_delegated_connection() ) )
13100
  ) {
13101
- /**
13102
- * @since 1.2.0 Add license action link only on plugins page.
13103
- */
13104
- $this->_add_license_action_link();
 
 
 
 
 
13105
  }
13106
  }
13107
 
@@ -13273,7 +13879,7 @@
13273
  self::shoot_ajax_failure();
13274
  }
13275
 
13276
- $site = $this->get_api_site_scope()->call(
13277
  '',
13278
  'put',
13279
  array(
@@ -13326,7 +13932,8 @@
13326
  fs_request_get( 'blog_id', null ),
13327
  fs_request_get( 'module_id', null, 'post' ),
13328
  fs_request_get( 'user_id', null ),
13329
- fs_request_get_bool( 'is_extensions_tracking_allowed', null )
 
13330
  );
13331
 
13332
  if (
@@ -13571,6 +14178,9 @@
13571
  * @param null|int $blog_id
13572
  * @param null|number $plugin_id
13573
  * @param null|number $license_owner_id
 
 
 
13574
  *
13575
  * @return array {
13576
  * @var bool $success
@@ -13585,7 +14195,8 @@
13585
  $blog_id = null,
13586
  $plugin_id = null,
13587
  $license_owner_id = null,
13588
- $is_extensions_tracking_allowed = null
 
13589
  ) {
13590
  $this->_logger->entrance();
13591
 
@@ -13605,7 +14216,10 @@
13605
  $this :
13606
  $this->get_addon_instance( $plugin_id );
13607
 
13608
- $this->update_extensions_tracking_flag( $is_extensions_tracking_allowed );
 
 
 
13609
 
13610
  $error = false;
13611
  $next_page = false;
@@ -13770,8 +14384,8 @@
13770
  }
13771
  }
13772
 
13773
- $all_sites = self::get_sites();
13774
- $pending_sites = array();
13775
 
13776
  /**
13777
  * Check if there are any sites that are not connected, skipped, nor delegated. For every site that falls into that category, if the product is freemium, skip the connection. If the product is premium only, delegate the connection to the site administrator.
@@ -13801,14 +14415,14 @@
13801
  continue;
13802
  }
13803
 
13804
- $pending_sites[] = self::get_site_info( $site );
13805
  }
13806
 
13807
- if ( ! empty( $pending_sites ) ) {
13808
  if ( $fs->is_freemium() && $fs->is_enable_anonymous() ) {
13809
- $fs->skip_connection( $pending_sites );
13810
  } else {
13811
- $fs->delegate_connection( $pending_sites );
13812
  }
13813
  }
13814
  }
@@ -13886,7 +14500,7 @@
13886
 
13887
  $addon_info = $fs->_get_addon_info( $addon_id, $is_installed );
13888
 
13889
- if ( ! $addon_info['is_connected'] ) {
13890
  // Add-on is not associated with an install entity.
13891
  continue;
13892
  }
@@ -13944,11 +14558,11 @@
13944
  $this->delegate_connection();
13945
  } else {
13946
  if ( ! empty( $sites_by_action['delegate'] ) ) {
13947
- $this->delegate_connection( $sites_by_action['delegate'] );
13948
  }
13949
 
13950
  if ( ! empty( $sites_by_action['skip'] ) ) {
13951
- $this->skip_connection( $sites_by_action['skip'] );
13952
  }
13953
 
13954
  if ( empty( $sites_by_action['allow'] ) ) {
@@ -14266,15 +14880,37 @@
14266
  return $this->_plugin->has_affiliate_program();
14267
  }
14268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14269
  /**
14270
  * @author Leo Fajardo (@leorw)
14271
  * @since 1.2.4
14272
  */
14273
  private function fetch_affiliate_terms() {
14274
  if ( ! is_object( $this->plugin_affiliate_terms ) ) {
14275
- $plugins_api = $this->get_api_plugin_scope();
 
 
 
 
 
 
14276
  $affiliate_terms = $plugins_api->get( '/aff.json?type=affiliation', false );
14277
 
 
 
 
14278
  if ( ! $this->is_api_result_entity( $affiliate_terms ) ) {
14279
  return;
14280
  }
@@ -14292,8 +14928,10 @@
14292
  $application_data = $this->_storage->affiliate_application_data;
14293
  $flush = ( ! isset( $application_data['status'] ) || 'pending' === $application_data['status'] );
14294
 
 
 
14295
  $users_api = $this->get_api_user_scope();
14296
- $result = $users_api->get( "/plugins/{$this->_plugin->id}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json", $flush );
14297
  if ( $this->is_api_result_object( $result, 'affiliates' ) ) {
14298
  if ( ! empty( $result->affiliates ) ) {
14299
  $affiliate = new FS_Affiliate( $result->affiliates[0] );
@@ -14393,15 +15031,17 @@
14393
  var_export( $next_page, true )
14394
  );
14395
  } else if ( $this->is_pending_activation() ) {
14396
- self::shoot_ajax_failure( $this->get_text_inline( 'Account is pending activation.', 'account-is-pending-activation' ) );
14397
  }
14398
  }
14399
 
14400
  $this->fetch_affiliate_terms();
14401
 
 
 
14402
  $api = $this->get_api_user_scope();
14403
  $result = $api->call(
14404
- ( "/plugins/{$this->_plugin->id}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json" ),
14405
  'post',
14406
  $affiliate
14407
  );
@@ -14863,6 +15503,16 @@
14863
  return ( defined( 'DOING_CRON' ) && DOING_CRON );
14864
  }
14865
 
 
 
 
 
 
 
 
 
 
 
14866
  /**
14867
  * Check if a real user is visiting the admin dashboard.
14868
  *
@@ -14876,7 +15526,7 @@
14876
  is_admin() &&
14877
  ! self::is_ajax() &&
14878
  ! self::is_cron() &&
14879
- ( 'admin-post.php' !== self::get_current_page() )
14880
  );
14881
  }
14882
 
@@ -15028,20 +15678,20 @@
15028
  * @author Leo Fajardo (@leorw)
15029
  * @since 2.0.0
15030
  *
15031
- * @param array|null $sites
15032
  */
15033
- private function delegate_connection( $sites = null ) {
15034
  $this->_logger->entrance();
15035
 
15036
  $this->_admin_notices->remove_sticky( 'connect_account' );
15037
 
15038
- if ( is_null( $sites ) ) {
15039
  // All sites delegation.
15040
- $this->_storage->store( 'is_delegated_connection', true, true, true );
15041
  } else {
15042
  // Specified sites delegation.
15043
- foreach ( $sites as $site ) {
15044
- $this->delegate_site_connection( $site['blog_id'] );
15045
  }
15046
  }
15047
 
@@ -15057,7 +15707,7 @@
15057
  * @param int $blog_id
15058
  */
15059
  private function delegate_site_connection( $blog_id ) {
15060
- $this->_storage->store( 'is_delegated_connection', true, $blog_id, true );
15061
  }
15062
 
15063
  /**
@@ -15097,7 +15747,7 @@
15097
  }
15098
 
15099
  /**
15100
- * Check if delegated the connection. When running within the the network admin,
15101
  * and haven't specified the blog ID, checks if network level delegated. If running
15102
  * within a site admin or specified a blog ID, check if delegated the connection for
15103
  * the current context site.
@@ -15157,12 +15807,17 @@
15157
  }
15158
 
15159
  /**
 
 
15160
  * @author Leo Fajardo (@leorw)
15161
  * @since 2.0.0
15162
  *
 
 
 
15163
  * @return array Active & public sites collection.
15164
  */
15165
- static function get_sites() {
15166
  if ( ! is_multisite() ) {
15167
  return array();
15168
  }
@@ -15184,27 +15839,11 @@
15184
  'mature' => 0,
15185
  'spam' => 0,
15186
  'deleted' => 0,
 
 
15187
  );
15188
 
15189
- if ( function_exists( 'get_sites' ) ) {
15190
- // For WP 4.6 and above.
15191
- return get_sites( $args );
15192
- } else if ( function_exists( 'wp_' . 'get_sites' ) ) {
15193
- // For WP 3.7 to WP 4.5.
15194
- /**
15195
- * This is a hack suggested previously proposed by the TRT. Our SDK is compliant with older WP versions and we'd like to keep it that way.
15196
- *
15197
- * @todo Remove this hack once this false-positive error is removed from the Theme Sniffer.
15198
- *
15199
- * @since 2.3.3
15200
- * @author Vova Feldman (@svovaf)
15201
- */
15202
- $fn = 'wp_' . 'get_sites';
15203
- return $fn( $args );
15204
- } else {
15205
- // For WP 3.6 and below.
15206
- return get_blog_list( 0, 'all' );
15207
- }
15208
  }
15209
 
15210
  /**
@@ -15253,7 +15892,7 @@
15253
  $address_to_blog_map = array();
15254
  foreach ( $sites as $site ) {
15255
  $blog_id = self::get_site_blog_id( $site );
15256
- $address = trailingslashit( fs_strip_url_protocol( get_site_url( $blog_id ) ) );
15257
  $address_to_blog_map[ $address ] = $blog_id;
15258
  }
15259
 
@@ -15289,6 +15928,42 @@
15289
  return $install_map;
15290
  }
15291
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15292
  /**
15293
  * Gets a map of module IDs that the given user has opted-in to.
15294
  *
@@ -15373,11 +16048,16 @@
15373
  *
15374
  * @param int $blog_id
15375
  * @param FS_Site $install
 
15376
  *
15377
  * @return bool Since 2.3.1 returns if a switch was made.
15378
  */
15379
- function switch_to_blog( $blog_id, FS_Site $install = null ) {
15380
- if ( ! is_numeric( $blog_id ) || $blog_id == $this->_context_is_network_or_blog_id ) {
 
 
 
 
15381
  return false;
15382
  }
15383
 
@@ -15441,7 +16121,7 @@
15441
  unset( $this->_site_api );
15442
  unset( $this->_user_api );
15443
 
15444
- return false;
15445
  }
15446
 
15447
  /**
@@ -15470,21 +16150,41 @@
15470
  $site['blog_id'] );
15471
  }
15472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15473
  /**
15474
  * @author Leo Fajardo (@leorw)
15475
  * @since 2.0.0
15476
  *
15477
  * @param array|WP_Site|null $site
 
15478
  *
15479
  * @return array
15480
  */
15481
- function get_site_info( $site = null ) {
15482
  $this->_logger->entrance();
15483
 
15484
  $switched = false;
15485
 
 
 
15486
  if ( is_null( $site ) ) {
15487
- $url = get_site_url();
15488
  $name = get_bloginfo( 'name' );
15489
  $blog_id = null;
15490
  } else {
@@ -15496,26 +16196,44 @@
15496
  }
15497
 
15498
  if ( $site instanceof WP_Site ) {
15499
- $url = $site->siteurl;
15500
- $name = $site->blogname;
 
15501
  } else {
15502
- $url = get_site_url( $blog_id );
15503
  $name = get_bloginfo( 'name' );
15504
  }
15505
  }
15506
 
 
 
 
 
 
 
 
 
15507
  $info = array(
15508
- 'uid' => $this->get_anonymous_id( $blog_id ),
15509
- 'url' => $url,
15510
- 'title' => $name,
15511
- 'language' => get_bloginfo( 'language' ),
15512
- 'charset' => get_bloginfo( 'charset' ),
15513
  );
15514
 
 
 
 
 
 
 
 
 
15515
  if ( is_numeric( $blog_id ) ) {
15516
  $info['blog_id'] = $blog_id;
15517
  }
15518
 
 
 
 
 
15519
  if ( $switched ) {
15520
  restore_current_blog();
15521
  }
@@ -15738,6 +16456,10 @@
15738
  }
15739
  }
15740
 
 
 
 
 
15741
  if ( $this->is_sync_cron_scheduled() &&
15742
  $context_blog_id == $this->get_sync_cron_blog_id()
15743
  ) {
@@ -15771,6 +16493,10 @@
15771
 
15772
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
15773
 
 
 
 
 
15774
  $current_blog_id = get_current_blog_id();
15775
 
15776
  $this->switch_to_blog( $context_blog_id );
@@ -15804,6 +16530,10 @@
15804
 
15805
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
15806
 
 
 
 
 
15807
  $current_blog_id = get_current_blog_id();
15808
 
15809
  $this->switch_to_blog( $context_blog_id );
@@ -15821,6 +16551,20 @@
15821
  $this->switch_to_blog( $current_blog_id );
15822
  }
15823
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15824
  /**
15825
  * Executed after site re-activation.
15826
  *
@@ -15935,9 +16679,17 @@
15935
  * @return bool
15936
  */
15937
  function is_product_settings_page() {
 
 
 
 
 
 
 
15938
  return fs_starts_with(
15939
- fs_request_get( 'page', '', 'get' ),
15940
- $this->_menu->get_slug()
 
15941
  );
15942
  }
15943
 
@@ -16017,10 +16769,11 @@
16017
  *
16018
  * @param bool|string $topic
16019
  * @param bool|string $message
 
16020
  *
16021
  * @return string
16022
  */
16023
- function contact_url( $topic = false, $message = false ) {
16024
  $params = array();
16025
  if ( is_string( $topic ) ) {
16026
  $params['topic'] = $topic;
@@ -16029,6 +16782,10 @@
16029
  $params['message'] = $message;
16030
  }
16031
 
 
 
 
 
16032
  if ( $this->is_addon() ) {
16033
  $params['addon_id'] = $this->get_id();
16034
 
@@ -16067,7 +16824,7 @@
16067
  }
16068
 
16069
  /* Logger
16070
- ------------------------------------------------------------------------------------------------------------------*/
16071
  /**
16072
  * @param string $id
16073
  * @param bool $prefix_slug
@@ -16092,7 +16849,7 @@
16092
  }
16093
 
16094
  /* Security
16095
- ------------------------------------------------------------------------------------------------------------------*/
16096
  private static function _encrypt( $str ) {
16097
  if ( is_null( $str ) ) {
16098
  return null;
@@ -16312,20 +17069,6 @@
16312
  ) {
16313
  // Load site.
16314
  $this->_site = $site;
16315
-
16316
- // Load plans.
16317
- $this->_plans = $plans[ $this->_slug ];
16318
- if ( ! is_array( $this->_plans ) || empty( $this->_plans ) ) {
16319
- $this->_sync_plans();
16320
- } else {
16321
- for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
16322
- if ( $this->_plans[ $i ] instanceof FS_Plugin_Plan ) {
16323
- $this->_plans[ $i ] = self::decrypt_entity( $this->_plans[ $i ] );
16324
- } else {
16325
- unset( $this->_plans[ $i ] );
16326
- }
16327
- }
16328
- }
16329
  }
16330
 
16331
  $user = null;
@@ -16354,7 +17097,30 @@
16354
  /**
16355
  * This is a special fault tolerance mechanism to handle a scenario that the user data is missing.
16356
  */
16357
- $user = $this->sync_user_by_current_install();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16358
  }
16359
 
16360
  $this->_user = ( $user instanceof FS_User ) ?
@@ -16368,6 +17134,23 @@
16368
  }
16369
 
16370
  if ( is_object( $this->_site ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16371
  $this->_license = $this->_get_license_by_id( $this->_site->license_id );
16372
 
16373
  if ( $this->_site->version != $this->get_plugin_version() ) {
@@ -16386,6 +17169,15 @@
16386
  if ( $this->is_theme() ) {
16387
  $this->_register_account_hooks();
16388
  }
 
 
 
 
 
 
 
 
 
16389
  }
16390
 
16391
  /**
@@ -16465,17 +17257,129 @@
16465
  */
16466
  private function get_versions() {
16467
  $versions = array();
16468
- $versions['platform_version'] = get_bloginfo( 'version' );
16469
- $versions['sdk_version'] = $this->version;
16470
- $versions['programming_language_version'] = phpversion();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16471
 
16472
- foreach ( $versions as $k => $version ) {
16473
- if ( is_string( $versions[ $k ] ) && ! empty( $versions[ $k ] ) ) {
16474
- $versions[ $k ] = substr( $versions[ $k ], 0, 16 );
16475
- }
16476
  }
16477
 
16478
- return $versions;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16479
  }
16480
 
16481
  /**
@@ -16530,23 +17434,22 @@
16530
  $versions = $this->get_versions();
16531
 
16532
  $params = array_merge( $versions, array(
16533
- 'user_firstname' => $current_user->user_firstname,
16534
- 'user_lastname' => $current_user->user_lastname,
16535
- 'user_nickname' => $current_user->user_nicename,
16536
- 'user_email' => $current_user->user_email,
16537
- 'user_ip' => WP_FS__REMOTE_ADDR,
16538
- 'plugin_slug' => $this->_slug,
16539
- 'plugin_id' => $this->get_id(),
16540
- 'plugin_public_key' => $this->get_public_key(),
16541
- 'plugin_version' => $this->get_plugin_version(),
16542
- 'return_url' => fs_nonce_url( $return_url, $activation_action ),
16543
- 'account_url' => fs_nonce_url( $this->_get_admin_page_url(
16544
  'account',
16545
  array( 'fs_action' => 'sync_user' )
16546
  ), 'sync_user' ),
16547
- 'is_premium' => $this->is_premium(),
16548
- 'is_active' => true,
16549
- 'is_uninstalled' => false,
 
16550
  ) );
16551
 
16552
  if ( $this->is_addon() ) {
@@ -16567,12 +17470,17 @@
16567
 
16568
  $site = $this->get_site_info( $site );
16569
 
16570
- $params = array_merge( $params, array(
 
 
 
 
 
 
 
 
16571
  'site_uid' => $site['uid'],
16572
  'site_url' => $site['url'],
16573
- 'site_name' => $site['title'],
16574
- 'language' => $site['language'],
16575
- 'charset' => $site['charset'],
16576
  ) );
16577
  }
16578
 
@@ -16597,6 +17505,10 @@
16597
  );
16598
  }
16599
 
 
 
 
 
16600
  return array_merge( $params, $override_with );
16601
  }
16602
 
@@ -16615,9 +17527,10 @@
16615
  * In this case, the user and site info will be sent to the server but no
16616
  * data will be saved to the WP installation's database.
16617
  * @param number|bool $trial_plan_id
16618
- * @param bool $is_disconnected Whether or not to opt in without tracking.
16619
  * @param null|bool $is_marketing_allowed
16620
  * @param array $sites If network-level opt-in, an array of containing details of sites.
 
16621
  *
16622
  * @return string|object
16623
  * @use WP_Error
@@ -16631,7 +17544,8 @@
16631
  $trial_plan_id = false,
16632
  $is_disconnected = false,
16633
  $is_marketing_allowed = null,
16634
- $sites = array()
 
16635
  ) {
16636
  $this->_logger->entrance();
16637
 
@@ -16655,7 +17569,7 @@
16655
  $fs_user,
16656
  false,
16657
  $trial_plan_id,
16658
- true,
16659
  true,
16660
  $sites
16661
  );
@@ -16723,13 +17637,15 @@
16723
  $params['is_marketing_allowed'] = $is_marketing_allowed;
16724
  }
16725
 
16726
- $params['is_disconnected'] = $is_disconnected;
16727
- $params['format'] = 'json';
 
 
16728
 
16729
  $request = array(
16730
  'method' => 'POST',
16731
  'body' => $params,
16732
- 'timeout' => WP_FS__DEBUG_SDK ? 60 : 30,
16733
  );
16734
 
16735
  $url = $this->add_show_pending( WP_FS__ADDRESS . '/action/service/user/install/' );
@@ -16815,7 +17731,8 @@
16815
  true ),
16816
  false,
16817
  $filtered_license_key,
16818
- ! empty( $params['trial_plan_id'] )
 
16819
  );
16820
  } else if ( isset( $decoded->install_secret_key ) ) {
16821
  return $this->install_with_new_user(
@@ -16828,6 +17745,9 @@
16828
  ( isset( $decoded->is_extensions_tracking_allowed ) && ! is_null( $decoded->is_extensions_tracking_allowed ) ?
16829
  $decoded->is_extensions_tracking_allowed :
16830
  null ),
 
 
 
16831
  $decoded->install_id,
16832
  $decoded->install_public_key,
16833
  $decoded->install_secret_key,
@@ -16844,6 +17764,9 @@
16844
  ( isset( $decoded->is_extensions_tracking_allowed ) && ! is_null( $decoded->is_extensions_tracking_allowed ) ?
16845
  $decoded->is_extensions_tracking_allowed :
16846
  null ),
 
 
 
16847
  $decoded->installs,
16848
  false
16849
  );
@@ -16941,15 +17864,11 @@
16941
  $this->_admin_notices->remove_sticky( 'connect_account' );
16942
 
16943
  if ( $this->is_pending_activation() || ! $this->has_settings_menu() ) {
16944
- // Remove pending activation sticky notice (if still exist).
16945
- $this->_admin_notices->remove_sticky( 'activation_pending' );
16946
-
16947
- // Remove plugin from pending activation mode.
16948
- unset( $this->_storage->is_pending_activation );
16949
 
16950
  if ( ! $this->is_paying_or_trial() ) {
16951
  $this->_admin_notices->add_sticky(
16952
- sprintf( $this->get_text_inline( '%s activation was successfully completed.', 'plugin-x-activation-message' ), '<b>' . $this->get_plugin_name() . '</b>' ),
16953
  'activation_complete'
16954
  );
16955
  }
@@ -17071,6 +17990,7 @@
17071
  fs_request_get( 'user_secret_key' ),
17072
  fs_request_get_bool( 'is_marketing_allowed', null ),
17073
  fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
 
17074
  $pending_sites_info['blog_ids'],
17075
  $pending_sites_info['license_key'],
17076
  $pending_sites_info['trial_plan_id']
@@ -17082,6 +18002,7 @@
17082
  fs_request_get( 'user_secret_key' ),
17083
  fs_request_get_bool( 'is_marketing_allowed', null ),
17084
  fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
 
17085
  fs_request_get( 'install_id' ),
17086
  fs_request_get( 'install_public_key' ),
17087
  fs_request_get( 'install_secret_key' ),
@@ -17090,7 +18011,13 @@
17090
  );
17091
  }
17092
  } else if ( fs_request_has( 'pending_activation' ) ) {
17093
- $this->set_pending_confirmation( fs_request_get( 'user_email' ), true );
 
 
 
 
 
 
17094
  }
17095
  }
17096
  }
@@ -17138,6 +18065,7 @@
17138
  * @param string $user_secret_key
17139
  * @param bool|null $is_marketing_allowed
17140
  * @param bool|null $is_extensions_tracking_allowed Since 2.3.2
 
17141
  * @param number $install_id
17142
  * @param string $install_public_key
17143
  * @param string $install_secret_key
@@ -17152,6 +18080,7 @@
17152
  $user_secret_key,
17153
  $is_marketing_allowed,
17154
  $is_extensions_tracking_allowed,
 
17155
  $install_id,
17156
  $install_public_key,
17157
  $install_secret_key,
@@ -17185,7 +18114,7 @@
17185
  $site->secret_key = $install_secret_key;
17186
 
17187
  $this->_site = $site;
17188
- $site_result = $this->get_api_site_scope()->get();
17189
  $site = new FS_Site( $site_result );
17190
  $this->_site = $site;
17191
 
@@ -17193,7 +18122,10 @@
17193
  $this->disable_opt_in_notice_and_lock_user();
17194
  }
17195
 
17196
- $this->update_extensions_tracking_flag( $is_extensions_tracking_allowed );
 
 
 
17197
 
17198
  return $this->setup_account(
17199
  $this->_user,
@@ -17214,6 +18146,7 @@
17214
  * @param string $user_secret_key
17215
  * @param bool|null $is_marketing_allowed
17216
  * @param bool|null $is_extensions_tracking_allowed Since 2.3.2
 
17217
  * @param array $site_ids
17218
  * @param bool $license_key
17219
  * @param bool $trial_plan_id
@@ -17227,6 +18160,7 @@
17227
  $user_secret_key,
17228
  $is_marketing_allowed,
17229
  $is_extensions_tracking_allowed,
 
17230
  $site_ids,
17231
  $license_key = false,
17232
  $trial_plan_id = false,
@@ -17238,7 +18172,10 @@
17238
  $this->disable_opt_in_notice_and_lock_user();
17239
  }
17240
 
17241
- $this->update_extensions_tracking_flag( $is_extensions_tracking_allowed );
 
 
 
17242
 
17243
  $sites = array();
17244
  foreach ( $site_ids as $site_id ) {
@@ -17259,6 +18196,7 @@
17259
  * @param string $user_secret_key
17260
  * @param bool|null $is_marketing_allowed
17261
  * @param bool|null $is_extensions_tracking_allowed Since 2.3.2
 
17262
  * @param object[] $installs
17263
  * @param bool $redirect
17264
  * @param bool $auto_install Since 1.2.1.7 If `true` and setting up an account with a valid license, will redirect (or return a URL) to the account page with a special parameter to trigger the auto installation processes.
@@ -17271,6 +18209,7 @@
17271
  $user_secret_key,
17272
  $is_marketing_allowed,
17273
  $is_extensions_tracking_allowed,
 
17274
  array $installs,
17275
  $redirect = true,
17276
  $auto_install = false
@@ -17281,7 +18220,10 @@
17281
  $this->disable_opt_in_notice_and_lock_user();
17282
  }
17283
 
17284
- $this->update_extensions_tracking_flag( $is_extensions_tracking_allowed );
 
 
 
17285
 
17286
  $install_ids = array();
17287
 
@@ -17289,12 +18231,13 @@
17289
  $install_ids[] = $install->id;
17290
  }
17291
 
17292
- $left = count( $install_ids );
17293
- $offset = 0;
 
17294
 
17295
  $installs = array();
17296
  while ( $left > 0 ) {
17297
- $result = $this->get_api_user_scope()->get( "/plugins/{$this->_module_id}/installs.json?ids=" . implode( ',', array_slice( $install_ids, $offset, 25 ) ) );
17298
 
17299
  if ( ! $this->is_api_result_object( $result, 'installs' ) ) {
17300
  // @todo Handle API error.
@@ -17302,7 +18245,8 @@
17302
 
17303
  $installs = array_merge( $installs, $result->installs );
17304
 
17305
- $left -= 25;
 
17306
  }
17307
 
17308
  foreach ( $installs as &$install ) {
@@ -17332,7 +18276,8 @@
17332
  $email = false,
17333
  $redirect = true,
17334
  $license_key = false,
17335
- $is_pending_trial = false
 
17336
  ) {
17337
  if ( $this->_ignore_pending_mode ) {
17338
  /**
@@ -17342,12 +18287,12 @@
17342
  * @author Vova Feldman
17343
  * @since 1.2.1.6
17344
  */
17345
- $this->skip_connection( null, fs_is_network_admin() );
17346
  } else {
17347
  // Install must be activated via email since
17348
  // user with the same email already exist.
17349
  $this->_storage->is_pending_activation = true;
17350
- $this->_add_pending_activation_notice( $email, $is_pending_trial );
17351
  }
17352
 
17353
  if ( ! empty( $license_key ) ) {
@@ -17362,8 +18307,8 @@
17362
 
17363
  $next_page = $this->get_after_activation_url( 'after_pending_connect_url' );
17364
 
17365
- // Reload the page with with pending activation message.
17366
  if ( $redirect ) {
 
17367
  fs_redirect( $next_page );
17368
  }
17369
 
@@ -17392,7 +18337,10 @@
17392
  */
17393
  $license_key = fs_request_get( 'license_secret_key' );
17394
 
17395
- $this->update_extensions_tracking_flag( fs_request_get_bool( 'is_extensions_tracking_allowed', null ) );
 
 
 
17396
 
17397
  $this->install_with_current_user( $license_key );
17398
  }
@@ -17410,7 +18358,7 @@
17410
  *
17411
  * @return object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install.
17412
  */
17413
- private function install_with_current_user(
17414
  $license_key = false,
17415
  $trial_plan_id = false,
17416
  $sites = array(),
@@ -17793,9 +18741,6 @@
17793
  $this->send_installs_update();
17794
  }
17795
 
17796
- // Switch install context back to the first install.
17797
- $this->_site = $first_install;
17798
-
17799
  $current_blog = get_current_blog_id();
17800
 
17801
  foreach ( $blog_2_install_map as $blog_id => $install ) {
@@ -17804,7 +18749,12 @@
17804
  $this->do_action( 'after_account_connection', $this->_user, $install );
17805
  }
17806
 
17807
- $this->switch_to_blog( $current_blog );
 
 
 
 
 
17808
 
17809
  $this->do_action( 'after_network_account_connection', $this->_user, $blog_2_install_map );
17810
  }
@@ -17852,9 +18802,7 @@
17852
  $parent_fs->_admin_notices->remove_sticky( 'connect_account' );
17853
 
17854
  if ( $parent_fs->is_pending_activation() ) {
17855
- $parent_fs->_admin_notices->remove_sticky( 'activation_pending' );
17856
-
17857
- unset( $parent_fs->_storage->is_pending_activation );
17858
  }
17859
 
17860
  // Get user information based on parent's plugin.
@@ -17908,6 +18856,10 @@
17908
  // return;
17909
  // }
17910
 
 
 
 
 
17911
  /**
17912
  * When running from a site admin with a network activated module and the connection
17913
  * was NOT delegated and the user still haven't skipped or opted-in, then hide the
@@ -18023,7 +18975,7 @@
18023
  if ( ! $this->has_settings_menu() ) {
18024
  // Add the opt-in page without a menu item.
18025
  $hook = FS_Admin_Menu_Manager::add_subpage(
18026
- null,
18027
  $this->get_plugin_name(),
18028
  $this->get_plugin_name(),
18029
  'manage_options',
@@ -18455,7 +19407,7 @@
18455
  $hook = FS_Admin_Menu_Manager::add_subpage(
18456
  $item['show_submenu'] ?
18457
  $top_level_menu_slug :
18458
- null,
18459
  $item['page_title'],
18460
  $menu_item,
18461
  $capability,
@@ -18470,7 +19422,7 @@
18470
  FS_Admin_Menu_Manager::add_subpage(
18471
  $item['show_submenu'] ?
18472
  $top_level_menu_slug :
18473
- null,
18474
  $item['page_title'],
18475
  $menu_item,
18476
  $capability,
@@ -18862,7 +19814,7 @@
18862
  *
18863
  * @return string
18864
  */
18865
- private static function get_ajax_action_static( $tag, $module_id = null ) {
18866
  $action = "fs_{$tag}";
18867
 
18868
  if ( ! empty( $module_id ) ) {
@@ -18885,10 +19837,10 @@
18885
  * @uses do_action()
18886
  */
18887
  function do_action( $tag, $arg = '' ) {
18888
- $this->_logger->entrance( $tag );
18889
-
18890
  $args = func_get_args();
18891
 
 
 
18892
  call_user_func_array( 'do_action', array_merge(
18893
  array( $this->get_action_tag( $tag ) ),
18894
  array_slice( $args, 1 ) )
@@ -19028,6 +19980,30 @@
19028
  wp_send_json( $result );
19029
  }
19030
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19031
  /**
19032
  * Apply filter, specific for the current context plugin.
19033
  *
@@ -19042,9 +20018,10 @@
19042
  * @uses apply_filters()
19043
  */
19044
  function apply_filters( $tag, $value ) {
 
 
19045
  $this->_logger->entrance( $tag );
19046
 
19047
- $args = func_get_args();
19048
  array_unshift( $args, $this->get_unique_affix() );
19049
 
19050
  return call_user_func_array( 'fs_apply_filter', $args );
@@ -19105,7 +20082,7 @@
19105
  }
19106
 
19107
  /* Account Page
19108
- ------------------------------------------------------------------------------------------------------------------*/
19109
  /**
19110
  * Update site information.
19111
  *
@@ -19116,7 +20093,7 @@
19116
  * @param null|int $network_level_or_blog_id Since 2.0.0
19117
  * @param \FS_Site $site Since 2.0.0
19118
  */
19119
- private function _store_site( $store = true, $network_level_or_blog_id = null, FS_Site $site = null ) {
19120
  $this->_logger->entrance();
19121
 
19122
  if ( is_null( $site ) ) {
@@ -19131,9 +20108,12 @@
19131
 
19132
  $site_clone = clone $site;
19133
 
19134
- $sites = self::get_all_sites( $this->_module_type, $network_level_or_blog_id );
19135
 
19136
- if ( is_object( $this->_user ) && $this->_user->id != $site->user_id ) {
 
 
 
19137
  $this->sync_user_by_current_install( $site->user_id );
19138
 
19139
  $prev_stored_user_id = $this->_storage->get( 'prev_user_id', false, $network_level_or_blog_id );
@@ -19158,7 +20138,26 @@
19158
 
19159
  $sites[ $this->_slug ] = $site_clone;
19160
 
19161
- $this->set_account_option( 'sites', $sites, $store, $network_level_or_blog_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19162
  }
19163
 
19164
  /**
@@ -20399,10 +21398,10 @@
20399
  $this->switch_to_blog( $current_blog_id );
20400
  }
20401
 
20402
- $result = $this->send_install_update( array(), true );
20403
  $is_valid = $this->is_api_result_entity( $result );
20404
  } else {
20405
- $result = $this->send_installs_update( array(), true );
20406
  $is_valid = $this->is_api_result_object( $result, 'installs' );
20407
  }
20408
 
@@ -20412,7 +21411,7 @@
20412
  $this->switch_to_blog( $this->_storage->network_install_blog_id );
20413
  }
20414
 
20415
- // Show API messages only if not background sync or if paying customer.
20416
  if ( ! $background || $this->is_paying() ) {
20417
  // Try to ping API to see if not blocked.
20418
  if ( ! FS_Api::test() ) {
@@ -20422,25 +21421,49 @@
20422
  * @author Vova Feldman (@svovaf)
20423
  * @since 1.1.6 Only show message related to one of the Freemius powered plugins. Once it will be resolved it will fix the issue for all plugins anyways. There's no point to scare users with multiple error messages.
20424
  */
20425
- $api = $this->get_api_site_scope();
20426
 
20427
  if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) {
20428
- self::$_global_admin_notices->add(
20429
- sprintf(
20430
- $this->get_text_inline( 'Your server is blocking the access to Freemius\' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s', 'server-blocking-access' ),
20431
- $this->get_plugin_name(),
20432
- '<b>' . implode( ', ', $this->apply_filters( 'api_domains', array(
20433
- 'api.freemius.com',
20434
- 'wp.freemius.com'
20435
- ) ) ) . '</b>'
20436
- ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
20437
- $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
20438
- 'error',
20439
- $background,
20440
- 'api_blocked'
20441
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20442
  }
20443
- } else {
20444
  // Authentication params are broken.
20445
  $this->_admin_notices->add(
20446
  $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
@@ -20454,6 +21477,9 @@
20454
  return;
20455
  }
20456
 
 
 
 
20457
  if ( $is_site_level_sync ) {
20458
  $site = new FS_Site( $result );
20459
  } else {
@@ -20645,7 +21671,7 @@
20645
  }
20646
 
20647
  if ( ! $this->is_addon() &&
20648
- $this->_site->is_beta() !== $site->is_beta
20649
  ) {
20650
  // Beta flag updated.
20651
  $this->_site = $site;
@@ -21200,8 +22226,7 @@
21200
  if ( ! $this->is_api_result_entity( $plan ) ) {
21201
  // Some API error while trying to start the trial.
21202
  $this->_admin_notices->add(
21203
- sprintf( $this->get_text_inline( 'Unexpected API error. Please contact the %s\'s author with the following error.', 'unexpected-api-error' ), $this->_module_type )
21204
- . ' ' . var_export( $plan, true ),
21205
  $oops_text,
21206
  'error'
21207
  );
@@ -21379,13 +22404,18 @@
21379
  ) {
21380
  $this->_logger->entrance();
21381
 
 
 
 
 
21382
  $switch_to_blog_id = null;
21383
 
21384
  /**
21385
  * @since 1.1.7.3 Check for plugin updates from Freemius only if opted-in.
21386
  * @since 1.1.7.4 Also check updates for add-ons.
21387
  */
21388
- if ( ! $this->is_registered() &&
 
21389
  ! $this->_is_addon_id( $addon_id )
21390
  ) {
21391
  if ( ! is_multisite() ) {
@@ -21395,6 +22425,10 @@
21395
  $installs_map = $this->get_blog_install_map();
21396
 
21397
  foreach ( $installs_map as $blog_id => $install ) {
 
 
 
 
21398
  /**
21399
  * @var FS_Site $install
21400
  */
@@ -21491,9 +22525,11 @@
21491
  private function get_latest_download_api_url( $plugin_id = false ) {
21492
  $this->_logger->entrance();
21493
 
21494
- return $this->get_api_site_scope()->get_signed_url(
21495
  $this->_get_latest_version_endpoint( $plugin_id, 'zip' )
21496
  );
 
 
21497
  }
21498
 
21499
  /**
@@ -21688,7 +22724,6 @@
21688
  private function update_email( $new_email ) {
21689
  $this->_logger->entrance();
21690
 
21691
-
21692
  $api = $this->get_api_user_scope();
21693
  $user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,email,is_verified", 'put', array(
21694
  'email' => $new_email,
@@ -21704,7 +22739,6 @@
21704
  $this->_store_user();
21705
  } else {
21706
  // handle different error cases.
21707
-
21708
  }
21709
 
21710
  return $user;
@@ -21780,15 +22814,32 @@
21780
  * @uses FS_Api
21781
  *
21782
  * @param string $candidate_email
 
21783
  *
21784
  * @return bool Is ownership change successfully initiated.
21785
  */
21786
- private function init_change_owner( $candidate_email ) {
21787
  $this->_logger->entrance();
21788
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21789
  $api = $this->get_api_site_scope();
21790
  $result = $api->call( "/users/{$this->_user->id}.json", 'put', array(
21791
  'email' => $candidate_email,
 
 
21792
  'after_confirm_url' => $this->_get_admin_page_url(
21793
  'account',
21794
  array( 'fs_action' => 'change_owner' )
@@ -21810,28 +22861,113 @@
21810
  private function complete_change_owner() {
21811
  $this->_logger->entrance();
21812
 
21813
- $site_result = $this->get_api_site_scope( true )->get();
21814
- $site = new FS_Site( $site_result );
21815
- $this->_site = $site;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21816
 
21817
- $user = new FS_User();
21818
- $user->id = fs_request_get( 'user_id' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21819
 
21820
  // Validate install's user and given user.
21821
  if ( $user->id != $this->_site->user_id ) {
 
 
21822
  return false;
21823
  }
21824
 
21825
- $user->public_key = fs_request_get( 'user_public_key' );
21826
- $user->secret_key = fs_request_get( 'user_secret_key' );
21827
 
21828
  // Fetch new user information.
21829
- $this->_user = $user;
21830
  $user_result = $this->get_api_user_scope( true )->get();
21831
  $user = new FS_User( $user_result );
21832
  $this->_user = $user;
21833
 
21834
- $this->_set_account( $user, $site );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21835
 
21836
  return true;
21837
  }
@@ -22076,26 +23212,23 @@
22076
 
22077
  if ( $is_parent_plugin_action ) {
22078
  if ( $is_network_action && ! empty( $blog_id ) ) {
22079
- if ( $this->is_registered() ) {
22080
- if ( $this->is_tracking_prohibited() ) {
22081
- if ( $this->allow_site_tracking() ) {
22082
  $this->_admin_notices->add(
22083
- sprintf( $this->get_text_inline( 'We appreciate your help in making the %s better by letting us track some usage data.', 'opt-out-message-appreciation' ), $this->_module_type ),
22084
  $this->get_text_inline( 'Thank you!', 'thank-you' )
22085
  );
22086
  }
22087
  } else {
22088
- if ( $this->stop_site_tracking() ) {
 
 
22089
  $this->_admin_notices->add(
22090
  sprintf(
22091
- $this->get_text_inline( 'We will no longer be sending any usage data of %s on %s to %s.', 'opted-out-successfully' ),
22092
- $this->get_plugin_title(),
22093
- fs_strip_url_protocol( get_site_url( $blog_id ) ),
22094
- sprintf(
22095
- '<a href="%s" target="_blank" rel="noopener">%s</a>',
22096
- 'https://freemius.com',
22097
- 'freemius.com'
22098
- )
22099
  )
22100
  );
22101
  }
@@ -22243,10 +23376,15 @@
22243
  $state = fs_request_get( 'state', 'init' );
22244
  switch ( $state ) {
22245
  case 'init':
22246
- $candidate_email = fs_request_get( 'candidate_email', '' );
 
22247
 
22248
- if ( $this->init_change_owner( $candidate_email ) ) {
22249
- $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Please check your mailbox, you should receive an email via %s to confirm the ownership change. From security reasons, you must confirm the change within the next 15 min. If you cannot find the email, please check your spam folder.', 'change-owner-request-sent-x' ), '<b>' . $this->_user->email . '</b>' ) );
 
 
 
 
22250
  }
22251
  break;
22252
  case 'owner_confirmed':
@@ -22269,37 +23407,6 @@
22269
 
22270
  return;
22271
 
22272
- case 'update_email':
22273
- check_admin_referer( 'update_email' );
22274
-
22275
- $new_email = fs_request_get( 'fs_email_' . $this->get_unique_affix(), '' );
22276
- $result = $this->update_email( $new_email );
22277
-
22278
- if ( isset( $result->error ) ) {
22279
- switch ( $result->error->code ) {
22280
- case 'user_exist':
22281
- $this->_admin_notices->add(
22282
- $this->get_text_inline( 'Sorry, we could not complete the email update. Another user with the same email is already registered.', 'user-exist-message' ) . ' ' .
22283
- sprintf( $this->get_text_inline( 'If you would like to give up the ownership of the %s\'s account to %s click the Change Ownership button.', 'user-exist-message_ownership' ), $this->_module_type, '<b>' . $new_email . '</b>' ) .
22284
- sprintf(
22285
- '<a style="margin-left: 10px;" href="%s"><button class="button button-primary">%s &nbsp;&#10140;</button></a>',
22286
- $this->get_account_url( 'change_owner', array(
22287
- 'state' => 'init',
22288
- 'candidate_email' => $new_email
22289
- ) ),
22290
- $this->get_text_inline( 'Change Ownership', 'change-ownership' )
22291
- ),
22292
- $oops_text,
22293
- 'error'
22294
- );
22295
- break;
22296
- }
22297
- } else {
22298
- $this->_admin_notices->add( $this->get_text_inline( 'Your email was successfully updated. You should receive an email with confirmation instructions in few moments.', 'email-updated-message' ) );
22299
- }
22300
-
22301
- return;
22302
-
22303
  case 'update_user_name':
22304
  check_admin_referer( 'update_user_name' );
22305
 
@@ -22461,7 +23568,26 @@
22461
 
22462
  fs_enqueue_local_style( 'fs_affiliation', '/admin/affiliation.css' );
22463
 
22464
- $vars = array( 'id' => $this->_module_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22465
  echo $this->apply_filters( "/forms/affiliation.php", fs_get_template( '/forms/affiliation.php', $vars ) );
22466
  }
22467
 
@@ -22562,7 +23688,7 @@
22562
  }
22563
 
22564
  /* Pricing & Upgrade
22565
- ------------------------------------------------------------------------------------------------------------------*/
22566
  /**
22567
  * Render pricing page.
22568
  *
@@ -22704,7 +23830,17 @@
22704
  }
22705
 
22706
  static function _clean_admin_content_section_hook() {
22707
- self::_hide_admin_notices();
 
 
 
 
 
 
 
 
 
 
22708
 
22709
  // Hide footer.
22710
  echo '<style>#wpfooter { display: none !important; }</style>';
@@ -22721,17 +23857,17 @@
22721
  }
22722
 
22723
  /* CSS & JavaScript
22724
- ------------------------------------------------------------------------------------------------------------------*/
22725
  /* function _enqueue_script($handle, $src) {
22726
- $url = plugins_url( substr( WP_FS__DIR_JS, strlen( $this->_plugin_dir_path ) ) . '/assets/js/' . $src );
22727
 
22728
- $this->_logger->entrance( 'script = ' . $url );
22729
 
22730
- wp_enqueue_script( $handle, $url );
22731
- }*/
22732
 
22733
  /* SDK
22734
- ------------------------------------------------------------------------------------------------------------------*/
22735
  private $_user_api;
22736
 
22737
  /**
@@ -22743,7 +23879,7 @@
22743
  *
22744
  * @return FS_Api
22745
  */
22746
- private function get_api_user_scope( $flush = false ) {
22747
  if ( ! isset( $this->_user_api ) || $flush ) {
22748
  $this->_user_api = $this->get_api_user_scope_by_user( $this->_user );
22749
  }
@@ -22822,13 +23958,56 @@
22822
  $this->_site->public_key,
22823
  ! $this->is_live(),
22824
  $this->_site->secret_key,
22825
- $this->get_sdk_version()
 
22826
  );
22827
  }
22828
 
22829
  return $this->_site_api;
22830
  }
22831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22832
  private $_plugin_api;
22833
 
22834
  /**
@@ -23264,7 +24443,7 @@
23264
  }
23265
 
23266
  /* Action Links
23267
- ------------------------------------------------------------------------------------------------------------------*/
23268
  private $_action_links_hooked = false;
23269
  private $_action_links = array();
23270
 
@@ -23435,15 +24614,6 @@
23435
 
23436
  $this->_logger->entrance();
23437
 
23438
- /**
23439
- * @author Vova Feldman (@svovaf)
23440
- * @since 2.3.2 Allow opting out from usage-tracking for paid products too by giving the appropriate warning letting the user know the automatic updates mechanism cannot function without an ongoing connection to the licensing and updates engine.
23441
- */
23442
- /*if ( $this->is_premium() ) {
23443
- // Don't add opt-in/out for premium code base.
23444
- return;
23445
- }*/
23446
-
23447
  if ( $this->is_only_premium() && $this->is_free_plan() ) {
23448
  // Don't add tracking links for premium-only products that were opted-in by relation (add-on or a parent product) before activating any license.
23449
  return;
@@ -23451,10 +24621,13 @@
23451
 
23452
  if (
23453
  $this->is_addon() &&
23454
- ! $this->is_only_premium() &&
23455
- $this->_parent->is_anonymous()
23456
  ) {
23457
- return;
 
 
 
 
23458
  }
23459
 
23460
  if ( fs_is_network_admin() ) {
@@ -23503,23 +24676,15 @@
23503
  }
23504
  }
23505
 
23506
- if ( $this->add_ajax_action( 'stop_tracking', array( &$this, '_stop_tracking_callback' ) ) ) {
23507
- return;
23508
- }
23509
-
23510
- if ( $this->add_ajax_action( 'allow_tracking', array( &$this, '_allow_tracking_callback' ) ) ) {
23511
- return;
23512
- }
23513
-
23514
- if ( $this->add_ajax_action( 'update_tracking_permission', array( &$this, '_update_tracking_permission_callback' ) ) ) {
23515
  return;
23516
  }
23517
 
23518
  $link_text_id = '';
23519
  $url = '#';
23520
 
23521
- if ( $this->is_registered() ) {
23522
- if ( $this->is_tracking_allowed() ) {
23523
  $link_text_id = $this->get_text_inline( 'Opt Out', 'opt-out' );
23524
  } else {
23525
  $link_text_id = $this->get_text_inline( 'Opt In', 'opt-in' );
@@ -23716,9 +24881,12 @@
23716
  */
23717
  private function is_premium_version_installed() {
23718
  $premium_plugin_basename = $this->premium_plugin_basename();
23719
- $premium_plugin = get_plugins( '/' . dirname( $premium_plugin_basename ) );
23720
 
23721
- return ! empty( $premium_plugin );
 
 
 
 
23722
  }
23723
 
23724
  /**
@@ -23754,7 +24922,9 @@
23754
  * @author Leo Fajardo (@leorw)
23755
  * @since 2.2.1
23756
  */
23757
- $premium_plugin_basename = $this->premium_plugin_basename();
 
 
23758
 
23759
  return sprintf(
23760
  /* translators: %1$s: Product title; %2$s: Plan title */
@@ -23763,7 +24933,9 @@
23763
  $plan_title,
23764
  sprintf(
23765
  '<a style="margin-left: 10px;" href="%s"><button class="button button-primary">%s</button></a>',
23766
- wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $premium_plugin_basename, 'activate-plugin_' . $premium_plugin_basename ),
 
 
23767
  esc_html( sprintf(
23768
  /* translators: %s: Plan title */
23769
  $this->get_text_inline( 'Activate %s features', 'activate-x-features' ),
@@ -23788,7 +24960,7 @@
23788
  ) ),
23789
  $deactivation_step,
23790
  $this->get_text_inline( 'Upload and activate the downloaded version', 'upload-and-activate' ),
23791
- $this->apply_filters( 'upload_and_install_video_url', '//bit.ly/upload-wp-' . $this->_module_type . 's' ),
23792
  $this->get_text_inline( 'How to upload and activate?', 'howto-upload-activate' )
23793
  );
23794
  }
@@ -24241,8 +25413,10 @@
24241
  function _tabs_capture() {
24242
  $this->_logger->entrance();
24243
 
24244
- if ( ! $this->is_product_settings_page() ||
24245
- ! $this->is_matching_url( $this->main_menu_url() )
 
 
24246
  ) {
24247
  return;
24248
  }
@@ -24296,8 +25470,10 @@
24296
  function _store_tabs_styles() {
24297
  $this->_logger->entrance();
24298
 
24299
- if ( ! $this->is_product_settings_page() ||
24300
- ! $this->is_matching_url( $this->main_menu_url() )
 
 
24301
  ) {
24302
  return;
24303
  }
377
  const REASON_DIDNT_WORK_AS_EXPECTED = 14;
378
  const REASON_TEMPORARY_DEACTIVATION = 15;
379
 
380
+ #endregion
381
+
382
  /**
383
  * @author Leo Fajardo (@leorw)
384
  * @since 2.3.1
394
  */
395
  private $_pricing_js_path = null;
396
 
397
+ const VERSION_MAX_CHARS = 16;
398
+ const LANGUAGE_MAX_CHARS = 8;
399
 
400
  /* Ctor
401
  ------------------------------------------------------------------------------------------------------------------*/
411
  * @param bool $is_init Since 1.2.1 Is initiation sequence.
412
  */
413
  private function __construct( $module_id, $slug = false, $is_init = false ) {
414
+ $main_file = false;
415
+
416
  if ( $is_init && is_numeric( $module_id ) && is_string( $slug ) ) {
417
+ $main_file = $this->store_id_slug_type_path_map( $module_id, $slug );
418
  }
419
 
420
  $this->_module_id = $module_id;
429
 
430
  $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $this->get_unique_affix(), WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
431
 
432
+ $this->_plugin_main_file_path = $this->_find_caller_plugin_file( $is_init, $main_file );
433
  $this->_plugin_dir_path = plugin_dir_path( $this->_plugin_main_file_path );
434
  $this->_plugin_basename = $this->get_plugin_basename();
435
  $this->_free_plugin_basename = str_replace( '-premium/', '/', $this->_plugin_basename );
524
  * @author Leo Fajardo (@leorw)
525
  * @since 1.2.2
526
  */
527
+ ( is_object( $this->_plugin ) && isset( $this->_plugin->title ) ?
528
+ $this->_plugin->title :
529
+ $this->get_plugin_name()
530
+ ),
531
  $this->get_unique_affix()
532
  );
533
 
941
  * @param string $sdk_version
942
  */
943
  function _sdk_version_update( $sdk_prev_version, $sdk_version ) {
 
 
 
944
  if ( empty( $sdk_prev_version ) ) {
945
  return;
946
  }
947
 
948
+ if (
949
+ version_compare( $sdk_prev_version, '2.5.1', '<' ) &&
950
+ version_compare( $sdk_version, '2.5.1', '>=' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
951
  ) {
952
+ if ( $this->is_registered( true ) ) {
953
+ /**
954
+ * Migrate to new permissions layer.
955
+ */
956
+ require_once WP_FS__DIR_INCLUDES . '/supplements/fs-migration-2.5.1.php';
 
 
 
 
 
 
957
 
958
+ $install_by_blog_id = is_multisite() ?
959
+ $this->get_blog_install_map() :
960
+ array( 0 => $this->_site );
961
 
962
+ fs_migrate_251( $this, $install_by_blog_id );
963
  }
964
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
965
  }
966
 
967
  /**
1012
  }
1013
  }
1014
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
  /**
1016
  * @author Vova Feldman (@svovaf)
1017
  * @since 1.2.2.7
1348
  }
1349
  }
1350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1351
  /**
1352
  * Opens the support forum subemenu item in a new browser page.
1353
  *
1458
  }
1459
 
1460
  if ( $this->is_plugin() ) {
1461
+ if ( version_compare( $GLOBALS['wp_version'], '5.1', '<' ) ) {
1462
  add_action( 'wpmu_new_blog', array( $this, '_after_new_blog_callback' ), 10, 6 );
1463
+ } else {
1464
+ add_action( 'wp_initialize_site', array( $this, '_after_wp_initialize_site_callback' ), 11, 2 );
1465
  }
1466
 
1467
  register_deactivation_hook( $this->_plugin_main_file_path, array( &$this, '_deactivate_plugin_hook' ) );
1471
  add_action( 'deactivate_blog', array( &$this, '_after_site_deactivated_callback' ) );
1472
  add_action( 'archive_blog', array( &$this, '_after_site_deactivated_callback' ) );
1473
  add_action( 'make_spam_blog', array( &$this, '_after_site_deactivated_callback' ) );
1474
+
1475
+ if ( version_compare( $GLOBALS['wp_version'], '5.1', '<' ) ) {
1476
+ add_action( 'deleted_blog', array( $this, '_after_site_deleted_callback' ), 10, 2 );
1477
+ } else {
1478
+ add_action( 'wp_delete_site', array( $this, '_after_wpsite_deleted_callback' ) );
1479
+ }
1480
 
1481
  add_action( 'activate_blog', array( &$this, '_after_site_reactivated_callback' ) );
1482
  add_action( 'unarchive_blog', array( &$this, '_after_site_reactivated_callback' ) );
1501
  add_action( 'admin_init', array( &$this, '_add_premium_version_upgrade_selection' ) );
1502
  add_action( 'admin_init', array( &$this, '_add_beta_mode_update_handler' ) );
1503
  add_action( 'admin_init', array( &$this, '_add_user_change_option' ) );
1504
+ add_action( 'admin_init', array( &$this, '_add_email_address_update_option' ) );
1505
 
1506
  $this->add_ajax_action( 'update_billing', array( &$this, '_update_billing_ajax_action' ) );
1507
  $this->add_ajax_action( 'start_trial', array( &$this, '_start_trial_ajax_action' ) );
1873
  return;
1874
  }
1875
 
1876
+ if (
1877
+ ( self::is_plugins_page() && $this->is_plugin() ) ||
1878
+ ( self::is_themes_page() && $this->is_theme() ) ||
1879
+ fs_request_is_action_secure( $this->get_unique_affix() . '_reconnect' )
1880
+ ) {
1881
+ $this->_add_tracking_links();
1882
+ }
1883
 
1884
  if ( self::is_plugins_page() && $this->is_plugin() ) {
1885
  $this->hook_plugin_action_links();
1925
  /**
1926
  * Leverage backtrace to find caller plugin file path.
1927
  *
1928
+ * @param bool $is_init Is initiation sequence.
1929
+ * @param string $main_file Since 2.5.0 expects the module's main file path to potentially purge the cached path.
 
 
1930
  *
1931
  * @return string
1932
+ * @since 1.0.6
1933
+ *
1934
+ * @author Vova Feldman (@svovaf)
1935
  */
1936
+ private function _find_caller_plugin_file( $is_init = false, $main_file = '' ) {
1937
  // Try to load the cached value of the file path.
1938
  if ( isset( $this->_storage->plugin_main_file ) ) {
1939
  $plugin_main_file = $this->_storage->plugin_main_file;
1940
  if ( ! empty( $plugin_main_file->path ) ) {
1941
  $absolute_path = $this->get_absolute_path( $plugin_main_file->path );
1942
  if ( file_exists( $absolute_path ) ) {
1943
+ if ( $is_init && $absolute_path !== $this->get_absolute_path( $main_file ) ) {
1944
+ // Update cached path if not matching the actual path.
1945
+ $plugin_main_file->path = $main_file;
1946
+ $this->_storage->plugin_main_file = $plugin_main_file;
1947
+ }
1948
+
1949
  return $absolute_path;
1950
  }
1951
  }
1986
  * Only the original instantiator that calls dynamic_init can modify the module's path.
1987
  */
1988
  // Find caller module.
 
1989
  $this->_storage->plugin_main_file = (object) array(
1990
+ 'path' => $main_file,
1991
  );
1992
 
1993
+ return $this->get_absolute_path( $main_file );
1994
  }
1995
 
1996
  /**
2052
  * @param number $module_id
2053
  * @param string $slug
2054
  *
2055
+ * @return string Since 2.5.0 return the module's main file path.
2056
+ *
2057
  * @since 1.2.2
2058
  */
2059
  private function store_id_slug_type_path_map( $module_id, $slug ) {
2075
  $store_option = true;
2076
  }
2077
 
2078
+ $find_caller = empty( $id_slug_type_path_map[ $module_id ]['path'] );
2079
+
2080
+ if ( ! $find_caller ) {
2081
+ /**
2082
+ * This verification is for cases when suddenly the same module
2083
+ * is installed but with a different folder name.
2084
+ *
2085
+ * @author Vova Feldman (@svovaf)
2086
+ * @since 1.2.3
2087
+ */
2088
+ $find_caller = ! file_exists( $this->get_absolute_path(
2089
+ $id_slug_type_path_map[ $module_id ]['path'],
2090
+ $id_slug_type_path_map[ $module_id ]['type']
2091
+ ) );
2092
+ }
2093
+
2094
+ foreach ( $id_slug_type_path_map as $id => $data ) {
2095
+ if ( empty( $id ) ) {
2096
+ // Remove maps with empty module ID.
2097
+ unset( $id_slug_type_path_map[ $id ] );
2098
+ $store_option = true;
2099
+ continue;
2100
+ }
2101
+
2102
+ /**
2103
+ * If the module's main file path is identical to the main file path of another module then it means that the cached path of the current module or the other one with the same path is wrong, and therefore, we need to recalculate those paths.
2104
+ *
2105
+ * @author Vova Feldman (@svovaf)
2106
+ * @since 2.5.0
2107
+ */
2108
+ if ( ! $find_caller ) {
2109
+ if ( $id == $module_id ) {
2110
+ continue;
2111
+ }
2112
+
2113
+ if (
2114
+ isset( $data['path'] ) &&
2115
+ $data['path'] === $id_slug_type_path_map[ $module_id ]['path']
2116
+ ) {
2117
+ $find_caller = true;
2118
+ }
2119
+ }
2120
+ }
2121
+
2122
+ if ( $find_caller ) {
2123
+ $caller_main_file_and_type = $this->get_caller_main_file_and_type( $module_id );
2124
 
2125
  $id_slug_type_path_map[ $module_id ]['type'] = $caller_main_file_and_type->module_type;
2126
  $id_slug_type_path_map[ $module_id ]['path'] = $caller_main_file_and_type->path;
2131
  if ( $store_option ) {
2132
  self::$_accounts->set_option( 'id_slug_type_path_map', $id_slug_type_path_map, true );
2133
  }
2134
+
2135
+ return $id_slug_type_path_map[ $module_id ]['path'];
2136
  }
2137
 
2138
  /**
2146
  * add-ons are relying on loading the SDK from the parent module, and also allows themes including the
2147
  * SDK an internal file instead of directly from functions.php.
2148
  * @since 1.2.1.7 Knows how to handle cases when an add-on includes the parent module logic.
2149
+ *
2150
+ * @param number $module_id @since 2.5.0
2151
  */
2152
+ private function get_caller_main_file_and_type( $module_id ) {
2153
  self::require_plugin_essentials();
2154
 
2155
  $all_plugins = fs_get_plugins( true );
2288
  }
2289
  }
2290
 
2291
+ $caller_main_file_and_type = (object) array(
2292
  'module_type' => $module_type,
2293
  'path' => $caller_file_candidate
2294
  );
2295
+
2296
+ return apply_filters( "fs_{$module_id}_caller_main_file_and_type", $caller_main_file_and_type );
2297
  }
2298
 
2299
  #----------------------------------------------------------------------------------
2310
  * @since 1.1.2
2311
  */
2312
  function _add_deactivation_feedback_dialog_box() {
2313
+ if (
2314
+ $this->is_clone() ||
2315
+ ( is_object( $this->_site ) && ! $this->is_registered() )
2316
+ ) {
2317
+ return;
2318
+ }
2319
+
2320
  $subscription_cancellation_dialog_box_template_params = $this->apply_filters( 'show_deactivation_subscription_cancellation', true ) ?
2321
  $this->_get_subscription_cancellation_dialog_box_template_params() :
2322
  array();
2324
  /**
2325
  * @since 2.3.0 Developers can optionally hide the deactivation feedback form using the 'show_deactivation_feedback_form' filter.
2326
  */
2327
+ $show_deactivation_feedback_form = ! self::is_deactivation_snoozed();
2328
  if ( $this->has_filter( 'show_deactivation_feedback_form' ) ) {
2329
  $show_deactivation_feedback_form = $this->apply_filters( 'show_deactivation_feedback_form', true );
2330
  } else if ( $this->is_addon() ) {
2429
  $reason_temporary_deactivation = array(
2430
  'id' => self::REASON_TEMPORARY_DEACTIVATION,
2431
  'text' => sprintf(
2432
+ $this->get_text_inline( "It's a temporary %s - I'm troubleshooting an issue", 'reason-temporary-x' ),
2433
  strtolower( $this->is_plugin() ?
2434
  $this->get_text_inline( 'Deactivation', 'deactivation' ) :
2435
  $this->get_text_inline( 'Theme Switch', 'theme-switch' )
2594
 
2595
  $this->_storage->store( 'uninstall_reason', $reason );
2596
 
2597
+ if ( self::REASON_TEMPORARY_DEACTIVATION == $reason->id ) {
2598
+ $snooze_period = fs_request_get( 'snooze_period' );
2599
+
2600
+ if ( is_numeric( $snooze_period ) && 0 < $snooze_period ) {
2601
+ self::snooze_deactivation_form( (int) $snooze_period );
2602
+ }
2603
+ }
2604
+
2605
  /**
2606
  * If the module type is "theme", trigger the uninstall event here (on theme deactivation) since themes do
2607
  * not support uninstall hook.
2623
  exit;
2624
  }
2625
 
2626
+ #--------------------------------------------------------------------------------
2627
+ #region Deactivation Feedback Snoozing
2628
+ #--------------------------------------------------------------------------------
2629
+
2630
+ /**
2631
+ * @author Vova Feldman (@svovaf)
2632
+ * @since 2.4.3
2633
+ *
2634
+ * @param int $period
2635
+ *
2636
+ * @return bool True if the value was set, false otherwise.
2637
+ */
2638
+ private static function snooze_deactivation_form( $period ) {
2639
+ return ( 0 < $period && self::reset_deactivation_snoozing( $period ) );
2640
+ }
2641
+
2642
+ /**
2643
+ * Check if deactivation feedback form is snoozed.
2644
+ *
2645
+ * @author Vova Feldman (@svovaf)
2646
+ * @since 2.4.3
2647
+ *
2648
+ * @return bool
2649
+ */
2650
+ static function is_deactivation_snoozed() {
2651
+ $is_snoozed = ( ! is_multisite() || fs_is_network_admin() ) ?
2652
+ get_transient( 'fs_snooze_period' ) :
2653
+ get_site_transient( 'fs_snooze_period' );
2654
+
2655
+
2656
+ return ( 'true' === $is_snoozed );
2657
+ }
2658
+
2659
+ /**
2660
+ * Reset deactivation snoozing. When `$period` is `0` will stop deactivation snoozing by deleting the transients. Otherwise, will set the transients for the selected period.
2661
+ *
2662
+ * @param int $period Period in seconds.
2663
+ *
2664
+ * @author Vova Feldman (@svovaf)
2665
+ * @since 2.4.3
2666
+ */
2667
+ private static function reset_deactivation_snoozing( $period = 0 ) {
2668
+ $value = ( 0 === $period ) ? null : 'true';
2669
+
2670
+ if ( ! is_multisite() || fs_is_network_admin() ) {
2671
+ return set_transient( 'fs_snooze_period', $value, $period );
2672
+ } else {
2673
+ return set_site_transient( 'fs_snooze_period', $value, $period );
2674
+ }
2675
+ }
2676
+
2677
+ /**
2678
+ * The deactivation snooze expiration UNIX timestamp (in sec).
2679
+ *
2680
+ * @author Vova Feldman (@svovaf)
2681
+ * @since 2.4.3
2682
+ *
2683
+ * @return int
2684
+ */
2685
+ static function deactivation_snooze_expires_at() {
2686
+ return ( ! is_multisite() || fs_is_network_admin() ) ?
2687
+ (int) get_option( '_transient_timeout_fs_snooze_period' ) :
2688
+ (int) get_site_option( '_site_transient_timeout_fs_snooze_period' );
2689
+ }
2690
+
2691
+ #endregion
2692
+
2693
  /**
2694
  * @author Leo Fajardo (@leorw)
2695
  * @since 2.1.4
2886
  return self::instance( $addon_id );
2887
  }
2888
 
2889
+ /**
2890
+ * @return Freemius[]
2891
+ */
2892
+ static function _get_all_instances() {
2893
+ return self::$_instances;
2894
+ }
2895
+
2896
  #endregion ------------------------------------------------------------------
2897
 
2898
  /**
3403
  add_action( 'plugins_loaded', array( 'Freemius', '_load_textdomain' ), 1 );
3404
  }
3405
 
3406
+ $clone_manager = FS_Clone_Manager::instance();
3407
+ add_action( 'init', array( $clone_manager, '_init' ) );
3408
+
3409
  add_action( 'admin_footer', array( 'Freemius', '_open_support_forum_in_new_page' ) );
3410
 
3411
  if ( self::is_plugins_page() || self::is_themes_page() ) {
3424
  self::$_statics_loaded = true;
3425
  }
3426
 
3427
+ #--------------------------------------------------------------------------------
3428
+ #region Clone
3429
+ #--------------------------------------------------------------------------------
3430
+
3431
  /**
3432
  * @author Leo Fajardo (@leorw)
3433
+ * @since 2.5.0
3434
  *
3435
+ * @param bool $only_if_manual_resolution_is_not_hidden
3436
+ *
3437
+ * @return bool
3438
  */
3439
+ private function is_unresolved_clone( $only_if_manual_resolution_is_not_hidden = false ) {
3440
+ if ( ! $this->is_clone( $only_if_manual_resolution_is_not_hidden ) ) {
3441
+ return false;
3442
+ }
 
 
 
 
 
3443
 
3444
+ return FS_Clone_Manager::instance()->has_temporary_duplicate_mode_expired();
3445
  }
3446
 
 
 
 
 
3447
  /**
3448
+ * @author Leo Fajardo (@leorw)
3449
+ * @since 2.5.0
3450
  *
3451
+ * @param bool $only_if_manual_resolution_is_not_hidden
 
3452
  */
3453
+ function is_clone( $only_if_manual_resolution_is_not_hidden = false ) {
3454
+ if ( ! is_object( $this->_site ) ) {
3455
+ return false;
3456
  }
3457
 
3458
+ $blog_id = null;
3459
 
3460
+ if (
3461
+ fs_is_network_admin() &&
3462
+ FS_Site::is_valid_id( $this->_storage->network_install_blog_id )
3463
+ ) {
3464
+ // Ensure that we're comparing the network install's URL with the relevant subsite's URL.
3465
+ $blog_id = $this->_storage->network_install_blog_id;
3466
+ }
3467
+
3468
+ $site_url = Freemius::get_unfiltered_site_url( $blog_id, true, true );
3469
+
3470
+ if ( ! $this->_site->is_clone( $site_url ) ) {
3471
+ return false;
3472
+ }
3473
+
3474
+ return (
3475
+ ! $only_if_manual_resolution_is_not_hidden ||
3476
+ ! FS_Clone_Manager::instance()->should_hide_manual_resolution()
3477
+ );
3478
+ }
3479
+
3480
+ /**
3481
+ * @author Leo Fajardo (@leorw)
3482
+ * @since 2.5.0
3483
+ *
3484
+ * @param int|null $blog_id
3485
+ * @param bool $strip_protocol
3486
+ * @param bool $add_trailing_slash
3487
+ *
3488
+ * @return string
3489
+ */
3490
+ static function get_unfiltered_site_url( $blog_id = null, $strip_protocol = false, $add_trailing_slash = false ) {
3491
+ global $wp_filter;
3492
+
3493
+ $site_url_filters = array(
3494
+ 'site_url' => null,
3495
+ 'pre_option_siteurl' => null,
3496
+ 'default_option_siteurl' => null,
3497
+ 'option_siteurl' => null,
3498
+ );
3499
+
3500
+ // Detach all URL-related filters to get the actual site's URL (stripped of potential manipulations by multilingual plugins).
3501
+ foreach ( $site_url_filters as $hook_name => $site_url_filter ) {
3502
+ if ( ! empty( $wp_filter[ $hook_name ] ) ) {
3503
+ $site_url_filters[ $hook_name ] = $wp_filter[ $hook_name ];
3504
+ unset( $wp_filter[ $hook_name ] );
3505
+ }
3506
+ }
3507
+
3508
+ $url = get_site_url( $blog_id );
3509
+
3510
+ // Re-attach the filters back.
3511
+ foreach ( $site_url_filters as $hook_name => $site_url_filter ) {
3512
+ if ( ! empty( $site_url_filter ) ) {
3513
+ $wp_filter[ $hook_name ] = $site_url_filter;
3514
+ }
3515
+ }
3516
+
3517
+ if ( $strip_protocol ) {
3518
+ $url = fs_strip_url_protocol( $url );
3519
+ }
3520
+
3521
+ if ( $add_trailing_slash ) {
3522
+ $url = trailingslashit( $url );
3523
+ }
3524
+
3525
+ return $url;
3526
+ }
3527
+
3528
+ /**
3529
+ * @author Leo Fajardo (@leorw)
3530
+ * @since 2.5.0
3531
+ *
3532
+ * @param number $site_id
3533
+ */
3534
+ function fetch_install_by_id( $site_id ) {
3535
+ return $this->get_current_or_network_user_api_scope()->get( "/installs/{$site_id}.json" );
3536
+ }
3537
+
3538
+ /**
3539
+ * @author Leo Fajardo (@leorw)
3540
+ * @since 2.5.0
3541
+ *
3542
+ * @return string|object|bool
3543
+ */
3544
+ function _handle_long_term_duplicate() {
3545
+ $this->_logger->entrance();
3546
+
3547
+ $this->delete_current_install( false );
3548
+
3549
+ $license_key = false;
3550
+
3551
+ if (
3552
+ is_object( $this->_license ) &&
3553
+ ! $this->_license->is_utilized(
3554
+ ( WP_FS__IS_LOCALHOST_FOR_SERVER || FS_Site::is_localhost_by_address( self::get_unfiltered_site_url() ) )
3555
+ )
3556
+ ) {
3557
+ $license_key = $this->_license->secret_key;
3558
+ }
3559
+
3560
+ return $this->opt_in(
3561
+ false,
3562
+ false,
3563
+ false,
3564
+ $license_key,
3565
+ false,
3566
+ false,
3567
+ false,
3568
+ null,
3569
+ array(),
3570
+ false
3571
+ );
3572
+ }
3573
+
3574
+ #endregion
3575
+
3576
+ /**
3577
+ * @author Leo Fajardo (@leorw)
3578
+ *
3579
+ * @since 2.1.3
3580
+ */
3581
+ private static function migrate_options_to_network() {
3582
+ self::migrate_accounts_to_network();
3583
+
3584
+ // Migrate API options from site level to network level.
3585
+ $api_network_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true, true );
3586
+ $api_network_options->migrate_to_network();
3587
+
3588
+ // Migrate API cache to network level storage.
3589
+ FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME )->migrate_to_network();
3590
+
3591
+ self::$_accounts->set_option( 'ms_migration_complete', true, true );
3592
+ }
3593
+
3594
+ #----------------------------------------------------------------------------------
3595
+ #region Localization
3596
+ #----------------------------------------------------------------------------------
3597
+
3598
+ /**
3599
+ * Load framework's text domain.
3600
+ *
3601
+ * @author Vova Feldman (@svovaf)
3602
+ * @since 1.2.1
3603
+ */
3604
+ static function _load_textdomain() {
3605
+ if ( ! is_admin() ) {
3606
+ return;
3607
+ }
3608
+
3609
+ global $fs_active_plugins;
3610
+
3611
+ // Works both for plugins and themes.
3612
+ load_plugin_textdomain(
3613
+ 'freemius',
3614
+ false,
3615
+ $fs_active_plugins->newest->sdk_path . '/languages/'
3616
+ );
3617
+ }
3618
 
3619
  #endregion
3620
 
3648
  } else {
3649
  // Add hidden debug page.
3650
  $hook = FS_Admin_Menu_Manager::add_subpage(
3651
+ '',
3652
  $title,
3653
  $title,
3654
  'manage_options',
3809
 
3810
  switch_to_blog( $current_blog_id );
3811
  }
3812
+ } else if ( fs_request_is_action( 'reset_deactivation_snoozing' ) ) {
3813
+ check_admin_referer( 'reset_deactivation_snoozing' );
3814
+
3815
+ self::reset_deactivation_snoozing();
3816
  } else if ( fs_request_is_action( 'simulate_trial' ) ) {
3817
  check_admin_referer( 'simulate_trial' );
3818
 
3861
  }
3862
 
3863
  /**
3864
+ * @author Leo Fajardo (@leorw)
3865
+ * @since 2.5.0
3866
+ *
3867
+ * @return array
3868
  */
3869
+ static function get_all_modules_sites() {
3870
  self::$_static_logger->entrance();
3871
 
3872
+ $sites_by_type = array(
3873
+ WP_FS__MODULE_TYPE_PLUGIN => array(),
3874
+ WP_FS__MODULE_TYPE_THEME => array(),
3875
+ );
3876
+
3877
+ $module_types = array_keys( $sites_by_type );
3878
+
3879
  if ( ! is_multisite() ) {
3880
+ foreach ( $module_types as $type ) {
3881
+ $sites_by_type[ $type ] = self::get_all_sites( $type );
3882
+
3883
+ foreach ( $sites_by_type[ $type ] as $slug => $install ) {
3884
+ $sites_by_type[ $type ][ $slug ] = array( $install );
3885
+ }
3886
+ }
3887
  } else {
3888
  $sites = self::get_sites();
3889
 
 
 
 
3890
  foreach ( $sites as $site ) {
3891
  $blog_id = self::get_site_blog_id( $site );
3892
 
3893
+ foreach ( $module_types as $type ) {
3894
+ $installs = self::get_all_sites( $type, $blog_id );
 
 
 
 
 
 
3895
 
3896
+ foreach ( $installs as $slug => $install ) {
3897
+ if ( ! isset( $sites_by_type[ $type ][ $slug ] ) ) {
3898
+ $sites_by_type[ $type ][ $slug ] = array();
3899
+ }
3900
 
3901
+ $install->blog_id = $blog_id;
3902
 
3903
+ $sites_by_type[ $type ][ $slug ][] = $install;
 
 
3904
  }
3905
 
 
 
 
3906
  }
3907
  }
3908
  }
3909
 
3910
+ return $sites_by_type;
3911
+ }
3912
+
3913
+ /**
3914
+ * @author Vova Feldman (@svovaf)
3915
+ * @since 1.0.8
3916
+ */
3917
+ static function _debug_page_render() {
3918
+ self::$_static_logger->entrance();
3919
+
3920
+ $all_modules_sites = self::get_all_modules_sites();
3921
+
3922
  $licenses_by_module_type = self::get_all_licenses_by_module_type();
3923
 
3924
  $vars = array(
3925
+ 'plugin_sites' => $all_modules_sites[ WP_FS__MODULE_TYPE_PLUGIN ],
3926
+ 'theme_sites' => $all_modules_sites[ WP_FS__MODULE_TYPE_THEME ],
3927
  'users' => self::get_all_users(),
3928
  'addons' => self::get_all_addons(),
3929
  'account_addons' => self::get_all_account_addons(),
3955
  function is_on() {
3956
  self::$_static_logger->entrance();
3957
 
3958
+ if ( is_object( $this->_site ) && ! $this->is_registered() ) {
3959
+ return false;
3960
+ }
3961
+
3962
  if ( isset( $this->_is_on ) ) {
3963
  return $this->_is_on;
3964
  }
4047
 
4048
  $is_update = $this->apply_filters( 'is_plugin_update', $this->is_plugin_update() );
4049
 
4050
+ $params = array(
4051
+ 'is_update' => json_encode( $is_update ),
4052
+ 'version' => $version,
4053
+ 'sdk' => $this->version,
4054
+ 'is_admin' => json_encode( is_admin() ),
4055
+ 'is_ajax' => json_encode( self::is_ajax() ),
4056
+ 'is_cron' => json_encode( self::is_cron() ),
4057
+ 'is_gdpr_test' => $is_gdpr_test,
4058
+ 'is_http' => json_encode( WP_FS__IS_HTTP_REQUEST ),
4059
+ );
4060
+
4061
+ if ( is_multisite() && function_exists( 'get_network' ) ) {
4062
+ $params['network_uid'] = $this->get_anonymous_network_id();
4063
+ }
4064
+
4065
  return $this->get_api_plugin_scope()->ping(
4066
  $this->get_anonymous_id( $blog_id ),
4067
+ $params
 
 
 
 
 
 
 
 
 
4068
  );
4069
  }
4070
 
4201
  $unique_id = self::$_accounts->get_option( 'unique_id', null, $blog_id );
4202
 
4203
  if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
4204
+ $key = self::get_unfiltered_site_url( $blog_id, true );
4205
 
4206
  $secure_auth = defined( 'SECURE_AUTH_KEY' ) ? SECURE_AUTH_KEY : '';
4207
  if ( empty( $secure_auth ) ||
4230
  return $unique_id;
4231
  }
4232
 
4233
+ /**
4234
+ * Returns anonymous network ID.
4235
+ *
4236
+ * @since 2.4.3
4237
+ *
4238
+ * @return string
4239
+ */
4240
+ function get_anonymous_network_id() {
4241
+ return $this->get_anonymous_id( get_network()->site_id );
4242
+ }
4243
+
4244
  /**
4245
  * @author Vova Feldman (@svovaf)
4246
  * @since 1.1.7.4
4389
  $install_previous_desc = $this->esc_html_inline( 'Uninstall this version and install the previous one.', 'install-previous-desc' );
4390
  $fix_issue_title = $this->esc_html_inline( 'Yes - I\'m giving you a chance to fix it', 'fix-issue-title' );
4391
  $fix_issue_desc = $this->esc_html_inline( 'We will do our best to whitelist your server and resolve this issue ASAP. You will get a follow-up email to %s once we have an update.', 'fix-issue-desc' );
4392
+ /* translators: %s: product title (e.g. "Awesome Plugin" requires access to...) */
4393
+ $x_requires_access_to_api = $this->esc_html_inline( '%s requires access to our API.', 'x-requires-access-to-api' );
4394
  $sysadmin_title = $this->esc_html_inline( 'I\'m a system administrator', 'sysadmin-title' );
4395
  $happy_to_resolve_issue_asap = $this->esc_html_inline( 'We are sure it\'s an issue on our side and more than happy to resolve it for you ASAP if you give us a chance.', 'happy-to-resolve-issue-asap' );
4396
 
4397
+ if ( $this->is_premium() ) {
4398
+ /* translators: This string is optionally prepended to 'plugin requires access to our API.' */
4399
+ $x_requires_access_to_api = $this->esc_html_inline( 'For automatic delivery of security & feature updates,', 'requires-api-for' ) . ' ' . $x_requires_access_to_api;
4400
+ }
4401
+
4402
  $message = false;
4403
  if ( is_object( $api_result ) &&
4404
  isset( $api_result->error ) &&
5030
 
5031
  /**
5032
  * This should be executed even if Freemius is off for the core module,
5033
+ * otherwise, the add-ons dialog box won't work properly. This is especially
5034
  * relevant when the developer decided to turn FS off for existing users.
5035
  *
5036
  * @author Vova Feldman (@svovaf)
5068
  * @since 1.1.7.3
5069
  *
5070
  */
5071
+ if ( $this->is_registered() && $this->is_tracking_allowed() ) {
5072
+ $this->maybe_schedule_sync_cron();
 
 
5073
  }
5074
 
5075
  /**
5076
  * Check if requested for manual blocking background sync.
5077
  */
5078
  if ( fs_request_has( 'background_sync' ) ) {
5079
+ self::require_pluggable_essentials();
5080
+ self::wp_cookie_constants();
5081
+
5082
  $this->run_manual_sync();
5083
  }
5084
  }
5085
  }
5086
 
5087
  if ( $this->is_registered() ) {
5088
+ FS_Clone_Manager::instance()->maybe_resolve_new_subsite_install_automatically( $this );
5089
+
5090
  $this->hook_callback_to_install_sync();
5091
  }
5092
 
5101
  }
5102
 
5103
  if ( $this->is_user_in_admin() ) {
5104
+ if ( $this->is_registered() && fs_request_has( 'purchase_completed' ) ) {
5105
+ $this->_admin_notices->add_sticky(
5106
+ sprintf(
5107
+ /* translators: %s: License type (e.g. you have a professional license) */
5108
+ $this->get_text_inline( 'You have purchased a %s license.', 'you-have-x-license' ),
5109
+ fs_request_get( 'purchased_plan' )
5110
+ ) .
5111
+ sprintf(
5112
+ $this->get_text_inline(" The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box.", 'post-purchase-email-sent-message' ),
5113
+ $this->get_module_label( true ),
5114
+ ( FS_Plugin::is_valid_id( $this->get_bundle_id() ) ? "products' " : '' ),
5115
+ ( FS_Plugin::is_valid_id( $this->get_bundle_id() ) ? 's' : '' ),
5116
+ sprintf(
5117
+ '<strong>%s</strong>',
5118
+ fs_request_get( 'purchase_email' )
5119
+ )
5120
+ ),
5121
+ 'plan_purchased',
5122
+ $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'
5123
+ );
5124
+ }
5125
+
5126
  if ( $this->is_addon() ) {
5127
  if ( ! $this->is_parent_plugin_installed() ) {
5128
  $parent_name = $this->get_option( $plugin_info, 'parent_name', null );
5227
  * because the updater has some logic that needs to be executed
5228
  * during AJAX calls.
5229
  *
5230
+ * Currently, we need to hook to the `http_request_host_is_external` filter.
5231
  * In the future, there might be additional logic added.
5232
  *
5233
  * @author Vova Feldman
5246
  */
5247
  ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->premium_plugin_basename() ) ) )
5248
  ) &&
5249
+ $this->has_release_on_freemius() &&
5250
+ ( ! $this->is_unresolved_clone( true ) )
5251
  ) {
5252
  FS_Plugin_Updater::instance( $this );
5253
  }
5316
  }
5317
 
5318
  /**
5319
+ * @param string[] $permissions
5320
+ * @param bool $is_enabled
5321
+ * @param int|null $blog_id
5322
  *
5323
+ * @return true|object `true` on success, API error object on failure.
5324
  */
5325
+ private function update_site_permissions( array $permissions, $is_enabled, $blog_id = null ) {
5326
  $this->_logger->entrance();
5327
 
5328
+ $params = array(
5329
+ 'permissions' => implode( ',', $permissions ),
5330
+ 'is_enabled' => $is_enabled,
 
 
 
 
 
 
 
 
 
 
 
 
5331
  );
 
 
 
 
 
 
 
 
 
 
 
 
5332
 
5333
+ $current_blog_id = get_current_blog_id();
5334
+ $is_blog_switched = false;
5335
+ if ( is_numeric( $blog_id ) && $current_blog_id != $blog_id ) {
5336
+ $is_blog_switched = $this->switch_to_blog( $blog_id );
5337
  }
5338
 
5339
+ $result = $this->api_site_call( '/permissions.json', 'put', $params );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5340
 
5341
+ if ( $is_blog_switched ) {
5342
+ $this->switch_to_blog( $current_blog_id );
 
5343
  }
5344
 
5345
+ if (
5346
+ ! $this->is_api_result_object( $result ) ||
5347
+ ! isset( $result->install_id )
 
 
 
 
 
5348
  ) {
5349
  $this->_logger->api_error( $result );
5350
 
5351
  return $result;
5352
  }
5353
 
 
 
 
 
 
 
5354
  return true;
5355
  }
5356
 
5357
  /**
5358
+ * @param string[] $permissions
5359
+ * @param bool $is_enabled
5360
+ * @param bool $has_site_delegated_connection
5361
  *
5362
+ * @return true|object `true` on success, API error object on failure.
 
 
 
 
 
 
 
 
5363
  */
5364
+ private function update_network_permissions(
5365
+ array $permissions,
5366
+ $is_enabled,
5367
+ &$has_site_delegated_connection
5368
+ ) {
5369
  $this->_logger->entrance();
5370
 
 
 
 
 
 
5371
  $install_id_2_blog_id = array();
5372
+ $install_by_blog_id = $this->get_blog_install_map();
5373
 
5374
+ $has_site_delegated_connection = false;
 
 
 
 
 
 
 
5375
 
5376
+ foreach ( $install_by_blog_id as $blog_id => $install ) {
5377
  if ( $this->is_site_delegated_connection( $blog_id ) ) {
5378
+ // Only update permissions of non-delegated installs.
5379
+ $has_site_delegated_connection = true;
5380
  continue;
5381
  }
5382
 
 
 
5383
  $install_id_2_blog_id[ $install->id ] = $blog_id;
5384
  }
5385
 
5387
  return true;
5388
  }
5389
 
5390
+ $params = array(
5391
+ 'permissions' => implode( ',', $permissions ),
5392
+ 'is_enabled' => $is_enabled,
5393
+ 'install_ids' => implode( ',', array_keys( $install_id_2_blog_id ) ),
5394
+ );
5395
 
5396
  // Send update to FS.
5397
+ $result = $this->get_current_or_network_user_api_scope()->call(
5398
+ "/plugins/{$this->_module_id}/installs/permissions.json",
5399
+ 'put',
5400
+ $params
5401
+ );
5402
 
5403
+ if ( ! $this->is_api_result_object( $result, 'installs_metadata' ) ) {
5404
  $this->_logger->api_error( $result );
5405
 
5406
  return $result;
5407
  }
5408
 
 
 
 
 
 
 
 
 
 
 
5409
  return true;
5410
  }
5411
 
5412
  /**
5413
+ * @param mixed $result
 
 
 
 
 
 
 
 
 
 
 
 
5414
  *
5415
+ * @return string
5416
  */
5417
+ private function get_api_error_message( $result ) {
5418
+ $error_message = sprintf( $this->get_text_inline( 'There was an unexpected API error while processing your request. Please try again in a few minutes and if it still doesn\'t work, contact the %s\'s author with the following:',
5419
+ 'unexpected-api-error' ), $this->_module_type ) . ' ';
5420
+
5421
+ if (
5422
+ $this->is_api_error( $result ) &&
5423
+ isset( $result->error )
5424
+ ) {
5425
+ $code = empty( $result->error->code ) ? '' : " Code: {$result->error->code}";
5426
+
5427
+ $error_message .= "<b>{$result->error->message}{$code}</b>";
5428
+ } else {
5429
+ $error_message .= var_export( $result, true );
5430
+ }
5431
 
5432
+ return $error_message;
 
 
5433
  }
5434
 
5435
  /**
5436
+ * @author Vova Feldman (@svovaf)
5437
+ * @since 2.5.1
 
 
 
 
 
 
 
 
 
 
 
5438
  */
5439
+ function _toggle_permission_tracking_callback() {
5440
  $this->_logger->entrance();
5441
 
5442
+ $this->check_ajax_referer( 'toggle_permission_tracking' );
 
 
 
5443
 
5444
+ if ( ! $this->is_registered( true ) ) {
5445
+ self::shoot_ajax_failure( 'User never opted-in.' );
 
5446
  }
5447
 
5448
+ $is_enabled = fs_request_get_bool( 'is_enabled' );
5449
+ $permissions = fs_request_get( 'permissions' );
 
 
 
 
 
 
 
5450
 
5451
+ if ( ! is_string( $permissions ) ) {
5452
+ self::shoot_ajax_failure( 'The permissions param must be a string.' );
5453
  }
5454
 
5455
+ $permissions = explode( ',', $permissions );
 
5456
 
5457
+ $result = $this->toggle_permission_tracking( $permissions, $is_enabled );
5458
 
5459
+ if ( true !== $result ) {
5460
+ self::shoot_ajax_failure( $this->get_api_error_message( $result ) );
5461
+ }
5462
+
5463
+ self::shoot_ajax_success();
5464
  }
5465
 
5466
  /**
5467
+ * @param string[] $permissions
5468
+ * @param bool $is_enabled
5469
+ * @param int|null $blog_id
 
 
 
 
 
 
 
 
5470
  *
5471
+ * @return bool|mixed `true` if updated successfully or no update is needed.
5472
  */
5473
+ private function toggle_permission_tracking( $permissions, $is_enabled, $blog_id = null ) {
5474
+ if ( ! $this->is_registered( true ) ) {
 
 
5475
  // User never opted-in.
5476
  return false;
5477
  }
5478
 
5479
+ // Check if permissions are already set as needed.
5480
+ if ( FS_Permission_Manager::instance( $this )->are_permissions( $permissions, $is_enabled, $blog_id ) ) {
5481
+ /**
5482
+ * Note:
5483
+ * When running on the network admin, there's no need to iterate through all the installs individually since network opt-in permissions are managed for ALL non-delegated installs through a single option (per permission) on the network-level storage.
5484
+ */
5485
+ return true;
 
 
 
 
 
5486
  }
5487
 
5488
+ $api_managed_permissions = array_intersect(
5489
+ $permissions,
5490
+ FS_Permission_Manager::get_api_managed_permission_ids()
5491
+ );
5492
+
5493
+ if (
5494
+ in_array( FS_Permission_Manager::PERMISSION_ESSENTIALS, $permissions ) &&
5495
+ ! in_array( FS_Permission_Manager::PERMISSION_SITE, $permissions )
5496
+ ) {
5497
+ $api_managed_permissions[] = FS_Permission_Manager::PERMISSION_SITE;
5498
  }
5499
 
5500
+ if ( ! empty( $api_managed_permissions ) ) {
5501
+ $has_site_delegated_connection = false;
5502
 
5503
+ if (
5504
+ ! $is_enabled &&
5505
+ ! in_array( FS_Permission_Manager::PERMISSION_EXTENSIONS, $api_managed_permissions ) &&
5506
+ false === FS_Permission_Manager::instance( $this )->is_extensions_tracking_allowed( $blog_id )
5507
+ ) {
5508
+ /**
5509
+ * If we are turning off a permission and the extensions permission is off too, enrich the permissions update request to also turn off extensions tracking, as currently when opting in with extensions tracking disabled the extensions tracking is off but the API isn't aware of it.
5510
+ *
5511
+ * @todo Remove this entire `if` after implementing granular opt-in that also sends the permissions to the API when opting in.
5512
+ */
5513
+ $api_managed_permissions[] = FS_Permission_Manager::PERMISSION_EXTENSIONS;
5514
+ }
5515
 
5516
+ if ( is_null( $blog_id ) && fs_is_network_admin() ) {
5517
+ $result = $this->update_network_permissions(
5518
+ $api_managed_permissions,
5519
+ $is_enabled,
5520
+ $has_site_delegated_connection
5521
+ );
5522
+ } else {
5523
+ $result = $this->update_site_permissions(
5524
+ $api_managed_permissions,
5525
+ $is_enabled,
5526
+ $blog_id
5527
+ );
5528
+ }
5529
 
5530
+ if ( true !== $result ) {
5531
+ return $result;
5532
+ }
5533
 
5534
+ if ( in_array( FS_Permission_Manager::PERMISSION_SITE, $api_managed_permissions ) ) {
5535
+ if ( $is_enabled ) {
5536
+ $this->schedule_sync_cron();
5537
+ } else {
5538
+ $this->clear_sync_cron( ! $has_site_delegated_connection );
5539
+ }
5540
+ }
5541
 
5542
+ if ( in_array( FS_Permission_Manager::PERMISSION_USER, $api_managed_permissions ) ) {
5543
+ $this->toggle_user_permission( $is_enabled, $blog_id );
5544
+ }
 
 
5545
  }
5546
 
5547
+ $this->update_tracking_permissions(
5548
+ $permissions,
5549
+ $is_enabled,
5550
+ $blog_id
5551
+ );
5552
 
 
5553
  return true;
5554
  }
5555
 
5556
+ /**
5557
+ * @param bool $is_enabled
5558
+ * @param int|null $blog_id
5559
+ */
5560
+ private function toggle_user_permission( $is_enabled, $blog_id = null ) {
5561
+ $network_or_blog_ids = is_numeric( $blog_id ) ?
5562
+ $blog_id :
5563
+ fs_is_network_admin();
5564
+
5565
+ if ( $is_enabled ) {
5566
+ $this->reset_anonymous_mode( $network_or_blog_ids );
5567
+ } else {
5568
+ $this->skip_connection( $network_or_blog_ids );
5569
+ }
5570
+ }
5571
+
5572
  /**
5573
  * Opt-in back into usage tracking.
5574
  *
5582
  * @author Leo Fajardo (@leorw)
5583
  * @since 1.2.1.5
5584
  *
5585
+ * @bool $is_enabled
5586
  *
5587
  * @return bool|object
5588
  */
5589
+ private function toggle_site_tracking( $is_enabled, $blog_id = null ) {
5590
  $this->_logger->entrance();
5591
 
5592
+ return $this->toggle_permission_tracking(
5593
+ FS_Permission_Manager::instance( $this )->get_site_tracking_permission_names(),
5594
+ $is_enabled,
5595
+ $blog_id
5596
+ );
5597
  }
5598
 
5599
  /**
5614
 
5615
  if ( ! fs_is_network_admin() || $is_context_single_site ) {
5616
  if ( $this->is_tracking_prohibited() ) {
5617
+ FS_Permission_Manager::instance( $this )->update_site_tracking( true );
 
5618
  }
5619
  } else {
5620
  $installs_map = $this->get_blog_install_map();
5622
  /**
5623
  * @var FS_Site $install
5624
  */
5625
+ if ( ! $this->is_tracking_allowed( $blog_id, $install ) ) {
5626
+ FS_Permission_Manager::instance( $this )->update_site_tracking( true, $blog_id );
 
5627
  }
5628
  }
5629
  }
5630
  }
5631
 
5632
  /**
5633
+ * Update permission tracking flags. When updating in a network context, in addition to updating the network-level flags, also update the permissions on the site-level for all non-delegated sites.
 
5634
  *
5635
+ * @param string[] $permissions
5636
+ * @param bool $is_enabled
5637
+ * @param int|null $blog_id
5638
+ *
5639
+ * @return array
5640
  */
5641
+ private function update_tracking_permissions( $permissions, $is_enabled, $blog_id = null ) {
5642
+ // Alias.
5643
+ $permission_manager = FS_Permission_Manager::instance( $this );
 
 
 
5644
 
5645
+ $network_or_blog_ids = is_numeric( $blog_id ) ?
5646
+ $blog_id :
5647
+ fs_is_network_admin();
 
 
 
5648
 
5649
+ if ( true === $network_or_blog_ids ) {
5650
+ // Update the permission for all non-delegated sub-sites.
5651
+ $blog_ids = $this->get_non_delegated_blog_ids();
5652
 
5653
+ // Add the network-level to the array, to update the permission on the network-level storage.
5654
+ array_unshift( $blog_ids, null );
5655
+ }
5656
+ else
5657
+ {
5658
+ if ( false === $network_or_blog_ids ) {
5659
+ $network_or_blog_ids = null;
5660
+ }
5661
 
5662
+ $blog_ids = is_array( $network_or_blog_ids ) ?
5663
+ $network_or_blog_ids :
5664
+ array( $network_or_blog_ids );
5665
  }
5666
 
5667
+ $result = array();
5668
+ foreach ( $permissions as $permission ) {
5669
+ $permission = trim( $permission );
5670
+ $is_permission_supported = true;
5671
+
5672
+ foreach ( $blog_ids as $id ) {
5673
+ $is_permission_supported = $permission_manager->update_permission_tracking_flag(
5674
+ $permission,
5675
+ $is_enabled,
5676
+ $id
5677
+ );
5678
+ }
5679
 
5680
+ if ( ! $is_permission_supported ) {
 
 
 
 
5681
  $permission = 'no_match';
5682
+ }
5683
 
5684
+ $result[ $permission ] = $is_enabled;
 
5685
  }
5686
 
5687
+ return $result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5688
  }
5689
 
5690
  /**
5890
  if ( $this->is_activation_mode() ) {
5891
  if ( ! is_admin() ) {
5892
  /**
5893
+ * If in activation mode, don't execute Freemius outside the admin dashboard.
 
5894
  *
5895
  * @author Vova Feldman (@svovaf)
5896
  * @since 1.1.7.3
6437
  if ( ! isset( $this->_is_anonymous ) ) {
6438
  if ( $this->is_network_anonymous() ) {
6439
  $this->_is_anonymous = true;
6440
+ } else if ( fs_is_network_admin() ) {
6441
+ /**
6442
+ * When not-network-anonymous, yet, running in the network admin, consider as anonymous only when ALL non-delegated sites are set to anonymous.
6443
+ */
6444
+ $non_delegated_sites = $this->get_non_delegated_blog_ids();
6445
+
6446
+ foreach ( $non_delegated_sites as $blog_id ) {
6447
+ $is_anonymous = $this->_storage->get( 'is_anonymous', false, $blog_id );
6448
+
6449
+ if ( empty( $is_anonymous ) || false === $is_anonymous[ 'is' ] ) {
6450
+ $this->_is_anonymous = false;
6451
+ break;
6452
+ }
6453
+ }
6454
+
6455
+ if ( false !== $this->_is_anonymous ) {
6456
+ $this->_is_anonymous = true;
6457
+ }
6458
+ } else {
6459
  if ( ! isset( $this->_storage->is_anonymous ) ) {
6460
  // Not skipped.
6461
  $this->_is_anonymous = false;
6511
  return $this->_storage->get( 'is_pending_activation', false );
6512
  }
6513
 
6514
+ /**
6515
+ * @author Leo Fajardo (@leorw)
6516
+ * @since 2.5.0
6517
+ */
6518
+ private function clear_pending_activation_mode() {
6519
+ // Remove the pending activation sticky notice (if it still exists).
6520
+ $this->_admin_notices->remove_sticky( 'activation_pending' );
6521
+
6522
+ // Clear the plugin's pending activation mode.
6523
+ unset( $this->_storage->is_pending_activation );
6524
+ }
6525
+
6526
  /**
6527
  * Check if plugin must be WordPress.org compliant.
6528
  *
6599
  private function get_cron_blog_id( $name ) {
6600
  $this->_logger->entrance( $name );
6601
 
6602
+ if ( ! is_multisite() ) {
6603
+ // Not a multisite.
6604
+ return 0;
6605
+ }
6606
+
6607
  $cron_data = $this->get_cron_data( $name );
6608
 
6609
  return ( is_object( $cron_data ) && is_numeric( $cron_data->blog_id ) ) ?
6714
  return 0;
6715
  }
6716
 
6717
+ if ( $this->_is_network_active ) {
6718
+ $network_install_blog_id = $this->_storage->network_install_blog_id;
 
 
 
 
 
6719
 
6720
+ if (
6721
+ is_numeric( $network_install_blog_id ) &&
6722
+ $except_blog_id != $network_install_blog_id &&
6723
+ self::is_site_active( $network_install_blog_id )
6724
  ) {
6725
+ // Try to run cron from the main network blog.
6726
+ $install = $this->get_install_by_blog_id( $network_install_blog_id );
6727
+
6728
+ if (
6729
+ is_object( $install ) &&
6730
+ $this->is_tracking_allowed( $network_install_blog_id, $install )
6731
+ ) {
6732
+ return $network_install_blog_id;
6733
+ }
6734
  }
6735
  }
6736
 
6739
  foreach ( $installs as $blog_id => $install ) {
6740
  if ( $except_blog_id != $blog_id &&
6741
  self::is_site_active( $blog_id ) &&
6742
+ $this->is_tracking_allowed( $blog_id, $install )
6743
  ) {
6744
  return $blog_id;
6745
  }
6771
  /**
6772
  * @var FS_Site $install
6773
  */
6774
+ if ( $this->is_tracking_allowed( $blog_id, $install ) ) {
6775
  $clear_cron = false;
6776
  break;
6777
  }
6782
  return;
6783
  }
6784
 
6785
+ $cron_blog_id = $this->get_cron_blog_id( $name );
 
 
 
 
 
 
 
6786
 
6787
  $this->clear_cron_data( $name );
6788
 
6819
  return false;
6820
  }
6821
 
6822
+ $cron_blog_id = $this->get_cron_blog_id( $name );
 
 
 
 
 
 
 
6823
 
6824
  if ( 0 < $cron_blog_id ) {
6825
  switch_to_blog( $cron_blog_id );
6930
  } else {
6931
  $installs = $this->get_blog_install_map();
6932
  foreach ( $installs as $blog_id => $install ) {
6933
+ if ( $this->is_tracking_allowed( $blog_id, $install ) ) {
6934
  if ( ! isset( $users_2_blog_ids[ $install->user_id ] ) ) {
6935
  $users_2_blog_ids[ $install->user_id ] = array();
6936
  }
6995
  * @since 1.1.7.3
6996
  */
6997
  private function run_manual_sync() {
 
 
6998
  if ( ! $this->is_user_admin() ) {
6999
  return;
7000
  }
7078
  return $this->is_cron_on( 'sync' );
7079
  }
7080
 
7081
+ /**
7082
+ * @author Leo Fajardo (@leorw)
7083
+ * @since 2.5.0
7084
+ */
7085
+ private function maybe_schedule_sync_cron() {
7086
+ $next_schedule = $this->next_sync_cron();
7087
+
7088
+ // The event is properly scheduled, so no need to reschedule it.
7089
+ if (
7090
+ is_numeric( $next_schedule ) &&
7091
+ $next_schedule > time()
7092
+ ) {
7093
+ return;
7094
+ }
7095
+
7096
+ $this->schedule_sync_cron();
7097
+ }
7098
+
7099
  /**
7100
  * @author Vova Feldman (@svovaf)
7101
  * @since 1.1.7.3
7202
  * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding excluded specified blog ID from being the cron executor.
7203
  */
7204
  private function schedule_install_sync( $except_blog_id = 0 ) {
7205
+ if ( $this->is_clone() ) {
7206
+ return;
7207
+ }
7208
+
7209
  $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id );
7210
  }
7211
 
7294
  /**
7295
  * Show a notice that activation is currently pending.
7296
  *
7297
+ * @todo Add some sort of mechanism to allow users to update the email address they would like to opt-in with when $is_suspicious_email is true.
7298
+ *
7299
  * @author Vova Feldman (@svovaf)
7300
  * @since 1.0.7
7301
  *
7302
  * @param bool|string $email
7303
  * @param bool $is_pending_trial Since 1.2.1.5
7304
+ * @param bool $is_suspicious_email Since 2.5.0 Set to true when there's an indication that email address the user opted in with is fake/dummy/placeholder.
7305
  */
7306
+ function _add_pending_activation_notice(
7307
+ $email = false,
7308
+ $is_pending_trial = false,
7309
+ $is_suspicious_email = false
7310
+ ) {
7311
  if ( ! is_string( $email ) ) {
7312
  $current_user = self::_get_current_wp_user();
7313
  $email = $current_user->user_email;
7315
 
7316
  $this->_admin_notices->add_sticky(
7317
  sprintf(
7318
+ $this->get_text_inline( 'You should receive a confirmation email for %s to your mailbox at %s. Please make sure you click the button in that email to %s.', 'pending-activation-message' ),
7319
  '<b>' . $this->get_plugin_name() . '</b>',
7320
  '<b>' . $email . '</b>',
7321
  ( $is_pending_trial ?
7322
  $this->get_text_inline( 'start the trial', 'start-the-trial' ) :
7323
+ $this->get_text_inline( 'complete the opt-in', 'complete-the-opt-in' ) )
7324
  ),
7325
  'activation_pending',
7326
  'Thanks!'
7363
  /**
7364
  * Don't redirect if activating multiple plugins at once (bulk activation).
7365
  */
7366
+ } else if (
7367
+ self::is_deactivation_snoozed() &&
7368
+ (
7369
+ // Either running the free code base.
7370
+ ! $this->is_premium() ||
7371
+ // Or if has a free version.
7372
+ ! $this->is_only_premium() ||
7373
+ // If premium only, don't redirect if license is activated.
7374
+ ( $this->is_registered() && ! $this->can_use_premium_code() )
7375
+ )
7376
+ ) {
7377
+ /**
7378
+ * Don't redirect if activating during the deactivation snooze period (aka troubleshooting), unless activating a paid product version that the admin didn't enter its license key yet.
7379
+ */
7380
  } else if ( ! $is_migration ) {
7381
  $this->_redirect_on_activation_hook();
7382
  return;
7390
  if ( fs_request_is_action( $this->get_unique_affix() . '_skip_activation' ) ) {
7391
  check_admin_referer( $this->get_unique_affix() . '_skip_activation' );
7392
 
7393
+ $this->skip_connection( fs_is_network_admin() );
7394
 
7395
  fs_redirect( $this->get_after_activation_url( 'after_skip_url' ) );
7396
  }
7563
 
7564
  fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.min.js' );
7565
  fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' );
 
 
7566
  }
7567
 
7568
  /**
7598
  <?php
7599
  echo $this->apply_filters( 'optin_pointer_execute', "
7600
 
7601
+ optin.pointer('open');
7602
 
7603
+ // Tag the opt-in pointer with custom class.
7604
+ $('.wp-pointer #fs_connect')
7605
+ .parents('.wp-pointer.wp-pointer-top')
7606
+ .addClass('fs-opt-in-pointer');
7607
 
7608
+ ", 'element', 'optin' ) ?>
7609
  }
7610
  }
7611
  });
7621
  *
7622
  * @return string
7623
  */
7624
+ static function current_page_url() {
7625
  $url = 'http';
7626
 
7627
  if ( isset( $_SERVER["HTTPS"] ) ) {
7653
  }
7654
 
7655
  /* Events
7656
+ ------------------------------------------------------------------------------------------------------------------*/
7657
  /**
7658
  * Delete site install from Database.
7659
  *
7819
  * @author Leo Fajardo (@leorw)
7820
  * @since 1.2.2
7821
  *
7822
+ * @return bool
7823
+ */
7824
+ private function can_activate_previous_theme() {
7825
+ return $this->can_activate_theme( $this->get_previous_theme_slug() );
7826
+ }
7827
+
7828
+ /**
7829
+ * @author Leo Fajardo (@leorw)
7830
+ * @since 2.5.0
7831
+ *
7832
+ * @return bool
7833
  */
7834
+ private function can_activate_theme( $slug ) {
 
7835
  if ( false !== $slug && current_user_can( 'switch_themes' ) ) {
7836
  $theme_instance = wp_get_theme( $slug );
7837
 
7928
  ( current_filter() !== ( 'activate_' . $this->_free_plugin_basename ) ) :
7929
  $this->is_premium();
7930
 
7931
+ if ( $is_premium_version_activation && $this->is_pending_activation() ) {
7932
+ $this->clear_pending_activation_mode();
7933
+ }
7934
+
7935
  $this->_logger->info( 'Activating ' . ( $is_premium_version_activation ? 'premium' : 'free' ) . ' plugin version.' );
7936
 
7937
  if ( $this->is_plugin() ) {
7998
  $plugin_version = $this->_storage->is_anonymous_ms['version'];
7999
  $network = true;
8000
  } else {
8001
+ $plugin_version = isset( $this->_storage->is_anonymous ) ?
8002
+ $this->_storage->is_anonymous['version'] :
8003
+ null;
8004
  $network = false;
8005
  }
8006
 
8148
  );
8149
  } else {
8150
  // Activate the license.
8151
+ $install = $this->api_site_call(
8152
  '/',
8153
  'put',
8154
  array( 'license_key' => $this->apply_filters( 'license_key', $license->secret_key ) )
8807
  $this->_is_anonymous = $is_anonymous;
8808
  }
8809
 
8810
+ /**
8811
+ * @author Vova Feldman (@svovaf)
8812
+ * @since 2.5.1
8813
+ *
8814
+ * @param bool|int $network_or_blog_id
8815
+ */
8816
+ private function unset_anonymous_mode( $network_or_blog_id = 0 ) {
8817
+ if ( true === $network_or_blog_id ) {
8818
+ unset( $this->_storage->is_anonymous_ms );
8819
+ } else {
8820
+ $this->_storage->remove( 'is_anonymous', true, $network_or_blog_id );
8821
+ }
8822
+ }
8823
+
8824
  /**
8825
  * @author Vova Feldman (@svovaf)
8826
  * @since 2.0.0
8837
  * @uses Freemius::is_network_anonymous() to check if the super-admin network skipped.
8838
  * @uses Freemius::is_network_delegated_connection() to check if the super-admin network delegated the connection to the site admins.
8839
  */
8840
+ public function _after_new_blog_callback( $blog_id, $user_id, $domain, $path, $network_id, $meta ) {
8841
  $this->_logger->entrance();
8842
 
8843
+ if ( ! $this->_is_network_active ) {
8844
+ FS_Clone_Manager::instance()->store_blog_install_info( $blog_id );
8845
+ return;
8846
+ }
8847
+
8848
+ $site = null;
8849
+ $new_blog_id = $blog_id;
8850
+
8851
  if ( $this->is_premium() &&
8852
  $this->is_network_connected() &&
8853
  is_object( $this->_license ) &&
8881
  }
8882
  }
8883
 
8884
+ $site = $this->_site;
8885
+
8886
  $this->switch_to_blog( $current_blog_id );
8887
 
8888
+ if ( is_object( $site ) ) {
8889
+ FS_Clone_Manager::instance()->store_blog_install_info( $blog_id, $site );
8890
+
8891
  // Already connected (with or without a license), so no need to continue.
8892
  return;
8893
  }
8920
  false
8921
  );
8922
 
8923
+ $site = $this->_site;
8924
+
8925
  $this->switch_to_blog( $current_blog_id );
8926
  } else {
8927
  /**
8932
  $has_delegated_site = false;
8933
 
8934
  $sites = self::get_sites();
8935
+ foreach ( $sites as $wp_site ) {
8936
+ $blog_id = self::get_site_blog_id( $wp_site );
8937
 
8938
  if ( $this->is_site_delegated_connection( $blog_id ) ) {
8939
  $has_delegated_site = true;
8947
  $this->skip_site_connection( $blog_id );
8948
  }
8949
  }
8950
+
8951
+ /**
8952
+ * Store the new blog's information even if there's no install so that when a clone install is stored in the new blog's storage, we can try to resolve it automatically.
8953
+ *
8954
+ * @author Leo Fajardo (@leorw)
8955
+ * @since 2.5.0
8956
+ */
8957
+ FS_Clone_Manager::instance()->store_blog_install_info( $new_blog_id, $site );
8958
+ }
8959
+
8960
+ /**
8961
+ * @author Vova Feldman (@svovaf)
8962
+ * @since 2.5.0
8963
+ *
8964
+ * @param \WP_Site $new_site
8965
+ * @param array $args
8966
+ */
8967
+ public function _after_wp_initialize_site_callback( WP_Site $new_site, $args ) {
8968
+ $this->_logger->entrance();
8969
+
8970
+ $this->_after_new_blog_callback(
8971
+ $new_site->id,
8972
+ // Dummy user ID (not in use).
8973
+ 0,
8974
+ $new_site->domain,
8975
+ $new_site->path,
8976
+ $new_site->network_id,
8977
+ // Dummy meta, not in use.
8978
+ array()
8979
+ );
8980
  }
8981
 
8982
  /**
8983
  * @author Vova Feldman (@svovaf)
8984
  * @since 1.1.3
8985
  *
8986
+ * @param bool|int|int[] $network_or_blog_ids Since 2.0.0.
8987
  */
8988
+ private function reset_anonymous_mode( $network_or_blog_ids = false ) {
8989
+ if ( true === $network_or_blog_ids ) {
8990
+ $this->unset_anonymous_mode( true );
8991
+
8992
+ if ( fs_is_network_admin() ) {
8993
+ $this->_is_anonymous = null;
8994
+ }
8995
+
8996
+ // Rest anonymous mode for all non-delegated sub-sites.
8997
+ $blog_ids = $this->get_non_delegated_blog_ids();
8998
+ }
8999
+ else
9000
+ {
9001
+ if ( false === $network_or_blog_ids ) {
9002
+ $network_or_blog_ids = 0;
9003
+ }
9004
+
9005
+ $blog_ids = is_array( $network_or_blog_ids ) ?
9006
+ $network_or_blog_ids :
9007
+ array( $network_or_blog_ids );
9008
+
9009
+ foreach ( $blog_ids as $blog_id ) {
9010
+ if ( 0 === $blog_id || get_current_blog_id() == $blog_id ) {
9011
+ $this->_is_anonymous = null;
9012
+ }
9013
+ }
9014
+ }
9015
+
9016
+ foreach ( $blog_ids as $blog_id ) {
9017
+ $this->unset_anonymous_mode( $blog_id );
9018
  }
9019
 
9020
  /**
9025
  * @author Leo Fajardo (@leorw)
9026
  * @since 1.2.2
9027
  */
9028
+ if ( ! $this->_is_network_active ) {
 
 
 
 
9029
  $this->_is_anonymous = null;
9030
  }
9031
  }
9071
  * @author Vova Feldman (@svovaf)
9072
  * @since 1.1.1
9073
  *
9074
+ * @param bool|int|int[] $network_or_blog_ids Since 2.5.1
 
9075
  */
9076
+ function skip_connection( $network_or_blog_ids = false ) {
9077
  $this->_logger->entrance();
9078
 
9079
  $this->_admin_notices->remove_sticky( 'connect_account' );
9080
 
9081
+ if ( true === $network_or_blog_ids ) {
9082
  $this->set_anonymous_mode( true, true );
 
9083
 
9084
+ if ( fs_is_network_admin() ) {
9085
+ $this->_is_anonymous = null;
9086
+ }
 
9087
 
9088
+ // Rest anonymous mode for all non-delegated sub-sites.
9089
+ $blog_ids = $this->get_non_delegated_blog_ids();
9090
+ }
9091
+ else
9092
+ {
9093
+ if ( false === $network_or_blog_ids ) {
9094
+ $network_or_blog_ids = 0;
9095
+ }
9096
 
9097
+ $blog_ids = is_array( $network_or_blog_ids ) ?
9098
+ $network_or_blog_ids :
9099
+ array( $network_or_blog_ids );
9100
+
9101
+ foreach ( $blog_ids as $blog_id ) {
9102
+ if ( 0 === $blog_id || get_current_blog_id() == $blog_id ) {
9103
+ $this->_is_anonymous = null;
 
 
 
9104
  }
9105
  }
9106
+ }
9107
 
9108
+ foreach ( $blog_ids as $blog_id ) {
9109
+ $this->skip_site_connection( $blog_id );
 
 
 
9110
  }
9111
 
9112
  $this->network_upgrade_mode_completed();
9121
  * @param int|null $blog_id
9122
  * @param bool $send_skip
9123
  */
9124
+ private function skip_site_connection( $blog_id = null ) {
9125
  $this->_logger->entrance();
9126
 
9127
  $this->_admin_notices->remove_sticky( 'connect_account', $blog_id );
9128
 
9129
  $this->set_anonymous_mode( true, $blog_id );
 
 
 
 
 
 
9130
  }
9131
 
9132
  /**
9498
  * @param string[] $override
9499
  * @param bool $include_plugins Since 1.1.8 by default include plugin changes.
9500
  * @param bool $include_themes Since 1.1.8 by default include plugin changes.
9501
+ * @param bool $include_blog_data Since 2.3.0 by default include the current blog's data (language, title, and URL).
9502
  *
9503
  * @return array
9504
  */
9508
  $include_themes = true,
9509
  $include_blog_data = true
9510
  ) {
9511
+ // Alias.
9512
+ $permissions = FS_Permission_Manager::instance( $this );
9513
+
9514
+ if ( $permissions->is_extensions_tracking_allowed() ) {
9515
  if ( ! defined( 'WP_FS__TRACK_PLUGINS' ) || false !== WP_FS__TRACK_PLUGINS ) {
9516
  /**
9517
  * @since 1.1.8 Also send plugin updates.
9539
 
9540
  $versions = $this->get_versions();
9541
 
9542
+ $blog_data = array();
9543
+ if ( $include_blog_data ) {
9544
+ $blog_data['url'] = self::get_unfiltered_site_url();
9545
+
9546
+ if ( $permissions->is_diagnostic_tracking_allowed() ) {
9547
+ $blog_data = array_merge( $blog_data, array(
9548
+ 'language' => self::get_sanitized_language(),
9549
+ 'title' => get_bloginfo( 'name' ),
9550
+ ) );
9551
+ }
9552
+ }
9553
 
9554
  return array_merge( $versions, $blog_data, array(
9555
  'version' => $this->get_plugin_version(),
9556
  'is_premium' => $this->is_premium(),
9557
  // Special params.
9558
  'is_active' => true,
 
9559
  'is_uninstalled' => false,
9560
  ), $override );
9561
  }
9570
  *
9571
  * @param string[] string $override
9572
  * @param bool $only_diff
9573
+ * @param bool $is_keepalive
9574
  * @param bool $include_plugins Since 1.1.8 by default include plugin changes.
9575
  * @param bool $include_themes Since 1.1.8 by default include plugin changes.
9576
  *
9579
  private function get_installs_data_for_api(
9580
  array $override,
9581
  $only_diff = false,
9582
+ $is_keepalive = false,
9583
  $include_plugins = true,
9584
  $include_themes = true
9585
  ) {
9617
 
9618
  $sites = self::get_sites();
9619
 
9620
+ $subsite_data_for_api_by_install_id = array();
9621
+ $install_url_by_install_id = array();
9622
+ $subsite_registration_date_by_install_id = array();
9623
+
9624
  foreach ( $sites as $site ) {
9625
  $blog_id = self::get_site_blog_id( $site );
9626
 
9632
  continue;
9633
  }
9634
 
9635
+ if ( ! $this->is_tracking_allowed( $blog_id, $install ) ) {
9636
  // Don't send updates regarding opted-out installs.
9637
  continue;
9638
  }
9639
 
9640
+ $install_data = $this->get_site_info( $site, true );
9641
+
9642
+ if ( FS_Clone_Manager::instance()->is_temporary_duplicate_by_blog_id( $install_data['blog_id'] ) ) {
9643
+ continue;
9644
+ }
9645
+
9646
+ $uid = $install_data['uid'];
9647
+ $url = $install_data['url'];
9648
+ $registration_date = $install_data['registration_date'];
9649
+
9650
+ if ( isset( $subsite_data_for_api_by_install_id[ $install->id ] ) ) {
9651
+ $clone_subsite_data = $subsite_data_for_api_by_install_id[ $install->id ];
9652
+ $clone_install_url = $install_url_by_install_id[ $install->id ];
9653
+ $clone_subsite_registration_date = $subsite_registration_date_by_install_id[ $install->id ];
9654
+
9655
+ $skip = false;
9656
+
9657
+ if (
9658
+ ! empty( $install_data['registration_date'] ) &&
9659
+ ! empty( $clone_subsite_registration_date )
9660
+ ) {
9661
+ /**
9662
+ * If the current subsite was created after the other subsite that is also linked to the same install ID, we assume that it's a clone (not the original), and therefore, would skip its processing.
9663
+ *
9664
+ * @author Leo Fajardo (@leorw)
9665
+ * @since 2.5.1
9666
+ */
9667
+ $skip = ( strtotime( $install_data['registration_date'] ) > strtotime( $clone_subsite_registration_date ) );
9668
+ } else if (
9669
+ /**
9670
+ * If we already have an install with the same URL as the subsite it's stored in, skip the current subsite. Otherwise, replace the existing install's data with the current subsite's install's data if the URLs match.
9671
+ *
9672
+ * @author Leo Fajardo (@leorw)
9673
+ * @since 2.5.0
9674
+ */
9675
+ fs_strip_url_protocol( untrailingslashit( $clone_install_url ) ) === fs_strip_url_protocol( untrailingslashit( $clone_subsite_data['url'] ) ) ||
9676
+ fs_strip_url_protocol( untrailingslashit( $install->url ) ) !== fs_strip_url_protocol( untrailingslashit( $url ) )
9677
+ ) {
9678
+ $skip = true;
9679
+ }
9680
 
9681
+ if ( $skip ) {
9682
+ // Store the skipped subsite's ID so that the clone resolution manager can try to resolve the clone install that is stored in that subsite later on.
9683
+ FS_Clone_Manager::instance()->store_blog_install_info( $blog_id );
9684
+ continue;
9685
+ }
9686
+ }
9687
 
9688
  unset( $install_data['blog_id'] );
9689
  unset( $install_data['uid'] );
9690
+ unset( $install_data['url'] );
9691
+ unset( $install_data['registration_date'] );
9692
 
 
9693
  $install_data['is_active'] = $this->is_active_for_site( $blog_id );
9694
  $install_data['is_uninstalled'] = $install->is_uninstalled;
9695
 
9712
  $is_common_diff_for_any_site = $is_common_diff_for_any_site || $is_common_diff;
9713
  }
9714
 
9715
+ if ( ! empty( $install_data ) || $is_common_diff || $is_keepalive ) {
9716
  // Add install ID and site unique ID.
9717
  $install_data['id'] = $install->id;
9718
  $install_data['uid'] = $uid;
9719
+ $install_data['url'] = $url;
9720
 
9721
+ $subsite_data_for_api_by_install_id[ $install->id ] = $install_data;
9722
+ $install_url_by_install_id[ $install->id ] = $install->url;
9723
+ $subsite_registration_date_by_install_id[ $install->id ] = $registration_date;
9724
  }
9725
  }
9726
  }
9727
 
9728
  restore_current_blog();
9729
 
9730
+ $installs_data = array_merge(
9731
+ $installs_data,
9732
+ array_values( $subsite_data_for_api_by_install_id )
9733
+ );
9734
+
9735
  if ( 0 < count( $installs_data ) && ( $is_common_diff_for_any_site || ! $only_diff ) ) {
9736
  if ( ! $only_diff ) {
9737
  $installs_data[] = $common;
9769
  if ( ( is_bool( $install->{$p} ) || ! empty( $install->{$p} ) ) &&
9770
  $install->{$p} != $v
9771
  ) {
9772
+ $val = self::get_api_sanitized_property( $p, $v );
9773
+
9774
+ if ( $install->{$p} != $val ) {
9775
+ $install->{$p} = $val;
9776
+ $diff[ $p ] = $val;
9777
+ }
9778
  }
9779
  } else {
9780
  $special[ $p ] = $v;
9799
  return $diff;
9800
  }
9801
 
9802
+ /**
9803
+ * @author Leo Fajardo (@leorw)
9804
+ * @since 2.5.1
9805
+ */
9806
+ private function send_pending_clone_update_once() {
9807
+ $this->_logger->entrance();
9808
+
9809
+ if ( ! empty( $this->_storage->clone_id ) ) {
9810
+ return;
9811
+ }
9812
+
9813
+ $install_clone = $this->get_api_site_scope()->call(
9814
+ '/clones',
9815
+ 'post',
9816
+ array( 'site_url' => self::get_unfiltered_site_url() )
9817
+ );
9818
+
9819
+ if ( $this->is_api_result_entity( $install_clone ) ) {
9820
+ $this->_storage->clone_id = $install_clone->id;
9821
+ }
9822
+ }
9823
+
9824
+ /**
9825
+ * @author Leo Fajardo (@leorw)
9826
+ * @since 2.5.1
9827
+ *
9828
+ * @param string $resolution_type
9829
+ * @param FS_Site $clone_context_install
9830
+ */
9831
+ function send_clone_resolution_update( $resolution_type, $clone_context_install ) {
9832
+ $this->_logger->entrance();
9833
+
9834
+ if ( empty( $this->_storage->clone_id ) ) {
9835
+ return;
9836
+ }
9837
+
9838
+ $new_install_id = null;
9839
+ $current_site = null;
9840
+
9841
+ $flush = false;
9842
+
9843
+ /**
9844
+ * If the current site is now different from the context install before the clone resolution, we need to override `$this->_site` so that the API call below will be made with the right install scope entity.
9845
+ */
9846
+ if ( $clone_context_install->id != $this->_site->id ) {
9847
+ $new_install_id = $this->_site->id;
9848
+ $current_site = $this->_site;
9849
+ $this->_site = $clone_context_install;
9850
+
9851
+ $flush = true;
9852
+ }
9853
+
9854
+ $this->get_api_site_scope( $flush )->call(
9855
+ "/clones/{$this->_storage->clone_id}",
9856
+ 'put',
9857
+ array(
9858
+ 'resolution' => $resolution_type,
9859
+ 'new_install_id' => $new_install_id,
9860
+ )
9861
+ );
9862
+
9863
+ if ( is_object( $current_site ) ) {
9864
+ /**
9865
+ * Ensure that the install scope entity is updated back to the previous install entity.
9866
+ */
9867
+ $this->_site = $current_site;
9868
+
9869
+ // Restore the previous install scope entity of the API.
9870
+ $this->get_api_site_scope( true );
9871
+ }
9872
+ }
9873
+
9874
  /**
9875
  * Update install only if changed.
9876
  *
9879
  *
9880
  * @param string[] string $override
9881
  * @param bool $flush
9882
+ * @param bool $is_two_way_sync @since 2.5.0 If true and there's a successful API request, the install sync cron will be cleared.
9883
  *
9884
  * @return false|object|string
9885
  */
9886
+ private function send_install_update( $override = array(), $flush = false, $is_two_way_sync = false ) {
9887
  $this->_logger->entrance();
9888
 
9889
  $check_properties = $this->get_install_data_for_api( $override );
9894
  $params = $this->get_install_diff_for_api( $check_properties, $this->_site, $override );
9895
  }
9896
 
 
9897
  if ( empty( $params ) ) {
9898
  $keepalive_only_update = $this->should_send_keepalive_update();
9899
 
9908
  }
9909
  }
9910
 
9911
+ if ( $is_two_way_sync ) {
9912
  /**
9913
+ * Update last install sync timestamp during a two-way sync call as we expect that updates are sent during this call.
 
9914
  *
9915
  * @author Leo Fajardo (@leorw)
9916
  * @since 2.2.3
9926
  $this->set_keepalive_timestamp();
9927
 
9928
  // Send updated values to FS.
9929
+ $site = $this->api_site_call( '/', 'put', $params, true );
9930
 
9931
+ if ( $is_two_way_sync && $this->is_api_result_entity( $site ) ) {
9932
  /**
9933
+ * Clear scheduled install sync after a two-way sync call.
9934
  *
9935
  * @author Leo Fajardo (@leorw)
9936
  * @since 2.2.3
9952
  *
9953
  * @param string[] string $override
9954
  * @param bool $flush
9955
+ * @param bool $is_two_way_sync @since 2.5.0 If true and there's a successful API request, the install sync cron will be cleared.
9956
  *
9957
  * @return false|object|string
9958
  */
9959
+ private function send_installs_update( $override = array(), $flush = false, $is_two_way_sync = false ) {
9960
  $this->_logger->entrance();
9961
 
9962
+ /**
9963
+ * Pass `true` to use the network level storage since the update is for many installs.
9964
+ *
9965
+ * @author Leo Fajardo (@leorw)
9966
+ * @since 2.2.3
9967
+ */
9968
+ $should_send_keepalive = $this->should_send_keepalive_update( true );
9969
 
9970
+ $installs_data = $this->get_installs_data_for_api( $override, ! $flush, $should_send_keepalive );
 
 
 
 
 
 
 
 
9971
 
9972
+ if ( empty( $installs_data ) ) {
9973
+ return false;
 
 
 
 
 
 
 
9974
  }
9975
 
9976
+ if ( $is_two_way_sync ) {
9977
+ // Update last install sync timestamp during a two-way sync call as we expect that updates are sent during this call.
9978
  $this->set_cron_execution_timestamp( 'install_sync' );
9979
  }
9980
 
9989
  // Send updated values to FS.
9990
  $result = $this->get_api_user_scope()->call( "/plugins/{$this->_plugin->id}/installs.json", 'put', $installs_data );
9991
 
9992
+ if ( $is_two_way_sync && $this->is_api_result_object( $result, 'installs' ) ) {
9993
+ // I successfully sent a two-way installs update, clear the scheduled install sync if it exists.
9994
  $this->clear_install_sync_cron();
9995
  }
9996
 
10040
  * @param string[] string $override
10041
  * @param bool $flush
10042
  */
10043
+ function sync_install( $override = array(), $flush = false ) {
10044
  $this->_logger->entrance();
10045
 
10046
+ $site = $this->send_install_update( $override, $flush, true );
10047
 
10048
  if ( false === $site ) {
10049
  // No sync required.
10072
  private function sync_installs( $override = array(), $flush = false ) {
10073
  $this->_logger->entrance();
10074
 
10075
+ $result = $this->send_installs_update( $override, $flush, true );
10076
 
10077
  if ( false === $result ) {
10078
  // No sync required.
10227
  // Send uninstall event.
10228
  $this->send_installs_update( $params );
10229
  } else {
10230
+ // Send uninstall event and handle the result.
10231
+ $this->sync_install( $params );
10232
  }
10233
  }
10234
 
10325
  return;
10326
  }
10327
 
10328
+ if (
10329
+ ! $fs->is_clone() &&
10330
+ /**
10331
+ * If there's a context install, run this method only when there's also a context user (e.g., when cloning a subsite of a multisite network into a single-site installation, it's possible for an install to be associated with a non-existing user entity; we want Freemius to be off in this case, while we are trying to recover the user).
10332
+ *
10333
+ * @author Leo Fajardo
10334
+ */
10335
+ ( ! is_object( $fs->_site ) || $fs->is_registered() )
10336
+ ) {
10337
+ $fs->_uninstall_plugin_event();
10338
+ }
10339
 
10340
  $fs->do_action( 'after_uninstall' );
10341
  }
10446
  * @return string
10447
  */
10448
  function get_premium_slug() {
10449
+ return ( is_object( $this->_plugin ) && ! empty( $this->_plugin->premium_slug ) ) ?
10450
  $this->_plugin->premium_slug :
10451
  "{$this->_slug}-premium";
10452
  }
10499
  null;
10500
  }
10501
 
10502
+ /**
10503
+ * Get whether the SDK has been initiated in the context of a Bundle.
10504
+ *
10505
+ * This will return true, if `bundle_id` is present in the SDK init parameters.
10506
+ *
10507
+ * ```php
10508
+ * $my_fs = fs_dynamic_init( array(
10509
+ * // ...
10510
+ * 'bundle_id' => 'XXXX', // Will return true since we have bundle id.
10511
+ * 'bundle_public_key' => 'pk_XXXX',
10512
+ * ) );
10513
+ * ```
10514
+ *
10515
+ * @author Swashata Ghosh (@swashata)
10516
+ * @since 2.5.0
10517
+ *
10518
+ * @return bool True if we are running in bundle context, false otherwise.
10519
+ */
10520
+ private function has_bundle_context() {
10521
+ return ! is_null( $this->get_bundle_id() );
10522
+ }
10523
+
10524
  /**
10525
  * @author Vova Feldman (@svovaf)
10526
  * @since 1.2.1.5
10565
  function get_eula_url() {
10566
  return $this->apply_filters(
10567
  'eula_url',
10568
+ "https://freemius.com/product/{$this->_plugin->id}/{$this->_slug}/legal/eula/"
10569
  );
10570
  }
10571
 
10753
  #endregion ------------------------------------------------------------------
10754
 
10755
  /* Account
10756
+ ------------------------------------------------------------------------------------------------------------------*/
10757
 
10758
  /**
10759
  * Find plugin's slug by plugin's basename.
10817
  */
10818
  private static function get_all_sites(
10819
  $module_type = WP_FS__MODULE_TYPE_PLUGIN,
10820
+ $blog_id = null,
10821
+ $is_backup = false
10822
  ) {
10823
+ $sites = self::get_account_option(
10824
+ ( $is_backup ? 'prev_' : '' ) . 'sites',
10825
+ $module_type,
10826
+ $blog_id
10827
+ );
10828
 
10829
  if ( ! is_array( $sites ) ) {
10830
  $sites = array();
11184
  *
11185
  * @author Vova Feldman (@svovaf)
11186
  * @since 1.0.1
11187
+ *
11188
+ * @param bool $ignore_anonymous_state Since 2.5.1
11189
+ *
11190
  * @return bool
11191
  */
11192
+ function is_registered( $ignore_anonymous_state = false ) {
11193
+ return (
11194
+ is_object( $this->_user ) &&
11195
+ (
11196
+ $this->is_premium() ||
11197
+ $ignore_anonymous_state ||
11198
+ ! $this->is_anonymous()
11199
+ )
11200
+ );
11201
  }
11202
 
11203
  /**
11208
  *
11209
  * @return bool
11210
  */
11211
+ function is_tracking_allowed( $blog_id = null, $install = null ) {
11212
+ if ( is_null( $install ) ) {
11213
+ $install = is_null( $blog_id ) ?
11214
+ $this->_site :
11215
+ $this->get_install_by_blog_id( $blog_id );
11216
+ }
11217
+
11218
+ return (
11219
+ is_object( $install ) &&
11220
+ FS_Permission_Manager::instance( $this )->is_homepage_url_tracking_allowed( $blog_id )
11221
+ );
11222
+ }
11223
+
11224
+ /**
11225
+ * Returns TRUE if the user never opted-in or manually opted-out.
11226
+ *
11227
+ * @author Vova Feldman (@svovaf)
11228
+ * @since 1.2.1.5
11229
+ *
11230
+ * @param int|null $blog_id
11231
+ *
11232
+ * @return bool
11233
+ */
11234
+ function is_tracking_prohibited( $blog_id = null ) {
11235
+ return (
11236
+ ! $this->is_registered( true ) ||
11237
+ ! $this->is_tracking_allowed( $blog_id )
11238
+ );
11239
  }
11240
 
11241
  /**
11271
  }
11272
 
11273
  /**
11274
+ * @author Vova Feldman (@svovaf)
11275
+ * @since 1.0.3
11276
+ *
11277
+ * @return FS_Site
11278
+ */
11279
+ function get_site() {
11280
+ return $this->_site;
11281
+ }
11282
+
11283
+ /**
11284
+ * @author Leo Fajardo (@leorw)
11285
+ * @since 2.5.0
11286
+ */
11287
+ function store_site( $site ) {
11288
+ $this->_site = $site;
11289
+ $this->_store_site( true );
11290
+ }
11291
+
11292
+ /**
11293
+ * Deletes the current install with an option to back it up in case restoration will be needed (e.g., if the automatic clone resolution attempt fails).
11294
+ *
11295
+ * @author Leo Fajardo (@leorw)
11296
+ * @since 2.5.0
11297
+ */
11298
+ function delete_current_install( $back_up ) {
11299
+ // Back up and delete the unique ID.
11300
+ if ( $back_up ) {
11301
+ self::$_accounts->set_option( 'prev_unique_id', $this->get_anonymous_id() );
11302
+ }
11303
+
11304
+ self::$_accounts->set_option( 'unique_id', null );
11305
+
11306
+ if ( $back_up ) {
11307
+ // Back up the install before deleting it so that it can be restored later on if necessary (e.g., if the automatic clone resolution attempt fails).
11308
+ $this->back_up_site();
11309
+ }
11310
+
11311
+ $this->_delete_site();
11312
+ $this->_site = null;
11313
+ }
11314
+
11315
+ /**
11316
+ * @author Leo Fajardo (@leorw)
11317
+ * @since 2.5.0
11318
  */
11319
+ function restore_backup_site() {
11320
+ self::$_accounts->set_option(
11321
+ 'unique_id',
11322
+ self::$_accounts->get_option( 'prev_unique_id' )
11323
+ );
11324
+
11325
+ $sites = self::get_all_sites( $this->_module_type, null, true );
11326
+ $this->store_site( clone $sites[ $this->_slug ] );
11327
  }
11328
 
11329
  /**
12607
  } else {
12608
  $url = is_object( $site ) ?
12609
  $site->siteurl :
12610
+ self::get_unfiltered_site_url( $blog_id );
12611
 
12612
  $disconnected_site_ids[] = $blog_id;
12613
  }
12980
  } else if ( $is_whitelabeled_flag ) {
12981
  $is_whitelabeled = true;
12982
  } else {
12983
+ if ( $this->is_registered() || $this->is_premium() ) {
12984
+ $addon_ids = $this->get_updated_account_addons();
12985
+ } else {
12986
+ $addons = self::get_all_addons();
12987
+
12988
+ $plugin_addons = isset( $addons[ $this->_plugin->id ] ) ?
12989
+ $addons[ $this->_plugin->id ] :
12990
+ array();
12991
+
12992
+ $addon_ids = array();
12993
+ foreach ( $plugin_addons as $addon ) {
12994
+ $addon_ids[] = $addon->id;
12995
+ }
12996
+ }
12997
+
12998
  $installed_addons = $this->get_installed_addons();
12999
  foreach ( $installed_addons as $fs_addon ) {
13000
  $addon_ids[] = $fs_addon->get_id();
13423
  fs_require_template( 'forms/resend-key.php', $vars );
13424
  }
13425
 
13426
+ /**
13427
+ * Displays an email address update dialog box when the user clicks on the email address "Edit" button on the "Account" page.
13428
+ *
13429
+ * @author Leo Fajardo (@leorw)
13430
+ * @since 2.5.0
13431
+ */
13432
+ function _add_email_address_update_dialog_box() {
13433
+ $vars = array( 'id' => $this->_module_id );
13434
+
13435
+ fs_require_template( 'forms/email-address-update.php', $vars );
13436
+ }
13437
+
13438
+ /**
13439
+ * @author Leo Fajardo (@leorw)
13440
+ * @since 2.5.0
13441
+ */
13442
+ function _add_email_address_update_option() {
13443
+ if ( ! $this->should_handle_user_change() ) {
13444
+ return;
13445
+ }
13446
+
13447
+ // Add email address update AJAX handler.
13448
+ $this->add_ajax_action( 'update_email_address', array( &$this, '_email_address_update_ajax_handler' ) );
13449
+ }
13450
+
13451
+ /**
13452
+ * @author Leo Fajardo (@leorw)
13453
+ * @since 2.5.0
13454
+ */
13455
+ function _email_address_update_ajax_handler() {
13456
+ $this->check_ajax_referer( 'update_email_address' );
13457
+
13458
+ $new_email_address = fs_request_get( 'email_address' );
13459
+ $transfer_type = fs_request_get( 'transfer_type' );
13460
+
13461
+ $result = $this->update_email( $new_email_address );
13462
+
13463
+ if ( ! FS_Api::is_api_error( $result ) ) {
13464
+ self::shoot_ajax_success();
13465
+ }
13466
+
13467
+ $error = '';
13468
+
13469
+ if ( FS_Api::is_api_error_object( $result ) ) {
13470
+ switch ( $result->error->code ) {
13471
+ case 'user_exist':
13472
+ case 'account_verification_required':
13473
+ $error = array(
13474
+ 'code' => 'change_ownership',
13475
+ 'url' => $this->get_account_url( 'change_owner', array(
13476
+ 'state' => 'init',
13477
+ 'candidate_email' => $new_email_address,
13478
+ 'transfer_type' => $transfer_type,
13479
+ ) ),
13480
+ );
13481
+
13482
+ break;
13483
+ }
13484
+ }
13485
+
13486
+ if ( empty( $error ) ) {
13487
+ $error = is_object( $result ) ?
13488
+ var_export( $result->error, true ) :
13489
+ $result;
13490
+ }
13491
+
13492
+ self::shoot_ajax_failure( $error );
13493
+ }
13494
+
13495
  /**
13496
  * Returns a collection of IDs of installs that are associated with the context product and its add-ons, and activated with foreign licenses.
13497
  *
13699
  ( $is_network_admin && $this->is_network_active() && ! $this->is_network_delegated_connection() ) ||
13700
  ( ! $is_network_admin && ( ! $this->is_network_active() || $this->is_delegated_connection() ) )
13701
  ) {
13702
+ if (
13703
+ $this->is_premium() ||
13704
+ ( $this->has_paid_plan() && ! $this->has_premium_version() )
13705
+ ) {
13706
+ /**
13707
+ * @since 1.2.0 Add license action link only on plugins page.
13708
+ */
13709
+ $this->_add_license_action_link();
13710
+ }
13711
  }
13712
  }
13713
 
13879
  self::shoot_ajax_failure();
13880
  }
13881
 
13882
+ $site = $this->api_site_call(
13883
  '',
13884
  'put',
13885
  array(
13932
  fs_request_get( 'blog_id', null ),
13933
  fs_request_get( 'module_id', null, 'post' ),
13934
  fs_request_get( 'user_id', null ),
13935
+ fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
13936
+ fs_request_get_bool( 'is_diagnostic_tracking_allowed', null )
13937
  );
13938
 
13939
  if (
14178
  * @param null|int $blog_id
14179
  * @param null|number $plugin_id
14180
  * @param null|number $license_owner_id
14181
+ * @param bool|null $is_extensions_tracking_allowed
14182
+ * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2 to allow license activation with minimal data footprint.
14183
+ *
14184
  *
14185
  * @return array {
14186
  * @var bool $success
14195
  $blog_id = null,
14196
  $plugin_id = null,
14197
  $license_owner_id = null,
14198
+ $is_extensions_tracking_allowed = null,
14199
+ $is_diagnostic_tracking_allowed = null
14200
  ) {
14201
  $this->_logger->entrance();
14202
 
14216
  $this :
14217
  $this->get_addon_instance( $plugin_id );
14218
 
14219
+ FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array(
14220
+ FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed,
14221
+ FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed,
14222
+ ) );
14223
 
14224
  $error = false;
14225
  $next_page = false;
14384
  }
14385
  }
14386
 
14387
+ $all_sites = self::get_sites();
14388
+ $pending_blog_ids = array();
14389
 
14390
  /**
14391
  * Check if there are any sites that are not connected, skipped, nor delegated. For every site that falls into that category, if the product is freemium, skip the connection. If the product is premium only, delegate the connection to the site administrator.
14415
  continue;
14416
  }
14417
 
14418
+ $pending_blog_ids[] = $blog_id;
14419
  }
14420
 
14421
+ if ( ! empty( $pending_blog_ids ) ) {
14422
  if ( $fs->is_freemium() && $fs->is_enable_anonymous() ) {
14423
+ $fs->skip_connection( $pending_blog_ids );
14424
  } else {
14425
+ $fs->delegate_connection( $pending_blog_ids );
14426
  }
14427
  }
14428
  }
14500
 
14501
  $addon_info = $fs->_get_addon_info( $addon_id, $is_installed );
14502
 
14503
+ if ( ! isset( $addon_info['is_connected'] ) || ! $addon_info['is_connected'] ) {
14504
  // Add-on is not associated with an install entity.
14505
  continue;
14506
  }
14558
  $this->delegate_connection();
14559
  } else {
14560
  if ( ! empty( $sites_by_action['delegate'] ) ) {
14561
+ $this->delegate_connection( self::get_sites_blog_ids( $sites_by_action['delegate'] ) );
14562
  }
14563
 
14564
  if ( ! empty( $sites_by_action['skip'] ) ) {
14565
+ $this->skip_connection( self::get_sites_blog_ids( $sites_by_action['skip'] ) );
14566
  }
14567
 
14568
  if ( empty( $sites_by_action['allow'] ) ) {
14880
  return $this->_plugin->has_affiliate_program();
14881
  }
14882
 
14883
+ /**
14884
+ * Get Plugin ID under which we will track affiliate application.
14885
+ *
14886
+ * This could either be the Bundle ID or the main plugin ID.
14887
+ *
14888
+ * @return number Bundle ID if developer has provided one, else the main plugin ID.
14889
+ */
14890
+ private function get_plugin_id_for_affiliate_terms() {
14891
+ return $this->has_bundle_context() ?
14892
+ $this->get_bundle_id() :
14893
+ $this->_plugin_id;
14894
+ }
14895
+
14896
  /**
14897
  * @author Leo Fajardo (@leorw)
14898
  * @since 1.2.4
14899
  */
14900
  private function fetch_affiliate_terms() {
14901
  if ( ! is_object( $this->plugin_affiliate_terms ) ) {
14902
+ /**
14903
+ * In case we have a bundle set in SDK configuration, we would like to use that for affiliates, not the main plugin.
14904
+ */
14905
+ $plugins_api = $this->has_bundle_context() ?
14906
+ $this->get_api_bundle_scope() :
14907
+ $this->get_api_plugin_scope();
14908
+
14909
  $affiliate_terms = $plugins_api->get( '/aff.json?type=affiliation', false );
14910
 
14911
+ /**
14912
+ * At this point, we intentionally don't fallback to the main plugin, because the developer has chosen to use bundle. So it makes sense the affiliate program should be in context to the bundle too.
14913
+ */
14914
  if ( ! $this->is_api_result_entity( $affiliate_terms ) ) {
14915
  return;
14916
  }
14928
  $application_data = $this->_storage->affiliate_application_data;
14929
  $flush = ( ! isset( $application_data['status'] ) || 'pending' === $application_data['status'] );
14930
 
14931
+ $plugin_id_for_affiliate = $this->get_plugin_id_for_affiliate_terms();
14932
+
14933
  $users_api = $this->get_api_user_scope();
14934
+ $result = $users_api->get( "/plugins/{$plugin_id_for_affiliate}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json", $flush );
14935
  if ( $this->is_api_result_object( $result, 'affiliates' ) ) {
14936
  if ( ! empty( $result->affiliates ) ) {
14937
  $affiliate = new FS_Affiliate( $result->affiliates[0] );
15031
  var_export( $next_page, true )
15032
  );
15033
  } else if ( $this->is_pending_activation() ) {
15034
+ self::shoot_ajax_failure( $this->get_text_inline( 'Account is pending activation. Please check your email and click the link to activate your account and then submit the affiliate form again.', 'account-is-pending-activation' ) );
15035
  }
15036
  }
15037
 
15038
  $this->fetch_affiliate_terms();
15039
 
15040
+ $plugin_id_for_affiliate = $this->get_plugin_id_for_affiliate_terms();
15041
+
15042
  $api = $this->get_api_user_scope();
15043
  $result = $api->call(
15044
+ ( "/plugins/{$plugin_id_for_affiliate}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json" ),
15045
  'post',
15046
  $affiliate
15047
  );
15503
  return ( defined( 'DOING_CRON' ) && DOING_CRON );
15504
  }
15505
 
15506
+ /**
15507
+ * @author Leo Fajardo (@leorw)
15508
+ * @since 2.5.0
15509
+ *
15510
+ * @return bool
15511
+ */
15512
+ static function is_admin_post() {
15513
+ return ( 'admin-post.php' === self::get_current_page() );
15514
+ }
15515
+
15516
  /**
15517
  * Check if a real user is visiting the admin dashboard.
15518
  *
15526
  is_admin() &&
15527
  ! self::is_ajax() &&
15528
  ! self::is_cron() &&
15529
+ ! self::is_admin_post()
15530
  );
15531
  }
15532
 
15678
  * @author Leo Fajardo (@leorw)
15679
  * @since 2.0.0
15680
  *
15681
+ * @param bool|int[] $all_or_blog_ids
15682
  */
15683
+ private function delegate_connection( $all_or_blog_ids = true ) {
15684
  $this->_logger->entrance();
15685
 
15686
  $this->_admin_notices->remove_sticky( 'connect_account' );
15687
 
15688
+ if ( true === $all_or_blog_ids ) {
15689
  // All sites delegation.
15690
+ $this->_storage->store( 'is_delegated_connection', true, true );
15691
  } else {
15692
  // Specified sites delegation.
15693
+ foreach ( $all_or_blog_ids as $blog_id ) {
15694
+ $this->delegate_site_connection( $blog_id );
15695
  }
15696
  }
15697
 
15707
  * @param int $blog_id
15708
  */
15709
  private function delegate_site_connection( $blog_id ) {
15710
+ $this->_storage->store( 'is_delegated_connection', true, $blog_id );
15711
  }
15712
 
15713
  /**
15747
  }
15748
 
15749
  /**
15750
+ * Check if delegated the connection. When running within the network admin,
15751
  * and haven't specified the blog ID, checks if network level delegated. If running
15752
  * within a site admin or specified a blog ID, check if delegated the connection for
15753
  * the current context site.
15807
  }
15808
 
15809
  /**
15810
+ * @todo Implement pagination when accessing the subsites collection.
15811
+ *
15812
  * @author Leo Fajardo (@leorw)
15813
  * @since 2.0.0
15814
  *
15815
+ * @param int $limit Default to 1,000
15816
+ * @param int $offset Default to 0
15817
+ *
15818
  * @return array Active & public sites collection.
15819
  */
15820
+ static function get_sites( $limit = 1000, $offset = 0 ) {
15821
  if ( ! is_multisite() ) {
15822
  return array();
15823
  }
15839
  'mature' => 0,
15840
  'spam' => 0,
15841
  'deleted' => 0,
15842
+ 'number' => $limit,
15843
+ 'offset' => $offset,
15844
  );
15845
 
15846
+ return get_sites( $args );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15847
  }
15848
 
15849
  /**
15892
  $address_to_blog_map = array();
15893
  foreach ( $sites as $site ) {
15894
  $blog_id = self::get_site_blog_id( $site );
15895
+ $address = self::get_unfiltered_site_url( $blog_id, true, true );
15896
  $address_to_blog_map[ $address ] = $blog_id;
15897
  }
15898
 
15928
  return $install_map;
15929
  }
15930
 
15931
+ /**
15932
+ * @author Vova Feldman (@svovaf)
15933
+ * @since 2.5.1
15934
+ *
15935
+ * @param bool|null $is_delegated When `true`, returns only connection delegated blog IDs. When `false`, only non-delegated blog IDs.
15936
+ *
15937
+ * @return int[]
15938
+ */
15939
+ private function get_blog_ids( $is_delegated = null ) {
15940
+ $blog_ids = array();
15941
+
15942
+ $sites = self::get_sites();
15943
+ foreach ( $sites as $site ) {
15944
+ $blog_id = self::get_site_blog_id( $site );
15945
+
15946
+ if (
15947
+ is_null( $is_delegated ) ||
15948
+ $is_delegated === $this->is_site_delegated_connection( $blog_id )
15949
+ ) {
15950
+ $blog_ids[] = $blog_id;
15951
+ }
15952
+ }
15953
+
15954
+ return $blog_ids;
15955
+ }
15956
+
15957
+ /**
15958
+ * @author Vova Feldman (@svovaf)
15959
+ * @since 2.5.1
15960
+ *
15961
+ * @return int[]
15962
+ */
15963
+ private function get_non_delegated_blog_ids() {
15964
+ return $this->get_blog_ids( false );
15965
+ }
15966
+
15967
  /**
15968
  * Gets a map of module IDs that the given user has opted-in to.
15969
  *
16048
  *
16049
  * @param int $blog_id
16050
  * @param FS_Site $install
16051
+ * @param bool $flush
16052
  *
16053
  * @return bool Since 2.3.1 returns if a switch was made.
16054
  */
16055
+ function switch_to_blog( $blog_id, FS_Site $install = null, $flush = false ) {
16056
+ if ( ! is_numeric( $blog_id ) ) {
16057
+ return false;
16058
+ }
16059
+
16060
+ if ( ! $flush && $blog_id == $this->_context_is_network_or_blog_id ) {
16061
  return false;
16062
  }
16063
 
16121
  unset( $this->_site_api );
16122
  unset( $this->_user_api );
16123
 
16124
+ return true;
16125
  }
16126
 
16127
  /**
16150
  $site['blog_id'] );
16151
  }
16152
 
16153
+ /**
16154
+ * @author Vova Feldman (@svovaf)
16155
+ * @since 2.5.1
16156
+ *
16157
+ * @param WP_Site[]|array[] $sites
16158
+ *
16159
+ * @return int[]
16160
+ */
16161
+ static function get_sites_blog_ids( $sites ) {
16162
+ $blog_ids = array();
16163
+ foreach ( $sites as $site ) {
16164
+ $blog_ids[] = self::get_site_blog_id( $site );
16165
+ }
16166
+
16167
+ return $blog_ids;
16168
+ }
16169
+
16170
  /**
16171
  * @author Leo Fajardo (@leorw)
16172
  * @since 2.0.0
16173
  *
16174
  * @param array|WP_Site|null $site
16175
+ * @param bool $load_registration Since 2.5.1 When set to `true` the method will attempt to return the subsite's registration date, regardless of the `$site` type and value. In most calls, the registration date will be returned anyway, even when the value is `false`. This param is purely for performance optimization.
16176
  *
16177
  * @return array
16178
  */
16179
+ function get_site_info( $site = null, $load_registration = false ) {
16180
  $this->_logger->entrance();
16181
 
16182
  $switched = false;
16183
 
16184
+ $registration_date = null;
16185
+
16186
  if ( is_null( $site ) ) {
16187
+ $url = self::get_unfiltered_site_url();
16188
  $name = get_bloginfo( 'name' );
16189
  $blog_id = null;
16190
  } else {
16196
  }
16197
 
16198
  if ( $site instanceof WP_Site ) {
16199
+ $url = $site->siteurl;
16200
+ $name = $site->blogname;
16201
+ $registration_date = $site->registered;
16202
  } else {
16203
+ $url = self::get_unfiltered_site_url( $blog_id );
16204
  $name = get_bloginfo( 'name' );
16205
  }
16206
  }
16207
 
16208
+ if ( empty( $registration_date ) && $load_registration ) {
16209
+ $blog_details = get_blog_details( $blog_id, false );
16210
+
16211
+ if ( is_object( $blog_details ) && isset( $blog_details->registered ) ) {
16212
+ $registration_date = $blog_details->registered;
16213
+ }
16214
+ }
16215
+
16216
  $info = array(
16217
+ 'uid' => $this->get_anonymous_id( $blog_id ),
16218
+ 'url' => $url,
 
 
 
16219
  );
16220
 
16221
+ // Add these diagnostic information only if user allowed to track.
16222
+ if ( FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed() ) {
16223
+ $info = array_merge( $info, array(
16224
+ 'title' => $name,
16225
+ 'language' => self::get_sanitized_language(),
16226
+ ) );
16227
+ }
16228
+
16229
  if ( is_numeric( $blog_id ) ) {
16230
  $info['blog_id'] = $blog_id;
16231
  }
16232
 
16233
+ if ( ! empty( $registration_date ) ) {
16234
+ $info[ 'registration_date' ] = $registration_date;
16235
+ }
16236
+
16237
  if ( $switched ) {
16238
  restore_current_blog();
16239
  }
16456
  }
16457
  }
16458
 
16459
+ if ( ! $this->is_registered() ) {
16460
+ return;
16461
+ }
16462
+
16463
  if ( $this->is_sync_cron_scheduled() &&
16464
  $context_blog_id == $this->get_sync_cron_blog_id()
16465
  ) {
16493
 
16494
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
16495
 
16496
+ if ( ! $this->is_registered() ) {
16497
+ return;
16498
+ }
16499
+
16500
  $current_blog_id = get_current_blog_id();
16501
 
16502
  $this->switch_to_blog( $context_blog_id );
16530
 
16531
  $this->update_multisite_data_after_site_deactivation( $context_blog_id );
16532
 
16533
+ if ( ! $this->is_registered() ) {
16534
+ return;
16535
+ }
16536
+
16537
  $current_blog_id = get_current_blog_id();
16538
 
16539
  $this->switch_to_blog( $context_blog_id );
16551
  $this->switch_to_blog( $current_blog_id );
16552
  }
16553
 
16554
+ /**
16555
+ * Executed after site deletion, called from wp_delete_site
16556
+ *
16557
+ * @author Dario Curvino (@dudo)
16558
+ * @since 2.5.0
16559
+ *
16560
+ * @param WP_Site $old_site
16561
+ */
16562
+ public function _after_wpsite_deleted_callback( WP_Site $old_site ) {
16563
+ $this->_logger->entrance();
16564
+
16565
+ $this->_after_site_deleted_callback( $old_site->blog_id, true );
16566
+ }
16567
+
16568
  /**
16569
  * Executed after site re-activation.
16570
  *
16679
  * @return bool
16680
  */
16681
  function is_product_settings_page() {
16682
+ $page = fs_request_get( 'page', '', 'get' );
16683
+ $menu_slug = $this->_menu->get_slug();
16684
+
16685
+ if ( $page === $menu_slug ) {
16686
+ return true;
16687
+ }
16688
+
16689
  return fs_starts_with(
16690
+ // e.g., {$menu_slug}-account, {$menu_slug}-affiliation, etc.
16691
+ $page,
16692
+ ( $menu_slug . '-' )
16693
  );
16694
  }
16695
 
16769
  *
16770
  * @param bool|string $topic
16771
  * @param bool|string $message
16772
+ * @param bool|string $summary Since 2.5.1.
16773
  *
16774
  * @return string
16775
  */
16776
+ function contact_url( $topic = false, $message = false, $summary = false ) {
16777
  $params = array();
16778
  if ( is_string( $topic ) ) {
16779
  $params['topic'] = $topic;
16782
  $params['message'] = $message;
16783
  }
16784
 
16785
+ if ( is_string( $summary ) ) {
16786
+ $params['summary'] = $summary;
16787
+ }
16788
+
16789
  if ( $this->is_addon() ) {
16790
  $params['addon_id'] = $this->get_id();
16791
 
16824
  }
16825
 
16826
  /* Logger
16827
+ ------------------------------------------------------------------------------------------------------------------*/
16828
  /**
16829
  * @param string $id
16830
  * @param bool $prefix_slug
16849
  }
16850
 
16851
  /* Security
16852
+ ------------------------------------------------------------------------------------------------------------------*/
16853
  private static function _encrypt( $str ) {
16854
  if ( is_null( $str ) ) {
16855
  return null;
17069
  ) {
17070
  // Load site.
17071
  $this->_site = $site;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17072
  }
17073
 
17074
  $user = null;
17097
  /**
17098
  * This is a special fault tolerance mechanism to handle a scenario that the user data is missing.
17099
  */
17100
+ if (
17101
+ ! isset( $this->_storage->user_recovery_from_install_last_attempt_timestamp ) ||
17102
+ time() > ( $this->_storage->user_recovery_from_install_last_attempt_timestamp + FS_Clone_Manager::CLONE_RESOLUTION_MAX_EXECUTION_TIME )
17103
+ ) {
17104
+ $user = $this->sync_user_by_current_install();
17105
+ } else {
17106
+ return;
17107
+ }
17108
+
17109
+ if ( is_object( $user ) ) {
17110
+ $this->_storage->user_was_recovered_from_install = true;
17111
+ } else {
17112
+ $this->_storage->user_recovery_from_install_attempts = isset( $this->_storage->user_recovery_from_install_attempts ) ?
17113
+ ( $this->_storage->user_recovery_from_install_attempts + 1 ) :
17114
+ 1;
17115
+
17116
+ if ( $this->_storage->user_recovery_from_install_attempts >= 3 ) {
17117
+ $this->delete_current_install( false );
17118
+ } else {
17119
+ $this->_storage->user_recovery_from_install_last_attempt_timestamp = time();
17120
+
17121
+ return;
17122
+ }
17123
+ }
17124
  }
17125
 
17126
  $this->_user = ( $user instanceof FS_User ) ?
17134
  }
17135
 
17136
  if ( is_object( $this->_site ) ) {
17137
+ // Load plans.
17138
+ $this->_plans = isset( $plans[ $this->_slug ] ) ?
17139
+ $plans[ $this->_slug ] :
17140
+ array();
17141
+
17142
+ if ( ! is_array( $this->_plans ) || empty( $this->_plans ) ) {
17143
+ $this->_sync_plans();
17144
+ } else {
17145
+ for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) {
17146
+ if ( $this->_plans[ $i ] instanceof FS_Plugin_Plan ) {
17147
+ $this->_plans[ $i ] = self::decrypt_entity( $this->_plans[ $i ] );
17148
+ } else {
17149
+ unset( $this->_plans[ $i ] );
17150
+ }
17151
+ }
17152
+ }
17153
+
17154
  $this->_license = $this->_get_license_by_id( $this->_site->license_id );
17155
 
17156
  if ( $this->_site->version != $this->get_plugin_version() ) {
17169
  if ( $this->is_theme() ) {
17170
  $this->_register_account_hooks();
17171
  }
17172
+
17173
+ if ( $this->is_user_in_admin() && $this->is_clone() ) {
17174
+ if ( empty( FS_Clone_Manager::instance()->get_clone_identification_timestamp() ) ) {
17175
+ FS_Clone_Manager::instance()->store_clone_identification_timestamp();
17176
+ }
17177
+
17178
+ FS_Clone_Manager::instance()->maybe_update_clone_resolution_support_flag( $this->_storage->sdk_last_version );
17179
+ $this->send_pending_clone_update_once();
17180
+ }
17181
  }
17182
 
17183
  /**
17257
  */
17258
  private function get_versions() {
17259
  $versions = array();
17260
+ $versions['sdk_version'] = $this->version;
17261
+
17262
+ // Collect these diagnostic information only if it's allowed.
17263
+ if ( FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed() ) {
17264
+ $versions['platform_version'] = get_bloginfo( 'version' );
17265
+ $versions['programming_language_version'] = phpversion();
17266
+ }
17267
+
17268
+ foreach ( $versions as $k => $version ) {
17269
+ $versions[ $k ] = self::get_api_sanitized_property( $k, $version );
17270
+ }
17271
+
17272
+ return $versions;
17273
+ }
17274
+
17275
+ /**
17276
+ * Get sanitized site language.
17277
+ *
17278
+ * @param string $language
17279
+ * @param int $max_len
17280
+ *
17281
+ * @since 2.5.1
17282
+ * @author Vova Feldman (@svovaf)
17283
+ *
17284
+ * @return string
17285
+ */
17286
+ private static function get_sanitized_language( $language = '', $max_len = self::LANGUAGE_MAX_CHARS ) {
17287
+ if ( empty( $language ) ) {
17288
+ $language = get_bloginfo( 'language' );
17289
+ }
17290
+
17291
+ return substr( $language, 0, $max_len );
17292
+ }
17293
+
17294
+ /**
17295
+ * Get core version stripped from pre-release and build.
17296
+ *
17297
+ * @since 2.5.1
17298
+ * @author Vova Feldman (@svovaf)
17299
+ *
17300
+ * @param string $version
17301
+ * @param int $parts
17302
+ * @param int $max_len
17303
+ * @param bool $include_pre_release
17304
+ *
17305
+ * @return string
17306
+ */
17307
+ private static function get_core_version(
17308
+ $version,
17309
+ $parts = 3,
17310
+ $max_len = self::VERSION_MAX_CHARS,
17311
+ $include_pre_release = false
17312
+ ) {
17313
+ if ( empty( $version ) ) {
17314
+ // Version is empty.
17315
+ return '';
17316
+ }
17317
+
17318
+ if ( is_numeric( $version ) ) {
17319
+ $is_float_version = is_float( $version );
17320
+
17321
+ $version = (string) $version;
17322
+
17323
+ /**
17324
+ * Casting a whole float number to a string cuts the decimal point. This part make sure to add the missing decimal part to the version.
17325
+ */
17326
+ if ( $is_float_version && false === strpos( $version, '.' ) ) {
17327
+ $version .= '.0';
17328
+ }
17329
+ }
17330
+
17331
+ if ( ! is_string( $version ) ) {
17332
+ return '';
17333
+ }
17334
+
17335
+ if ( $parts < 1 ) {
17336
+ return '';
17337
+ }
17338
+
17339
+ $pre_release_regex = $include_pre_release ?
17340
+ '(\-(alpha|beta|RC)([0-9]+)?)?' :
17341
+ '';
17342
 
17343
+ if ( 0 === preg_match( '/^([0-9]+(\.[0-9]+){0,' . ( $parts - 1 ) . '}' . $pre_release_regex . ')/i', $version, $matches ) ) {
17344
+ // Version is not starting with a digit.
17345
+ return '';
 
17346
  }
17347
 
17348
+ return substr( $matches[1], 0, $max_len );
17349
+ }
17350
+
17351
+ /**
17352
+ * @param string $prop
17353
+ * @param mixed $val
17354
+ *
17355
+ * @return mixed
17356
+ *@author Vova Feldman (@svovaf)
17357
+ *
17358
+ * @since 2.5.1
17359
+ */
17360
+ private static function get_api_sanitized_property( $prop, $val ) {
17361
+ if ( ! is_string( $val ) || empty( $val ) ) {
17362
+ return $val;
17363
+ }
17364
+
17365
+ switch ( $prop ) {
17366
+ case 'programming_language_version':
17367
+ // Get core PHP version, which can have up to 3 parts (ignore pre-releases).
17368
+ return self::get_core_version( $val );
17369
+ case 'platform_version':
17370
+ // Get the exact WordPress version, which can have up to 3 parts (including pre-releases).
17371
+ return self::get_core_version( $val, 3, self::VERSION_MAX_CHARS, true );
17372
+ case 'sdk_version':
17373
+ // Get the exact SDK version, which can have up to 4 parts.
17374
+ return self::get_core_version( $val, 4 );
17375
+ case 'version':
17376
+ // Get the entire version but just limited in length.
17377
+ return substr( $val, 0, self::VERSION_MAX_CHARS );
17378
+ case 'language':
17379
+ return self::get_sanitized_language( $val );
17380
+ default:
17381
+ return $val;
17382
+ }
17383
  }
17384
 
17385
  /**
17434
  $versions = $this->get_versions();
17435
 
17436
  $params = array_merge( $versions, array(
17437
+ 'user_firstname' => $current_user->user_firstname,
17438
+ 'user_lastname' => $current_user->user_lastname,
17439
+ 'user_email' => $current_user->user_email,
17440
+ 'plugin_slug' => $this->_slug,
17441
+ 'plugin_id' => $this->get_id(),
17442
+ 'plugin_public_key' => $this->get_public_key(),
17443
+ 'plugin_version' => $this->get_plugin_version(),
17444
+ 'return_url' => fs_nonce_url( $return_url, $activation_action ),
17445
+ 'account_url' => fs_nonce_url( $this->_get_admin_page_url(
 
 
17446
  'account',
17447
  array( 'fs_action' => 'sync_user' )
17448
  ), 'sync_user' ),
17449
+ 'is_premium' => $this->is_premium(),
17450
+ 'is_active' => true,
17451
+ 'is_uninstalled' => false,
17452
+ 'is_localhost' => WP_FS__IS_LOCALHOST,
17453
  ) );
17454
 
17455
  if ( $this->is_addon() ) {
17470
 
17471
  $site = $this->get_site_info( $site );
17472
 
17473
+ $diagnostic_info = array();
17474
+ if ( FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed() ) {
17475
+ $diagnostic_info = array(
17476
+ 'site_name' => $site['title'],
17477
+ 'language' => self::get_sanitized_language( $site['language'] ),
17478
+ );
17479
+ }
17480
+
17481
+ $params = array_merge( $params, $diagnostic_info, array(
17482
  'site_uid' => $site['uid'],
17483
  'site_url' => $site['url'],
 
 
 
17484
  ) );
17485
  }
17486
 
17505
  );
17506
  }
17507
 
17508
+ if ( is_multisite() && function_exists( 'get_network' ) ) {
17509
+ $params['network_uid'] = $this->get_anonymous_network_id();
17510
+ }
17511
+
17512
  return array_merge( $params, $override_with );
17513
  }
17514
 
17527
  * In this case, the user and site info will be sent to the server but no
17528
  * data will be saved to the WP installation's database.
17529
  * @param number|bool $trial_plan_id
17530
+ * @param bool $is_disconnected Whether to opt in without tracking.
17531
  * @param null|bool $is_marketing_allowed
17532
  * @param array $sites If network-level opt-in, an array of containing details of sites.
17533
+ * @param bool $redirect
17534
  *
17535
  * @return string|object
17536
  * @use WP_Error
17544
  $trial_plan_id = false,
17545
  $is_disconnected = false,
17546
  $is_marketing_allowed = null,
17547
+ $sites = array(),
17548
+ $redirect = true
17549
  ) {
17550
  $this->_logger->entrance();
17551
 
17569
  $fs_user,
17570
  false,
17571
  $trial_plan_id,
17572
+ $redirect,
17573
  true,
17574
  $sites
17575
  );
17637
  $params['is_marketing_allowed'] = $is_marketing_allowed;
17638
  }
17639
 
17640
+ $params['is_disconnected'] = $is_disconnected;
17641
+ $params['format'] = 'json';
17642
+ $params['is_extensions_tracking_allowed'] = FS_Permission_Manager::instance( $this )->is_extensions_tracking_allowed();
17643
+ $params['is_diagnostic_tracking_allowed'] = FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed();
17644
 
17645
  $request = array(
17646
  'method' => 'POST',
17647
  'body' => $params,
17648
+ 'timeout' => 60,
17649
  );
17650
 
17651
  $url = $this->add_show_pending( WP_FS__ADDRESS . '/action/service/user/install/' );
17731
  true ),
17732
  false,
17733
  $filtered_license_key,
17734
+ ! empty( $params['trial_plan_id'] ),
17735
+ isset( $decoded->is_suspicious_email ) && $decoded->is_suspicious_email
17736
  );
17737
  } else if ( isset( $decoded->install_secret_key ) ) {
17738
  return $this->install_with_new_user(
17745
  ( isset( $decoded->is_extensions_tracking_allowed ) && ! is_null( $decoded->is_extensions_tracking_allowed ) ?
17746
  $decoded->is_extensions_tracking_allowed :
17747
  null ),
17748
+ ( isset( $decoded->is_diagnostic_tracking_allowed ) && ! is_null( $decoded->is_diagnostic_tracking_allowed ) ?
17749
+ $decoded->is_diagnostic_tracking_allowed :
17750
+ null ),
17751
  $decoded->install_id,
17752
  $decoded->install_public_key,
17753
  $decoded->install_secret_key,
17764
  ( isset( $decoded->is_extensions_tracking_allowed ) && ! is_null( $decoded->is_extensions_tracking_allowed ) ?
17765
  $decoded->is_extensions_tracking_allowed :
17766
  null ),
17767
+ ( isset( $decoded->is_diagnostic_tracking_allowed ) && ! is_null( $decoded->is_diagnostic_tracking_allowed ) ?
17768
+ $decoded->is_diagnostic_tracking_allowed :
17769
+ null ),
17770
  $decoded->installs,
17771
  false
17772
  );
17864
  $this->_admin_notices->remove_sticky( 'connect_account' );
17865
 
17866
  if ( $this->is_pending_activation() || ! $this->has_settings_menu() ) {
17867
+ $this->clear_pending_activation_mode();
 
 
 
 
17868
 
17869
  if ( ! $this->is_paying_or_trial() ) {
17870
  $this->_admin_notices->add_sticky(
17871
+ sprintf( $this->get_text_inline( '%s opt-in was successfully completed.', 'plugin-x-activation-message' ), '<b>' . $this->get_plugin_name() . '</b>' ),
17872
  'activation_complete'
17873
  );
17874
  }
17990
  fs_request_get( 'user_secret_key' ),
17991
  fs_request_get_bool( 'is_marketing_allowed', null ),
17992
  fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
17993
+ fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ),
17994
  $pending_sites_info['blog_ids'],
17995
  $pending_sites_info['license_key'],
17996
  $pending_sites_info['trial_plan_id']
18002
  fs_request_get( 'user_secret_key' ),
18003
  fs_request_get_bool( 'is_marketing_allowed', null ),
18004
  fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
18005
+ fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ),
18006
  fs_request_get( 'install_id' ),
18007
  fs_request_get( 'install_public_key' ),
18008
  fs_request_get( 'install_secret_key' ),
18011
  );
18012
  }
18013
  } else if ( fs_request_has( 'pending_activation' ) ) {
18014
+ $this->set_pending_confirmation(
18015
+ fs_request_get( 'user_email' ),
18016
+ true,
18017
+ false,
18018
+ false,
18019
+ fs_request_get_bool( 'is_suspicious_email' )
18020
+ );
18021
  }
18022
  }
18023
  }
18065
  * @param string $user_secret_key
18066
  * @param bool|null $is_marketing_allowed
18067
  * @param bool|null $is_extensions_tracking_allowed Since 2.3.2
18068
+ * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2
18069
  * @param number $install_id
18070
  * @param string $install_public_key
18071
  * @param string $install_secret_key
18080
  $user_secret_key,
18081
  $is_marketing_allowed,
18082
  $is_extensions_tracking_allowed,
18083
+ $is_diagnostic_tracking_allowed,
18084
  $install_id,
18085
  $install_public_key,
18086
  $install_secret_key,
18114
  $site->secret_key = $install_secret_key;
18115
 
18116
  $this->_site = $site;
18117
+ $site_result = $this->get_api_site_scope( true )->get();
18118
  $site = new FS_Site( $site_result );
18119
  $this->_site = $site;
18120
 
18122
  $this->disable_opt_in_notice_and_lock_user();
18123
  }
18124
 
18125
+ FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array(
18126
+ FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed,
18127
+ FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed,
18128
+ ) );
18129
 
18130
  return $this->setup_account(
18131
  $this->_user,
18146
  * @param string $user_secret_key
18147
  * @param bool|null $is_marketing_allowed
18148
  * @param bool|null $is_extensions_tracking_allowed Since 2.3.2
18149
+ * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2
18150
  * @param array $site_ids
18151
  * @param bool $license_key
18152
  * @param bool $trial_plan_id
18160
  $user_secret_key,
18161
  $is_marketing_allowed,
18162
  $is_extensions_tracking_allowed,
18163
+ $is_diagnostic_tracking_allowed,
18164
  $site_ids,
18165
  $license_key = false,
18166
  $trial_plan_id = false,
18172
  $this->disable_opt_in_notice_and_lock_user();
18173
  }
18174
 
18175
+ FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array(
18176
+ FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed,
18177
+ FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed,
18178
+ ) );
18179
 
18180
  $sites = array();
18181
  foreach ( $site_ids as $site_id ) {
18196
  * @param string $user_secret_key
18197
  * @param bool|null $is_marketing_allowed
18198
  * @param bool|null $is_extensions_tracking_allowed Since 2.3.2
18199
+ * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2
18200
  * @param object[] $installs
18201
  * @param bool $redirect
18202
  * @param bool $auto_install Since 1.2.1.7 If `true` and setting up an account with a valid license, will redirect (or return a URL) to the account page with a special parameter to trigger the auto installation processes.
18209
  $user_secret_key,
18210
  $is_marketing_allowed,
18211
  $is_extensions_tracking_allowed,
18212
+ $is_diagnostic_tracking_allowed,
18213
  array $installs,
18214
  $redirect = true,
18215
  $auto_install = false
18220
  $this->disable_opt_in_notice_and_lock_user();
18221
  }
18222
 
18223
+ FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array(
18224
+ FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed,
18225
+ FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed,
18226
+ ) );
18227
 
18228
  $install_ids = array();
18229
 
18231
  $install_ids[] = $install->id;
18232
  }
18233
 
18234
+ $items_per_request = 25;
18235
+ $left = count( $install_ids );
18236
+ $offset = 0;
18237
 
18238
  $installs = array();
18239
  while ( $left > 0 ) {
18240
+ $result = $this->get_api_user_scope()->get( "/plugins/{$this->_module_id}/installs.json?ids=" . implode( ',', array_slice( $install_ids, $offset, $items_per_request ) ) );
18241
 
18242
  if ( ! $this->is_api_result_object( $result, 'installs' ) ) {
18243
  // @todo Handle API error.
18245
 
18246
  $installs = array_merge( $installs, $result->installs );
18247
 
18248
+ $left -= $items_per_request;
18249
+ $offset += $items_per_request;
18250
  }
18251
 
18252
  foreach ( $installs as &$install ) {
18276
  $email = false,
18277
  $redirect = true,
18278
  $license_key = false,
18279
+ $is_pending_trial = false,
18280
+ $is_suspicious_email = false
18281
  ) {
18282
  if ( $this->_ignore_pending_mode ) {
18283
  /**
18287
  * @author Vova Feldman
18288
  * @since 1.2.1.6
18289
  */
18290
+ $this->skip_connection( fs_is_network_admin() );
18291
  } else {
18292
  // Install must be activated via email since
18293
  // user with the same email already exist.
18294
  $this->_storage->is_pending_activation = true;
18295
+ $this->_add_pending_activation_notice( $email, $is_pending_trial, $is_suspicious_email );
18296
  }
18297
 
18298
  if ( ! empty( $license_key ) ) {
18307
 
18308
  $next_page = $this->get_after_activation_url( 'after_pending_connect_url' );
18309
 
 
18310
  if ( $redirect ) {
18311
+ // Reload the page with a pending activation message.
18312
  fs_redirect( $next_page );
18313
  }
18314
 
18337
  */
18338
  $license_key = fs_request_get( 'license_secret_key' );
18339
 
18340
+ FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array(
18341
+ FS_Permission_Manager::PERMISSION_DIAGNOSTIC => fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ),
18342
+ FS_Permission_Manager::PERMISSION_EXTENSIONS => fs_request_get_bool( 'is_extensions_tracking_allowed', null ),
18343
+ ) );
18344
 
18345
  $this->install_with_current_user( $license_key );
18346
  }
18358
  *
18359
  * @return object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install.
18360
  */
18361
+ function install_with_current_user(
18362
  $license_key = false,
18363
  $trial_plan_id = false,
18364
  $sites = array(),
18741
  $this->send_installs_update();
18742
  }
18743
 
 
 
 
18744
  $current_blog = get_current_blog_id();
18745
 
18746
  foreach ( $blog_2_install_map as $blog_id => $install ) {
18749
  $this->do_action( 'after_account_connection', $this->_user, $install );
18750
  }
18751
 
18752
+ // Switch install context back to the first install.
18753
+ $this->switch_to_blog(
18754
+ $current_blog,
18755
+ $first_install,
18756
+ ( $this->_site->id != $first_install->id )
18757
+ );
18758
 
18759
  $this->do_action( 'after_network_account_connection', $this->_user, $blog_2_install_map );
18760
  }
18802
  $parent_fs->_admin_notices->remove_sticky( 'connect_account' );
18803
 
18804
  if ( $parent_fs->is_pending_activation() ) {
18805
+ $parent_fs->clear_pending_activation_mode();
 
 
18806
  }
18807
 
18808
  // Get user information based on parent's plugin.
18856
  // return;
18857
  // }
18858
 
18859
+ if ( is_object( $this->_site ) && ! $this->is_registered() ) {
18860
+ return;
18861
+ }
18862
+
18863
  /**
18864
  * When running from a site admin with a network activated module and the connection
18865
  * was NOT delegated and the user still haven't skipped or opted-in, then hide the
18975
  if ( ! $this->has_settings_menu() ) {
18976
  // Add the opt-in page without a menu item.
18977
  $hook = FS_Admin_Menu_Manager::add_subpage(
18978
+ '',
18979
  $this->get_plugin_name(),
18980
  $this->get_plugin_name(),
18981
  'manage_options',
19407
  $hook = FS_Admin_Menu_Manager::add_subpage(
19408
  $item['show_submenu'] ?
19409
  $top_level_menu_slug :
19410
+ '',
19411
  $item['page_title'],
19412
  $menu_item,
19413
  $capability,
19422
  FS_Admin_Menu_Manager::add_subpage(
19423
  $item['show_submenu'] ?
19424
  $top_level_menu_slug :
19425
+ '',
19426
  $item['page_title'],
19427
  $menu_item,
19428
  $capability,
19814
  *
19815
  * @return string
19816
  */
19817
+ static function get_ajax_action_static( $tag, $module_id = null ) {
19818
  $action = "fs_{$tag}";
19819
 
19820
  if ( ! empty( $module_id ) ) {
19837
  * @uses do_action()
19838
  */
19839
  function do_action( $tag, $arg = '' ) {
 
 
19840
  $args = func_get_args();
19841
 
19842
+ $this->_logger->entrance( $tag );
19843
+
19844
  call_user_func_array( 'do_action', array_merge(
19845
  array( $this->get_action_tag( $tag ) ),
19846
  array_slice( $args, 1 ) )
19980
  wp_send_json( $result );
19981
  }
19982
 
19983
+ /**
19984
+ * Returns an AJAX URL with a special extra param to indicate whether the request was triggered from the network admin or blog admin.
19985
+ *
19986
+ * @author Vova Feldman (@svovaf)
19987
+ * @since 2.5.1
19988
+ *
19989
+ * @param string $wrap_with By default, returns the AJAX URL wrapped with single quotes.
19990
+ *
19991
+ * @return string
19992
+ */
19993
+ static function ajax_url( $wrap_with = "'") {
19994
+ if ( fs_is_network_admin() ) {
19995
+ $param_name = '_fs_network_admin';
19996
+ } else {
19997
+ $param_name = '_fs_blog_admin';
19998
+ }
19999
+
20000
+ $url = admin_url( 'admin-ajax.php', 'relative' );
20001
+ $url .= ( false === strpos( $url, '?' ) ) ? '?' : '&';
20002
+ $url .= "{$param_name}=true";
20003
+
20004
+ return "{$wrap_with}{$url}{$wrap_with}";
20005
+ }
20006
+
20007
  /**
20008
  * Apply filter, specific for the current context plugin.
20009
  *
20018
  * @uses apply_filters()
20019
  */
20020
  function apply_filters( $tag, $value ) {
20021
+ $args = func_get_args();
20022
+
20023
  $this->_logger->entrance( $tag );
20024
 
 
20025
  array_unshift( $args, $this->get_unique_affix() );
20026
 
20027
  return call_user_func_array( 'fs_apply_filter', $args );
20082
  }
20083
 
20084
  /* Account Page
20085
+ ------------------------------------------------------------------------------------------------------------------*/
20086
  /**
20087
  * Update site information.
20088
  *
20093
  * @param null|int $network_level_or_blog_id Since 2.0.0
20094
  * @param \FS_Site $site Since 2.0.0
20095
  */
20096
+ private function _store_site( $store = true, $network_level_or_blog_id = null, FS_Site $site = null, $is_backup = false ) {
20097
  $this->_logger->entrance();
20098
 
20099
  if ( is_null( $site ) ) {
20108
 
20109
  $site_clone = clone $site;
20110
 
20111
+ $sites = self::get_all_sites( $this->_module_type, $network_level_or_blog_id, $is_backup );
20112
 
20113
+ if (
20114
+ ! $is_backup &&
20115
+ is_object( $this->_user ) && $this->_user->id != $site->user_id
20116
+ ) {
20117
  $this->sync_user_by_current_install( $site->user_id );
20118
 
20119
  $prev_stored_user_id = $this->_storage->get( 'prev_user_id', false, $network_level_or_blog_id );
20138
 
20139
  $sites[ $this->_slug ] = $site_clone;
20140
 
20141
+ $this->set_account_option(
20142
+ ( $is_backup ? 'prev_' : '' ) . 'sites',
20143
+ $sites,
20144
+ $store,
20145
+ $network_level_or_blog_id
20146
+ );
20147
+ }
20148
+
20149
+ /**
20150
+ * Stores the context site in the sites backup storage. This logic is used before deleting the site info so that it can be restored later on if necessary (e.g., if the automatic clone resolution attempt fails).
20151
+ *
20152
+ * @author Leo Fajardo (@leorw)
20153
+ * @since 2.5.0
20154
+ */
20155
+ private function back_up_site() {
20156
+ $this->_logger->entrance();
20157
+
20158
+ $site_clone = clone $this->_site;
20159
+
20160
+ $this->_store_site( true, null, $site_clone, true );
20161
  }
20162
 
20163
  /**
21398
  $this->switch_to_blog( $current_blog_id );
21399
  }
21400
 
21401
+ $result = $this->send_install_update( array(), true, true );
21402
  $is_valid = $this->is_api_result_entity( $result );
21403
  } else {
21404
+ $result = $this->send_installs_update( array(), true, true );
21405
  $is_valid = $this->is_api_result_object( $result, 'installs' );
21406
  }
21407
 
21411
  $this->switch_to_blog( $this->_storage->network_install_blog_id );
21412
  }
21413
 
21414
+ // Show API message only if not background sync or if paying customer.
21415
  if ( ! $background || $this->is_paying() ) {
21416
  // Try to ping API to see if not blocked.
21417
  if ( ! FS_Api::test() ) {
21421
  * @author Vova Feldman (@svovaf)
21422
  * @since 1.1.6 Only show message related to one of the Freemius powered plugins. Once it will be resolved it will fix the issue for all plugins anyways. There's no point to scare users with multiple error messages.
21423
  */
 
21424
 
21425
  if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) {
21426
+ // Add notice immediately if not a background sync.
21427
+ $add_notice = ( ! $background );
21428
+
21429
+ if ( ! $add_notice ) {
21430
+ $counter = (int) get_transient( '_fs_api_connection_retry_counter' );
21431
+
21432
+ // We only want to add the notice after 3 consecutive failures.
21433
+ $add_notice = ( 3 <= $counter );
21434
+
21435
+ if ( ! $add_notice ) {
21436
+ /**
21437
+ * Update counter transient only if notice shouldn't be added. If it is added the transient will be reset anyway, because the retries mechanism should only start counting if the admin isn't aware of the connectivity issue.
21438
+ *
21439
+ * Also, since the background sync happens once a day, setting the transient expiration for a week should be enough to count 3 failures, if there's an actual connectivity issue.
21440
+ */
21441
+ set_transient( '_fs_api_connection_retry_counter', $counter + 1, WP_FS__TIME_WEEK_IN_SEC );
21442
+ }
21443
+ }
21444
+
21445
+ // Add notice instantly for not-background sync and only after 3 failed attempts for background sync.
21446
+ if ( $add_notice ) {
21447
+ self::$_global_admin_notices->add(
21448
+ sprintf(
21449
+ $this->get_text_inline( 'Your server is blocking the access to Freemius\' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s', 'server-blocking-access' ),
21450
+ $this->get_plugin_name(),
21451
+ '<b>' . implode( ', ', $this->apply_filters( 'api_domains', array(
21452
+ 'api.freemius.com',
21453
+ 'wp.freemius.com'
21454
+ ) ) ) . '</b>'
21455
+ ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
21456
+ $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...',
21457
+ 'error',
21458
+ $background,
21459
+ 'api_blocked'
21460
+ );
21461
+
21462
+ // Notice was just shown, reset connectivity counter.
21463
+ delete_transient( '_fs_api_connection_retry_counter' );
21464
+ }
21465
  }
21466
+ } else if ( is_object( $result ) ) {
21467
  // Authentication params are broken.
21468
  $this->_admin_notices->add(
21469
  $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ),
21477
  return;
21478
  }
21479
 
21480
+ // API is working now. Delete the transient and start afresh.
21481
+ delete_transient('_fs_api_connection_retry_counter');
21482
+
21483
  if ( $is_site_level_sync ) {
21484
  $site = new FS_Site( $result );
21485
  } else {
21671
  }
21672
 
21673
  if ( ! $this->is_addon() &&
21674
+ $this->_site->is_beta() !== $site->is_beta()
21675
  ) {
21676
  // Beta flag updated.
21677
  $this->_site = $site;
22226
  if ( ! $this->is_api_result_entity( $plan ) ) {
22227
  // Some API error while trying to start the trial.
22228
  $this->_admin_notices->add(
22229
+ $this->get_api_error_message( $plan ),
 
22230
  $oops_text,
22231
  'error'
22232
  );
22404
  ) {
22405
  $this->_logger->entrance();
22406
 
22407
+ if ( $this->is_unresolved_clone( true ) ) {
22408
+ return false;
22409
+ }
22410
+
22411
  $switch_to_blog_id = null;
22412
 
22413
  /**
22414
  * @since 1.1.7.3 Check for plugin updates from Freemius only if opted-in.
22415
  * @since 1.1.7.4 Also check updates for add-ons.
22416
  */
22417
+ if (
22418
+ ( ! $this->is_registered() || ! FS_Permission_Manager::instance( $this )->is_essentials_tracking_allowed() ) &&
22419
  ! $this->_is_addon_id( $addon_id )
22420
  ) {
22421
  if ( ! is_multisite() ) {
22425
  $installs_map = $this->get_blog_install_map();
22426
 
22427
  foreach ( $installs_map as $blog_id => $install ) {
22428
+ if ( ! FS_Permission_Manager::instance( $this )->is_essentials_tracking_allowed( $blog_id ) ) {
22429
+ continue;
22430
+ }
22431
+
22432
  /**
22433
  * @var FS_Site $install
22434
  */
22525
  private function get_latest_download_api_url( $plugin_id = false ) {
22526
  $this->_logger->entrance();
22527
 
22528
+ $download_api_url = $this->get_api_site_scope()->get_signed_url(
22529
  $this->_get_latest_version_endpoint( $plugin_id, 'zip' )
22530
  );
22531
+
22532
+ return str_replace( 'http:', 'https:', $download_api_url );
22533
  }
22534
 
22535
  /**
22724
  private function update_email( $new_email ) {
22725
  $this->_logger->entrance();
22726
 
 
22727
  $api = $this->get_api_user_scope();
22728
  $user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,email,is_verified", 'put', array(
22729
  'email' => $new_email,
22739
  $this->_store_user();
22740
  } else {
22741
  // handle different error cases.
 
22742
  }
22743
 
22744
  return $user;
22814
  * @uses FS_Api
22815
  *
22816
  * @param string $candidate_email
22817
+ * @param string $transfer_type
22818
  *
22819
  * @return bool Is ownership change successfully initiated.
22820
  */
22821
+ private function init_change_owner( $candidate_email, $transfer_type ) {
22822
  $this->_logger->entrance();
22823
 
22824
+ $installs_info_by_slug_map = $this->get_parent_and_addons_installs_info();
22825
+ $install_ids = array();
22826
+
22827
+ foreach ( $installs_info_by_slug_map as $slug => $install_info ) {
22828
+ $install = $install_info['install'];
22829
+
22830
+ if ( $this->_user->id != $install->user_id ) {
22831
+ // Skip add-on installs that are not owned by the parent product's install's owner.
22832
+ continue;
22833
+ }
22834
+
22835
+ $install_ids[ $slug ] = $install->id;
22836
+ }
22837
+
22838
  $api = $this->get_api_site_scope();
22839
  $result = $api->call( "/users/{$this->_user->id}.json", 'put', array(
22840
  'email' => $candidate_email,
22841
+ 'transfer_type' => $transfer_type,
22842
+ 'install_ids' => implode( ',', array_values( $install_ids ) ),
22843
  'after_confirm_url' => $this->_get_admin_page_url(
22844
  'account',
22845
  array( 'fs_action' => 'change_owner' )
22861
  private function complete_change_owner() {
22862
  $this->_logger->entrance();
22863
 
22864
+ $install_ids = fs_request_get( 'install_ids' );
22865
+
22866
+ if ( ! empty( $install_ids ) ) {
22867
+ $install_ids = explode( ',', $install_ids );
22868
+
22869
+ foreach ( $install_ids as $key => $install_id ) {
22870
+ if ( ! FS_Site::is_valid_id( $install_id ) ) {
22871
+ unset( $install_ids[ $key ] );
22872
+ }
22873
+ }
22874
+ }
22875
+
22876
+ if ( ! is_array( $install_ids ) ) {
22877
+ $install_ids = array();
22878
+ }
22879
+
22880
+ $user = new FS_User();
22881
+ $user->id = fs_request_get( 'user_id' );
22882
+ $user->public_key = fs_request_get( 'user_public_key' );
22883
+ $user->secret_key = fs_request_get( 'user_secret_key' );
22884
 
22885
+ $prev_user = $this->_user;
22886
+ $this->_user = $user;
22887
+
22888
+ $result = $this->get_api_user_scope( true )->get(
22889
+ "/installs.json?install_ids=" . implode( ',', $install_ids )
22890
+ );
22891
+
22892
+ $current_blog_sites = self::get_all_sites( $this->get_module_type() );
22893
+
22894
+ if ( $this->is_api_result_object( $result, 'installs' ) ) {
22895
+ $site_id_slug_map = array();
22896
+
22897
+ foreach ( $current_blog_sites as $slug => $site ) {
22898
+ $site_id_slug_map[ $site->id ] = $slug;
22899
+ }
22900
+
22901
+ foreach ( $result->installs as $install ) {
22902
+ $site = new FS_Site( $install );
22903
+
22904
+ if ( ! isset( $site_id_slug_map[ $install->id ] ) ) {
22905
+ continue;
22906
+ }
22907
+
22908
+ $current_blog_sites[ $site_id_slug_map[ $install->id ] ] = clone $site;
22909
+
22910
+ if ( $this->_site->id == $site->id ) {
22911
+ $this->_site = $site;
22912
+ }
22913
+ }
22914
+ }
22915
 
22916
  // Validate install's user and given user.
22917
  if ( $user->id != $this->_site->user_id ) {
22918
+ $this->_user = $prev_user;
22919
+
22920
  return false;
22921
  }
22922
 
22923
+ $this->set_account_option( 'sites', $current_blog_sites, true );
 
22924
 
22925
  // Fetch new user information.
 
22926
  $user_result = $this->get_api_user_scope( true )->get();
22927
  $user = new FS_User( $user_result );
22928
  $this->_user = $user;
22929
 
22930
+ $this->_set_account( $user, $this->_site );
22931
+
22932
+ $remove_user = true;
22933
+ $all_modules_sites = self::get_all_modules_sites();
22934
+
22935
+ foreach ( $all_modules_sites as $sites_by_module_type ) {
22936
+ foreach ( $sites_by_module_type as $sites_by_slug ) {
22937
+ foreach ( $sites_by_slug as $site ) {
22938
+ if ( $prev_user->id == $site->user_id ) {
22939
+ $remove_user = false;
22940
+ break;
22941
+ }
22942
+ }
22943
+
22944
+ if ( ! $remove_user ) {
22945
+ break;
22946
+ }
22947
+ }
22948
+
22949
+ if ( ! $remove_user ) {
22950
+ break;
22951
+ }
22952
+ }
22953
+
22954
+ if ( $remove_user ) {
22955
+ $users = self::get_all_users();
22956
+
22957
+ if ( isset( $users[ $prev_user->id ] ) ) {
22958
+ unset( $users[ $prev_user->id ] );
22959
+ } else {
22960
+ // If the prev user wasn't found by the key, iterate over the users collection.
22961
+ foreach ( $users as $key => $user ) {
22962
+ if ( $user->id == $prev_user->id ) {
22963
+ unset( $users[ $key ] );
22964
+ break;
22965
+ }
22966
+ }
22967
+ }
22968
+
22969
+ $this->set_account_option( 'users', $users, true );
22970
+ }
22971
 
22972
  return true;
22973
  }
23212
 
23213
  if ( $is_parent_plugin_action ) {
23214
  if ( $is_network_action && ! empty( $blog_id ) ) {
23215
+ if ( $this->is_registered( true ) ) {
23216
+ if ( $this->is_tracking_prohibited( $blog_id ) ) {
23217
+ if ( $this->toggle_site_tracking( true, $blog_id ) ) {
23218
  $this->_admin_notices->add(
23219
+ sprintf( $this->get_text_inline( 'Sharing diagnostic data with %s helps to provide functionality that\'s more relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the plugin should be translated and tailored to.', 'opt-out-message-appreciation' ), "<b>{$this->get_plugin_title()}</b>" ),
23220
  $this->get_text_inline( 'Thank you!', 'thank-you' )
23221
  );
23222
  }
23223
  } else {
23224
+ if ( $this->toggle_site_tracking( false, $blog_id ) ) {
23225
+ $install = $this->get_install_by_blog_id( $blog_id );
23226
+
23227
  $this->_admin_notices->add(
23228
  sprintf(
23229
+ $this->get_text_inline( 'Diagnostic data will no longer be sent from %s to %s.', 'opted-out-successfully' ),
23230
+ self::get_unfiltered_site_url( $blog_id, true ),
23231
+ "<b>{$this->get_plugin_title()}</b>"
 
 
 
 
 
23232
  )
23233
  );
23234
  }
23376
  $state = fs_request_get( 'state', 'init' );
23377
  switch ( $state ) {
23378
  case 'init':
23379
+ $candidate_email = fs_request_get( 'candidate_email' );
23380
+ $transfer_type = fs_request_get( 'transfer_type' );
23381
 
23382
+ if ( $this->init_change_owner( $candidate_email, $transfer_type ) ) {
23383
+ if ( 'transfer' === $transfer_type ) {
23384
+ $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours.', 'change-owner-request-sent-x-transfer' ), '<b>' . $this->_user->email . '</b>' ) );
23385
+ } else {
23386
+ $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder.', 'change-owner-request-sent-x' ), '<b>' . $this->_user->email . '</b>' ) );
23387
+ }
23388
  }
23389
  break;
23390
  case 'owner_confirmed':
23407
 
23408
  return;
23409
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23410
  case 'update_user_name':
23411
  check_admin_referer( 'update_user_name' );
23412
 
23568
 
23569
  fs_enqueue_local_style( 'fs_affiliation', '/admin/affiliation.css' );
23570
 
23571
+ $is_bundle_context = $this->has_bundle_context();
23572
+
23573
+ $plugin_title = $this->get_plugin_title();
23574
+
23575
+ if ( $is_bundle_context ) {
23576
+ $plugin_title = $this->plugin_affiliate_terms->plugin_title;
23577
+
23578
+ // Add the suffix "Bundle" only if the word is not present in the title itself.
23579
+ if ( false === mb_stripos( $plugin_title, fs_text_inline( 'Bundle', 'bundle' ) ) ) {
23580
+ $plugin_title = $this->apply_filters(
23581
+ 'formatted_bundle_title',
23582
+ $plugin_title . ' ' . fs_text_inline( 'Bundle', 'bundle' )
23583
+ );
23584
+ }
23585
+ }
23586
+
23587
+ $vars = array(
23588
+ 'id' => $this->_module_id,
23589
+ 'plugin_title' => $plugin_title,
23590
+ );
23591
  echo $this->apply_filters( "/forms/affiliation.php", fs_get_template( '/forms/affiliation.php', $vars ) );
23592
  }
23593
 
23688
  }
23689
 
23690
  /* Pricing & Upgrade
23691
+ ------------------------------------------------------------------------------------------------------------------*/
23692
  /**
23693
  * Render pricing page.
23694
  *
23830
  }
23831
 
23832
  static function _clean_admin_content_section_hook() {
23833
+ $hide_admin_notices = true;
23834
+
23835
+ if ( fs_request_is_action( 'allow_clone_resolution_notice' ) ) {
23836
+ check_admin_referer( 'fs_allow_clone_resolution_notice' );
23837
+
23838
+ $hide_admin_notices = false;
23839
+ }
23840
+
23841
+ if ( $hide_admin_notices ) {
23842
+ self::_hide_admin_notices();
23843
+ }
23844
 
23845
  // Hide footer.
23846
  echo '<style>#wpfooter { display: none !important; }</style>';
23857
  }
23858
 
23859
  /* CSS & JavaScript
23860
+ ------------------------------------------------------------------------------------------------------------------*/
23861
  /* function _enqueue_script($handle, $src) {
23862
+ $url = plugins_url( substr( WP_FS__DIR_JS, strlen( $this->_plugin_dir_path ) ) . '/assets/js/' . $src );
23863
 
23864
+ $this->_logger->entrance( 'script = ' . $url );
23865
 
23866
+ wp_enqueue_script( $handle, $url );
23867
+ }*/
23868
 
23869
  /* SDK
23870
+ ------------------------------------------------------------------------------------------------------------------*/
23871
  private $_user_api;
23872
 
23873
  /**
23879
  *
23880
  * @return FS_Api
23881
  */
23882
+ function get_api_user_scope( $flush = false ) {
23883
  if ( ! isset( $this->_user_api ) || $flush ) {
23884
  $this->_user_api = $this->get_api_user_scope_by_user( $this->_user );
23885
  }
23958
  $this->_site->public_key,
23959
  ! $this->is_live(),
23960
  $this->_site->secret_key,
23961
+ $this->get_sdk_version(),
23962
+ self::get_unfiltered_site_url()
23963
  );
23964
  }
23965
 
23966
  return $this->_site_api;
23967
  }
23968
 
23969
+ /**
23970
+ * @author Leo Fajardo (@leorw)
23971
+ * @since 2.5.0
23972
+ *
23973
+ * @param string $path
23974
+ * @param string $method
23975
+ * @param array $params
23976
+ * @param bool $flush_instance
23977
+ *
23978
+ * @return array|mixed|string|void
23979
+ * @throws Freemius_Exception
23980
+ */
23981
+ private function api_site_call( $path, $method = 'GET', $params = array(), $flush_instance = false ) {
23982
+ $result = $this->get_api_site_scope( $flush_instance )->call( $path, $method, $params );
23983
+
23984
+ /**
23985
+ * Checks if the local install's URL is different from the remote install's URL, update the local install if necessary, and then run the clone handler if the install's URL is different from the URL of the site.
23986
+ *
23987
+ * @author Leo Fajardo (@leorw)
23988
+ * @since 2.5.0
23989
+ */
23990
+ if (
23991
+ $this->is_registered() &&
23992
+ FS_Api::is_api_result_entity( $result ) &&
23993
+ isset( $result->url )
23994
+ ) {
23995
+ $stored_local_url = trailingslashit( $this->_site->url );
23996
+ $stored_remote_url = trailingslashit( $result->url );
23997
+
23998
+ if ( $stored_local_url !== $stored_remote_url ) {
23999
+ $this->_site->url = $result->url;
24000
+ $this->_store_site();
24001
+ }
24002
+
24003
+ if ( fs_strip_url_protocol( $stored_remote_url ) !== self::get_unfiltered_site_url( null, true, true ) ) {
24004
+ FS_Clone_Manager::instance()->maybe_run_clone_resolution();
24005
+ }
24006
+ }
24007
+
24008
+ return $result;
24009
+ }
24010
+
24011
  private $_plugin_api;
24012
 
24013
  /**
24443
  }
24444
 
24445
  /* Action Links
24446
+ ------------------------------------------------------------------------------------------------------------------*/
24447
  private $_action_links_hooked = false;
24448
  private $_action_links = array();
24449
 
24614
 
24615
  $this->_logger->entrance();
24616
 
 
 
 
 
 
 
 
 
 
24617
  if ( $this->is_only_premium() && $this->is_free_plan() ) {
24618
  // Don't add tracking links for premium-only products that were opted-in by relation (add-on or a parent product) before activating any license.
24619
  return;
24621
 
24622
  if (
24623
  $this->is_addon() &&
24624
+ ! $this->is_only_premium()
 
24625
  ) {
24626
+ $parent = $this->get_parent_instance();
24627
+
24628
+ if ( is_object( $parent ) && $parent->is_anonymous() ) {
24629
+ return;
24630
+ }
24631
  }
24632
 
24633
  if ( fs_is_network_admin() ) {
24676
  }
24677
  }
24678
 
24679
+ if ( $this->add_ajax_action( 'toggle_permission_tracking', array( &$this, '_toggle_permission_tracking_callback' ) ) ) {
 
 
 
 
 
 
 
 
24680
  return;
24681
  }
24682
 
24683
  $link_text_id = '';
24684
  $url = '#';
24685
 
24686
+ if ( $this->is_registered( true ) ) {
24687
+ if ( $this->is_registered() && $this->is_tracking_allowed() ) {
24688
  $link_text_id = $this->get_text_inline( 'Opt Out', 'opt-out' );
24689
  } else {
24690
  $link_text_id = $this->get_text_inline( 'Opt In', 'opt-in' );
24881
  */
24882
  private function is_premium_version_installed() {
24883
  $premium_plugin_basename = $this->premium_plugin_basename();
 
24884
 
24885
+ if ( $this->is_theme() ) {
24886
+ return $this->can_activate_theme( $this->get_premium_slug() );
24887
+ }
24888
+
24889
+ return file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_plugin_basename ) );
24890
  }
24891
 
24892
  /**
24922
  * @author Leo Fajardo (@leorw)
24923
  * @since 2.2.1
24924
  */
24925
+ $premium_theme_slug_or_plugin_basename = $this->is_theme() ?
24926
+ $this->get_premium_slug() :
24927
+ $this->premium_plugin_basename();
24928
 
24929
  return sprintf(
24930
  /* translators: %1$s: Product title; %2$s: Plan title */
24933
  $plan_title,
24934
  sprintf(
24935
  '<a style="margin-left: 10px;" href="%s"><button class="button button-primary">%s</button></a>',
24936
+ ( $this->is_theme() ?
24937
+ wp_nonce_url( 'themes.php?action=activate&amp;stylesheet=' . $premium_theme_slug_or_plugin_basename, 'switch-theme_' . $premium_theme_slug_or_plugin_basename ) :
24938
+ wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $premium_theme_slug_or_plugin_basename, 'activate-plugin_' . $premium_theme_slug_or_plugin_basename ) ),
24939
  esc_html( sprintf(
24940
  /* translators: %s: Plan title */
24941
  $this->get_text_inline( 'Activate %s features', 'activate-x-features' ),
24960
  ) ),
24961
  $deactivation_step,
24962
  $this->get_text_inline( 'Upload and activate the downloaded version', 'upload-and-activate' ),
24963
+ $this->apply_filters( 'upload_and_install_video_url', '//bit.ly/wp-' . $this->_module_type . '-upload' ),
24964
  $this->get_text_inline( 'How to upload and activate?', 'howto-upload-activate' )
24965
  );
24966
  }
25413
  function _tabs_capture() {
25414
  $this->_logger->entrance();
25415
 
25416
+ if (
25417
+ ! $this->is_product_settings_page() ||
25418
+ ! $this->should_page_include_tabs() ||
25419
+ ! $this->is_matching_url( $this->main_menu_url() )
25420
  ) {
25421
  return;
25422
  }
25470
  function _store_tabs_styles() {
25471
  $this->_logger->entrance();
25472
 
25473
+ if (
25474
+ ! $this->is_product_settings_page() ||
25475
+ ! $this->should_page_include_tabs() ||
25476
+ ! $this->is_matching_url( $this->main_menu_url() )
25477
  ) {
25478
  return;
25479
  }
freemius/includes/class-fs-admin-notices.php CHANGED
@@ -125,13 +125,10 @@
125
  $is_sticky = false,
126
  $id = '',
127
  $store_if_sticky = true,
128
- $network_level_or_blog_id = null
 
129
  ) {
130
- if ( $this->should_use_network_notices( $id, $network_level_or_blog_id ) ) {
131
- $notices = $this->_network_notices;
132
- } else {
133
- $notices = $this->get_site_notices( $network_level_or_blog_id );
134
- }
135
 
136
  $notices->add(
137
  $message,
@@ -139,7 +136,11 @@
139
  $type,
140
  $is_sticky,
141
  $id,
142
- $store_if_sticky
 
 
 
 
143
  );
144
  }
145
 
@@ -149,8 +150,9 @@
149
  *
150
  * @param string|string[] $ids
151
  * @param int|null $network_level_or_blog_id
 
152
  */
153
- function remove_sticky( $ids, $network_level_or_blog_id = null ) {
154
  if ( ! is_array( $ids ) ) {
155
  $ids = array( $ids );
156
  }
@@ -161,7 +163,7 @@
161
  $notices = $this->get_site_notices( $network_level_or_blog_id );
162
  }
163
 
164
- return $notices->remove_sticky( $ids );
165
  }
166
 
167
  /**
@@ -176,11 +178,7 @@
176
  * @return bool
177
  */
178
  function has_sticky( $id, $network_level_or_blog_id = null ) {
179
- if ( $this->should_use_network_notices( $id, $network_level_or_blog_id ) ) {
180
- $notices = $this->_network_notices;
181
- } else {
182
- $notices = $this->get_site_notices( $network_level_or_blog_id );
183
- }
184
 
185
  return $notices->has_sticky( $id );
186
  }
@@ -200,6 +198,7 @@
200
  * @param string|null $plugin_title
201
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and
202
  * blog admin pages.
 
203
  */
204
  function add_sticky(
205
  $message,
@@ -209,15 +208,30 @@
209
  $network_level_or_blog_id = null,
210
  $wp_user_id = null,
211
  $plugin_title = null,
212
- $is_network_and_blog_admins = false
 
 
213
  ) {
214
- if ( $this->should_use_network_notices( $id, $network_level_or_blog_id ) ) {
215
- $notices = $this->_network_notices;
216
- } else {
217
- $notices = $this->get_site_notices( $network_level_or_blog_id );
218
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
- $notices->add_sticky( $message, $id, $title, $type, $wp_user_id, $plugin_title, $is_network_and_blog_admins );
221
  }
222
 
223
  /**
@@ -227,21 +241,22 @@
227
  * @since 2.0.0
228
  *
229
  * @param int|null $network_level_or_blog_id
 
230
  */
231
- function clear_all_sticky( $network_level_or_blog_id = null ) {
232
  if ( ! $this->_is_multisite ||
233
  false === $network_level_or_blog_id ||
234
  0 == $network_level_or_blog_id ||
235
  is_null( $network_level_or_blog_id )
236
  ) {
237
  $notices = $this->get_site_notices( $network_level_or_blog_id );
238
- $notices->clear_all_sticky();
239
  }
240
 
241
  if ( $this->_is_multisite &&
242
  ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
243
  ) {
244
- $this->_network_notices->clear_all_sticky();
245
  }
246
  }
247
 
@@ -317,5 +332,22 @@
317
  return fs_is_network_admin();
318
  }
319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  #endregion
321
  }
125
  $is_sticky = false,
126
  $id = '',
127
  $store_if_sticky = true,
128
+ $network_level_or_blog_id = null,
129
+ $is_dimissible = null
130
  ) {
131
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
 
 
 
 
132
 
133
  $notices->add(
134
  $message,
136
  $type,
137
  $is_sticky,
138
  $id,
139
+ $store_if_sticky,
140
+ null,
141
+ null,
142
+ false,
143
+ $is_dimissible
144
  );
145
  }
146
 
150
  *
151
  * @param string|string[] $ids
152
  * @param int|null $network_level_or_blog_id
153
+ * @param bool $store
154
  */
155
+ function remove_sticky( $ids, $network_level_or_blog_id = null, $store = true ) {
156
  if ( ! is_array( $ids ) ) {
157
  $ids = array( $ids );
158
  }
163
  $notices = $this->get_site_notices( $network_level_or_blog_id );
164
  }
165
 
166
+ return $notices->remove_sticky( $ids, $store );
167
  }
168
 
169
  /**
178
  * @return bool
179
  */
180
  function has_sticky( $id, $network_level_or_blog_id = null ) {
181
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
 
 
 
 
182
 
183
  return $notices->has_sticky( $id );
184
  }
198
  * @param string|null $plugin_title
199
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and
200
  * blog admin pages.
201
+ * @param bool $is_dismissible
202
  */
203
  function add_sticky(
204
  $message,
208
  $network_level_or_blog_id = null,
209
  $wp_user_id = null,
210
  $plugin_title = null,
211
+ $is_network_and_blog_admins = false,
212
+ $is_dismissible = true,
213
+ $data = array()
214
  ) {
215
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
216
+
217
+ $notices->add_sticky( $message, $id, $title, $type, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dismissible, $data );
218
+ }
219
+
220
+ /**
221
+ * Retrieves the data of a sticky notice.
222
+ *
223
+ * @author Leo Fajardo (@leorw)
224
+ * @since 2.4.3
225
+ *
226
+ * @param string $id
227
+ * @param int|null $network_level_or_blog_id
228
+ *
229
+ * @return array|null
230
+ */
231
+ function get_sticky( $id, $network_level_or_blog_id ) {
232
+ $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );
233
 
234
+ return $notices->get_sticky( $id );
235
  }
236
 
237
  /**
241
  * @since 2.0.0
242
  *
243
  * @param int|null $network_level_or_blog_id
244
+ * @param bool $is_temporary
245
  */
246
+ function clear_all_sticky( $network_level_or_blog_id = null, $is_temporary = false ) {
247
  if ( ! $this->_is_multisite ||
248
  false === $network_level_or_blog_id ||
249
  0 == $network_level_or_blog_id ||
250
  is_null( $network_level_or_blog_id )
251
  ) {
252
  $notices = $this->get_site_notices( $network_level_or_blog_id );
253
+ $notices->clear_all_sticky( $is_temporary );
254
  }
255
 
256
  if ( $this->_is_multisite &&
257
  ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
258
  ) {
259
+ $this->_network_notices->clear_all_sticky( $is_temporary );
260
  }
261
  }
262
 
332
  return fs_is_network_admin();
333
  }
334
 
335
+ /**
336
+ * Retrieves an instance of FS_Admin_Notice_Manager.
337
+ *
338
+ * @author Leo Fajardo (@leorw)
339
+ * @since 2.5.0
340
+ *
341
+ * @param string $id
342
+ * @param int|null $network_level_or_blog_id
343
+ *
344
+ * @return FS_Admin_Notice_Manager
345
+ */
346
+ private function get_site_or_network_notices( $id, $network_level_or_blog_id ) {
347
+ return $this->should_use_network_notices( $id, $network_level_or_blog_id ) ?
348
+ $this->_network_notices :
349
+ $this->get_site_notices( $network_level_or_blog_id );
350
+ }
351
+
352
  #endregion
353
  }
freemius/includes/class-fs-api.php CHANGED
@@ -64,6 +64,14 @@
64
  */
65
  private $_sdk_version;
66
 
 
 
 
 
 
 
 
 
67
  /**
68
  * @param string $slug
69
  * @param string $scope 'app', 'developer', 'user' or 'install'.
@@ -72,6 +80,7 @@
72
  * @param bool $is_sandbox
73
  * @param bool|string $secret_key Element's secret key.
74
  * @param null|string $sdk_version
 
75
  *
76
  * @return FS_Api
77
  */
@@ -82,14 +91,15 @@
82
  $public_key,
83
  $is_sandbox,
84
  $secret_key = false,
85
- $sdk_version = null
 
86
  ) {
87
  $identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
88
 
89
  if ( ! isset( self::$_instances[ $identifier ] ) ) {
90
  self::_init();
91
 
92
- self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version );
93
  }
94
 
95
  return self::$_instances[ $identifier ];
@@ -123,6 +133,7 @@
123
  * @param bool|string $secret_key Element's secret key.
124
  * @param bool $is_sandbox
125
  * @param null|string $sdk_version
 
126
  */
127
  private function __construct(
128
  $slug,
@@ -131,12 +142,14 @@
131
  $public_key,
132
  $secret_key,
133
  $is_sandbox,
134
- $sdk_version
 
135
  ) {
136
  $this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox );
137
 
138
  $this->_slug = $slug;
139
  $this->_sdk_version = $sdk_version;
 
140
  $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
141
  }
142
 
@@ -198,6 +211,17 @@
198
  }
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
 
201
  $result = $this->_api->Api( $path, $method, $params );
202
 
203
  if ( null !== $result &&
64
  */
65
  private $_sdk_version;
66
 
67
+ /**
68
+ * @author Leo Fajardo (@leorw)
69
+ * @since 2.5.0
70
+ *
71
+ * @var string
72
+ */
73
+ private $_url;
74
+
75
  /**
76
  * @param string $slug
77
  * @param string $scope 'app', 'developer', 'user' or 'install'.
80
  * @param bool $is_sandbox
81
  * @param bool|string $secret_key Element's secret key.
82
  * @param null|string $sdk_version
83
+ * @param null|string $url
84
  *
85
  * @return FS_Api
86
  */
91
  $public_key,
92
  $is_sandbox,
93
  $secret_key = false,
94
+ $sdk_version = null,
95
+ $url = null
96
  ) {
97
  $identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
98
 
99
  if ( ! isset( self::$_instances[ $identifier ] ) ) {
100
  self::_init();
101
 
102
+ self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version, $url );
103
  }
104
 
105
  return self::$_instances[ $identifier ];
133
  * @param bool|string $secret_key Element's secret key.
134
  * @param bool $is_sandbox
135
  * @param null|string $sdk_version
136
+ * @param null|string $url
137
  */
138
  private function __construct(
139
  $slug,
142
  $public_key,
143
  $secret_key,
144
  $is_sandbox,
145
+ $sdk_version,
146
+ $url
147
  ) {
148
  $this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox );
149
 
150
  $this->_slug = $slug;
151
  $this->_sdk_version = $sdk_version;
152
+ $this->_url = $url;
153
  $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
154
  }
155
 
211
  }
212
  }
213
 
214
+ /**
215
+ * @since 2.5.0 Include the site's URL, if available, in all API requests that are going through the API manager.
216
+ */
217
+ if ( ! empty( $this->_url ) ) {
218
+ if ( false === strpos( $path, 'url=' ) &&
219
+ ! isset( $params['url'] )
220
+ ) {
221
+ $path = add_query_arg( 'url', $this->_url, $path );
222
+ }
223
+ }
224
+
225
  $result = $this->_api->Api( $path, $method, $params );
226
 
227
  if ( null !== $result &&
freemius/includes/class-fs-lock.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.1
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Class FS_Lock
15
+ *
16
+ * @author Vova Feldman (@svovaf)
17
+ * @since 2.5.1
18
+ */
19
+ class FS_Lock {
20
+ /**
21
+ * @var int Random ID representing the current PHP thread.
22
+ */
23
+ private static $_thread_id;
24
+ /**
25
+ * @var string
26
+ */
27
+ private $_lock_id;
28
+
29
+ /**
30
+ * @param string $lock_id
31
+ */
32
+ function __construct( $lock_id ) {
33
+ if ( ! fs_starts_with( $lock_id, WP_FS___OPTION_PREFIX ) ) {
34
+ $lock_id = WP_FS___OPTION_PREFIX . $lock_id;
35
+ }
36
+
37
+ $this->_lock_id = $lock_id;
38
+
39
+ if ( ! isset( self::$_thread_id ) ) {
40
+ self::$_thread_id = mt_rand( 0, 32000 );
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Try to acquire lock. If the lock is already set or is being acquired by another locker, don't do anything.
46
+ *
47
+ * @param int $expiration
48
+ *
49
+ * @return bool TRUE if successfully acquired lock.
50
+ */
51
+ function try_lock( $expiration = 0 ) {
52
+ if ( $this->is_locked() ) {
53
+ // Already locked.
54
+ return false;
55
+ }
56
+
57
+ set_site_transient( $this->_lock_id, self::$_thread_id, $expiration );
58
+
59
+ if ( $this->has_lock() ) {
60
+ $this->lock($expiration);
61
+
62
+ return true;
63
+ }
64
+
65
+ return false;
66
+ }
67
+
68
+ /**
69
+ * Acquire lock regardless if it's already acquired by another locker or not.
70
+ *
71
+ * @author Vova Feldman (@svovaf)
72
+ * @since 2.1.0
73
+ *
74
+ * @param int $expiration
75
+ */
76
+ function lock( $expiration = 0 ) {
77
+ set_site_transient( $this->_lock_id, true, $expiration );
78
+ }
79
+
80
+ /**
81
+ * Checks if lock is currently acquired.
82
+ *
83
+ * @author Vova Feldman (@svovaf)
84
+ * @since 2.1.0
85
+ *
86
+ * @return bool
87
+ */
88
+ function is_locked() {
89
+ return ( false !== get_site_transient( $this->_lock_id ) );
90
+ }
91
+
92
+ /**
93
+ * Unlock the lock.
94
+ *
95
+ * @author Vova Feldman (@svovaf)
96
+ * @since 2.1.0
97
+ */
98
+ function unlock() {
99
+ delete_site_transient( $this->_lock_id );
100
+ }
101
+
102
+ /**
103
+ * Checks if lock is currently acquired by the current locker.
104
+ *
105
+ * @return bool
106
+ */
107
+ protected function has_lock() {
108
+ return ( self::$_thread_id == get_site_transient( $this->_lock_id ) );
109
+ }
110
+ }
freemius/includes/class-fs-logger.php CHANGED
@@ -38,9 +38,10 @@
38
  private static $_HOOKED_FOOTER = false;
39
 
40
  private function __construct( $id, $on = false, $echo = false ) {
 
 
41
  $this->_id = $id;
42
 
43
- $bt = debug_backtrace();
44
  $caller = $bt[2];
45
 
46
  if ( false !== strpos( $caller['file'], 'plugins' ) ) {
38
  private static $_HOOKED_FOOTER = false;
39
 
40
  private function __construct( $id, $on = false, $echo = false ) {
41
+ $bt = debug_backtrace();
42
+
43
  $this->_id = $id;
44
 
 
45
  $caller = $bt[2];
46
 
47
  if ( false !== strpos( $caller['file'], 'plugins' ) ) {
freemius/includes/class-fs-plugin-updater.php CHANGED
@@ -240,7 +240,11 @@
240
  * @since 2.0.0
241
  */
242
  private function add_transient_filters() {
243
- if ( $this->_fs->is_premium() && ! $this->_fs->is_tracking_allowed() ) {
 
 
 
 
244
  $this->_logger->log( 'Opted out sites cannot receive automatic software updates.' );
245
 
246
  return;
@@ -413,7 +417,7 @@
413
 
414
  $themes_update = get_site_transient( 'update_themes' );
415
  if ( ! isset( $themes_update->response[ $theme_basename ] ) ||
416
- empty( $themes_update->response[ $theme_basename ]['package'] )
417
  ) {
418
  return $prepared_themes;
419
  }
@@ -610,11 +614,9 @@
610
  if ( ! isset( $this->_translation_updates ) ) {
611
  $this->_translation_updates = array();
612
 
613
- if ( current_user_can( 'update_languages' ) ) {
614
- $translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug );
615
- if ( ! empty( $translation_updates ) ) {
616
- $this->_translation_updates = $translation_updates;
617
- }
618
  }
619
  }
620
 
@@ -634,7 +636,7 @@
634
  foreach ( $this->_translation_updates as $translation_update ) {
635
  $lang = $translation_update['language'];
636
  if ( ! isset( $current_plugin_translation_updates_map[ $lang ] ) ||
637
- version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' )
638
  ) {
639
  $current_plugin_translation_updates_map[ $lang ] = $translation_update;
640
  }
@@ -648,7 +650,7 @@
648
  }
649
 
650
  /**
651
- * Get module's required data for the updates mechanism.
652
  *
653
  * @author Vova Feldman (@svovaf)
654
  * @since 2.0.0
@@ -658,13 +660,14 @@
658
  * @return object
659
  */
660
  function get_update_details( FS_Plugin_Tag $new_version ) {
661
- $update = new stdClass();
662
- $update->slug = $this->_fs->get_slug();
663
- $update->new_version = $new_version->version;
664
- $update->url = WP_FS__ADDRESS;
665
- $update->package = $new_version->url;
666
- $update->tested = $new_version->tested_up_to_version;
667
- $update->requires = $new_version->requires_platform_version;
 
668
 
669
  $icon = $this->_fs->get_local_icon_url();
670
 
@@ -806,9 +809,9 @@
806
  $basename = $this->_fs->get_plugin_basename();
807
 
808
  if ( ! is_object( $transient_data ) ||
809
- ! isset( $transient_data->response ) ||
810
  ! is_array( $transient_data->response ) ||
811
- empty( $transient_data->response[ $basename ] )
812
  ) {
813
  return;
814
  }
@@ -1093,6 +1096,7 @@ if ( !isset($info->error) ) {
1093
  if ( ! $plugin_in_repo ) {
1094
  $data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created;
1095
  $data->requires = $new_version->requires_platform_version;
 
1096
  $data->tested = $new_version->tested_up_to_version;
1097
  }
1098
 
@@ -1146,9 +1150,28 @@ if ( !isset($info->error) ) {
1146
  }
1147
  }
1148
 
 
 
 
 
1149
  return $data;
1150
  }
1151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1152
  /**
1153
  * @author Vova Feldman (@svovaf)
1154
  * @since 1.2.1.7
240
  * @since 2.0.0
241
  */
242
  private function add_transient_filters() {
243
+ if (
244
+ $this->_fs->is_premium() &&
245
+ $this->_fs->is_registered() &&
246
+ ! FS_Permission_Manager::instance( $this->_fs )->is_essentials_tracking_allowed()
247
+ ) {
248
  $this->_logger->log( 'Opted out sites cannot receive automatic software updates.' );
249
 
250
  return;
417
 
418
  $themes_update = get_site_transient( 'update_themes' );
419
  if ( ! isset( $themes_update->response[ $theme_basename ] ) ||
420
+ empty( $themes_update->response[ $theme_basename ]['package'] )
421
  ) {
422
  return $prepared_themes;
423
  }
614
  if ( ! isset( $this->_translation_updates ) ) {
615
  $this->_translation_updates = array();
616
 
617
+ $translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug );
618
+ if ( ! empty( $translation_updates ) ) {
619
+ $this->_translation_updates = $translation_updates;
 
 
620
  }
621
  }
622
 
636
  foreach ( $this->_translation_updates as $translation_update ) {
637
  $lang = $translation_update['language'];
638
  if ( ! isset( $current_plugin_translation_updates_map[ $lang ] ) ||
639
+ version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' )
640
  ) {
641
  $current_plugin_translation_updates_map[ $lang ] = $translation_update;
642
  }
650
  }
651
 
652
  /**
653
+ * Get module's required data for the updates' mechanism.
654
  *
655
  * @author Vova Feldman (@svovaf)
656
  * @since 2.0.0
660
  * @return object
661
  */
662
  function get_update_details( FS_Plugin_Tag $new_version ) {
663
+ $update = new stdClass();
664
+ $update->slug = $this->_fs->get_slug();
665
+ $update->new_version = $new_version->version;
666
+ $update->url = WP_FS__ADDRESS;
667
+ $update->package = $new_version->url;
668
+ $update->tested = self::get_tested_wp_version( $new_version->tested_up_to_version );
669
+ $update->requires = $new_version->requires_platform_version;
670
+ $update->requires_php = $new_version->requires_programming_language_version;
671
 
672
  $icon = $this->_fs->get_local_icon_url();
673
 
809
  $basename = $this->_fs->get_plugin_basename();
810
 
811
  if ( ! is_object( $transient_data ) ||
812
+ ! isset( $transient_data->response ) ||
813
  ! is_array( $transient_data->response ) ||
814
+ empty( $transient_data->response[ $basename ] )
815
  ) {
816
  return;
817
  }
1096
  if ( ! $plugin_in_repo ) {
1097
  $data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created;
1098
  $data->requires = $new_version->requires_platform_version;
1099
+ $data->requires_php = $new_version->requires_programming_language_version;
1100
  $data->tested = $new_version->tested_up_to_version;
1101
  }
1102
 
1150
  }
1151
  }
1152
 
1153
+ if ( ! empty( $data->tested ) ) {
1154
+ $data->tested = self::get_tested_wp_version( $data->tested );
1155
+ }
1156
+
1157
  return $data;
1158
  }
1159
 
1160
+ /**
1161
+ * @since 2.5.3 If the current WordPress version is a patch of the tested version (e.g., 6.1.2 is a patch of 6.1), then override the tested version with the patch so developers won't need to release a new version just to bump the latest supported WP version.
1162
+ *
1163
+ * @param string|null $tested_up_to
1164
+ *
1165
+ * @return string|null
1166
+ */
1167
+ private static function get_tested_wp_version( $tested_up_to ) {
1168
+ $current_wp_version = get_bloginfo( 'version' );
1169
+
1170
+ return ( ! empty($tested_up_to) && fs_starts_with( $current_wp_version, $tested_up_to . '.' ) ) ?
1171
+ $current_wp_version :
1172
+ $tested_up_to;
1173
+ }
1174
+
1175
  /**
1176
  * @author Vova Feldman (@svovaf)
1177
  * @since 1.2.1.7
freemius/includes/class-fs-storage.php CHANGED
@@ -15,9 +15,11 @@
15
  *
16
  * A wrapper class for handling network level and single site level storage.
17
  *
18
- * @property bool $is_network_activation
19
- * @property int $network_install_blog_id
20
- * @property object $sync_cron
 
 
21
  */
22
  class FS_Storage {
23
  /**
@@ -72,6 +74,16 @@
72
  */
73
  private static $_NETWORK_OPTIONS_MAP;
74
 
 
 
 
 
 
 
 
 
 
 
75
  /**
76
  * @author Leo Fajardo (@leorw)
77
  *
@@ -142,10 +154,17 @@
142
  * @param string $key
143
  * @param mixed $value
144
  * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
 
145
  * @param bool $flush
146
  */
147
- function store( $key, $value, $network_level_or_blog_id = null, $flush = true ) {
148
- if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
 
 
 
 
 
 
149
  $this->_network_storage->store( $key, $value, $flush );
150
  } else {
151
  $storage = $this->get_site_storage( $network_level_or_blog_id );
@@ -199,11 +218,17 @@
199
  * @param string $key
200
  * @param mixed $default
201
  * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
 
202
  *
203
  * @return mixed
204
  */
205
- function get( $key, $default = false, $network_level_or_blog_id = null ) {
206
- if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
 
 
 
 
 
207
  return $this->_network_storage->get( $key, $default );
208
  } else {
209
  $storage = $this->get_site_storage( $network_level_or_blog_id );
@@ -289,19 +314,6 @@
289
  // Migrate option to the network storage.
290
  $this->_network_storage->store( $option, $this->_storage->{$option}, false );
291
 
292
- /**
293
- * Remove the option from site level storage.
294
- *
295
- * IMPORTANT:
296
- * The line below is intentionally commented since we want to preserve the option
297
- * on the site storage level for "downgrade compatibility". Basically, if the user
298
- * will downgrade to an older version of the plugin with the prev storage structure,
299
- * it will continue working.
300
- *
301
- * @todo After a few releases we can remove this.
302
- */
303
- // $this->_storage->remove($option, false);
304
-
305
  $updated = true;
306
  }
307
  }
@@ -336,63 +348,61 @@
336
  private static function load_network_options_map() {
337
  self::$_NETWORK_OPTIONS_MAP = array(
338
  // Network level options.
339
- 'affiliate_application_data' => 0,
340
- 'beta_data' => 0,
341
- 'connectivity_test' => 0,
342
- 'handle_gdpr_admin_notice' => 0,
343
- 'has_trial_plan' => 0,
344
- 'install_sync_timestamp' => 0,
345
- 'install_sync_cron' => 0,
346
- 'is_anonymous_ms' => 0,
347
- 'is_network_activated' => 0,
348
- 'is_on' => 0,
349
- 'is_plugin_new_install' => 0,
350
- 'network_install_blog_id' => 0,
351
- 'pending_sites_info' => 0,
352
- 'plugin_last_version' => 0,
353
- 'plugin_main_file' => 0,
354
- 'plugin_version' => 0,
355
- 'sdk_downgrade_mode' => 0,
356
- 'sdk_last_version' => 0,
357
- 'sdk_upgrade_mode' => 0,
358
- 'sdk_version' => 0,
359
- 'sticky_optin_added_ms' => 0,
360
- 'subscriptions' => 0,
361
- 'sync_timestamp' => 0,
362
- 'sync_cron' => 0,
363
- 'was_plugin_loaded' => 0,
364
- 'network_user_id' => 0,
365
- 'plugin_upgrade_mode' => 0,
366
- 'plugin_downgrade_mode' => 0,
367
- 'is_network_connected' => 0,
368
  /**
369
- * Special flag that is used when a super-admin upgrades to the new version of the SDK that
370
- * supports network level integration, when the connection decision wasn't made for all of the
371
- * sites in the network.
372
  */
373
- 'is_network_activation' => 0,
374
- 'license_migration' => 0,
375
 
376
  // When network activated, then network level.
377
- 'install_timestamp' => 1,
378
- 'prev_is_premium' => 1,
379
- 'require_license_activation' => 1,
380
 
381
  // If not network activated OR delegated, then site level.
382
- 'activation_timestamp' => 2,
383
- 'expired_license_notice_shown' => 2,
384
- 'is_whitelabeled' => 2,
385
- 'last_license_key' => 2,
386
- 'last_license_user_id' => 2,
387
- 'prev_user_id' => 2,
388
- 'sticky_optin_added' => 2,
389
- 'uninstall_reason' => 2,
390
- 'is_pending_activation' => 2,
391
- 'pending_license_key' => 2,
392
- 'is_extensions_tracking_allowed' => 2,
393
 
394
  // Site level options.
395
- 'is_anonymous' => 3,
 
396
  );
397
  }
398
 
@@ -403,25 +413,33 @@
403
  * @since 2.0.0
404
  *
405
  * @param string $key
 
406
  *
407
- * @return bool|mixed
408
  */
409
- private function is_multisite_option( $key ) {
410
  if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
411
  self::load_network_options_map();
412
  }
413
 
414
- if ( ! isset( self::$_NETWORK_OPTIONS_MAP[ $key ] ) ) {
 
 
 
 
 
 
 
415
  // Option not found -> use site level storage.
416
  return false;
417
  }
418
 
419
- if ( 0 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
420
  // Option found and set to always use the network level storage on a multisite.
421
  return true;
422
  }
423
 
424
- if ( 3 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
425
  // Option found and set to always use the site level storage on a multisite.
426
  return false;
427
  }
@@ -430,12 +448,15 @@
430
  return false;
431
  }
432
 
433
- if ( 1 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
434
  // Network activated.
435
  return true;
436
  }
437
 
438
- if ( 2 === self::$_NETWORK_OPTIONS_MAP[ $key ] && ! $this->_is_delegated_connection ) {
 
 
 
439
  // Network activated and not delegated.
440
  return true;
441
  }
@@ -448,10 +469,15 @@
448
  *
449
  * @param string $key
450
  * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
 
451
  *
452
  * @return bool
453
  */
454
- private function should_use_network_storage( $key, $network_level_or_blog_id = null ) {
 
 
 
 
455
  if ( ! $this->_is_multisite ) {
456
  // Not a multisite environment.
457
  return false;
@@ -463,12 +489,12 @@
463
  }
464
 
465
  if ( is_bool( $network_level_or_blog_id ) ) {
466
- // Explicitly specified whether should use the network or blog level storage.
467
  return $network_level_or_blog_id;
468
  }
469
 
470
  // Determine which storage to use based on the option.
471
- return $this->is_multisite_option( $key );
472
  }
473
 
474
  /**
@@ -529,4 +555,4 @@
529
  }
530
 
531
  #endregion
532
- }
15
  *
16
  * A wrapper class for handling network level and single site level storage.
17
  *
18
+ * @property bool $is_network_activation
19
+ * @property int $network_install_blog_id
20
+ * @property bool|null $is_extensions_tracking_allowed
21
+ * @property bool|null $is_diagnostic_tracking_allowed
22
+ * @property object $sync_cron
23
  */
24
  class FS_Storage {
25
  /**
74
  */
75
  private static $_NETWORK_OPTIONS_MAP;
76
 
77
+ const OPTION_LEVEL_UNDEFINED = -1;
78
+ // The option should be stored on the network level.
79
+ const OPTION_LEVEL_NETWORK = 0;
80
+ // The option should be stored on the network level when the plugin is network-activated.
81
+ const OPTION_LEVEL_NETWORK_ACTIVATED = 1;
82
+ // The option should be stored on the network level when the plugin is network-activated and the opt-in connection was NOT delegated to the sub-site admin.
83
+ const OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED = 2;
84
+ // The option should be stored on the site level.
85
+ const OPTION_LEVEL_SITE = 3;
86
+
87
  /**
88
  * @author Leo Fajardo (@leorw)
89
  *
154
  * @param string $key
155
  * @param mixed $value
156
  * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
157
+ * @param int $option_level Since 2.5.1
158
  * @param bool $flush
159
  */
160
+ function store(
161
+ $key,
162
+ $value,
163
+ $network_level_or_blog_id = null,
164
+ $option_level = self::OPTION_LEVEL_UNDEFINED,
165
+ $flush = true
166
+ ) {
167
+ if ( $this->should_use_network_storage( $key, $network_level_or_blog_id, $option_level ) ) {
168
  $this->_network_storage->store( $key, $value, $flush );
169
  } else {
170
  $storage = $this->get_site_storage( $network_level_or_blog_id );
218
  * @param string $key
219
  * @param mixed $default
220
  * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
221
+ * @param int $option_level Since 2.5.1
222
  *
223
  * @return mixed
224
  */
225
+ function get(
226
+ $key,
227
+ $default = false,
228
+ $network_level_or_blog_id = null,
229
+ $option_level = self::OPTION_LEVEL_UNDEFINED
230
+ ) {
231
+ if ( $this->should_use_network_storage( $key, $network_level_or_blog_id, $option_level ) ) {
232
  return $this->_network_storage->get( $key, $default );
233
  } else {
234
  $storage = $this->get_site_storage( $network_level_or_blog_id );
314
  // Migrate option to the network storage.
315
  $this->_network_storage->store( $option, $this->_storage->{$option}, false );
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  $updated = true;
318
  }
319
  }
348
  private static function load_network_options_map() {
349
  self::$_NETWORK_OPTIONS_MAP = array(
350
  // Network level options.
351
+ 'affiliate_application_data' => self::OPTION_LEVEL_NETWORK,
352
+ 'beta_data' => self::OPTION_LEVEL_NETWORK,
353
+ 'connectivity_test' => self::OPTION_LEVEL_NETWORK,
354
+ 'handle_gdpr_admin_notice' => self::OPTION_LEVEL_NETWORK,
355
+ 'has_trial_plan' => self::OPTION_LEVEL_NETWORK,
356
+ 'install_sync_timestamp' => self::OPTION_LEVEL_NETWORK,
357
+ 'install_sync_cron' => self::OPTION_LEVEL_NETWORK,
358
+ 'is_anonymous_ms' => self::OPTION_LEVEL_NETWORK,
359
+ 'is_network_activated' => self::OPTION_LEVEL_NETWORK,
360
+ 'is_on' => self::OPTION_LEVEL_NETWORK,
361
+ 'is_plugin_new_install' => self::OPTION_LEVEL_NETWORK,
362
+ 'network_install_blog_id' => self::OPTION_LEVEL_NETWORK,
363
+ 'pending_sites_info' => self::OPTION_LEVEL_NETWORK,
364
+ 'plugin_last_version' => self::OPTION_LEVEL_NETWORK,
365
+ 'plugin_main_file' => self::OPTION_LEVEL_NETWORK,
366
+ 'plugin_version' => self::OPTION_LEVEL_NETWORK,
367
+ 'sdk_downgrade_mode' => self::OPTION_LEVEL_NETWORK,
368
+ 'sdk_last_version' => self::OPTION_LEVEL_NETWORK,
369
+ 'sdk_upgrade_mode' => self::OPTION_LEVEL_NETWORK,
370
+ 'sdk_version' => self::OPTION_LEVEL_NETWORK,
371
+ 'sticky_optin_added_ms' => self::OPTION_LEVEL_NETWORK,
372
+ 'subscriptions' => self::OPTION_LEVEL_NETWORK,
373
+ 'sync_timestamp' => self::OPTION_LEVEL_NETWORK,
374
+ 'sync_cron' => self::OPTION_LEVEL_NETWORK,
375
+ 'was_plugin_loaded' => self::OPTION_LEVEL_NETWORK,
376
+ 'network_user_id' => self::OPTION_LEVEL_NETWORK,
377
+ 'plugin_upgrade_mode' => self::OPTION_LEVEL_NETWORK,
378
+ 'plugin_downgrade_mode' => self::OPTION_LEVEL_NETWORK,
379
+ 'is_network_connected' => self::OPTION_LEVEL_NETWORK,
380
  /**
381
+ * Special flag that is used when a super-admin upgrades to the new version of the SDK that supports network level integration, when the connection decision wasn't made for all the sites in the network.
 
 
382
  */
383
+ 'is_network_activation' => self::OPTION_LEVEL_NETWORK,
384
+ 'license_migration' => self::OPTION_LEVEL_NETWORK,
385
 
386
  // When network activated, then network level.
387
+ 'install_timestamp' => self::OPTION_LEVEL_NETWORK_ACTIVATED,
388
+ 'prev_is_premium' => self::OPTION_LEVEL_NETWORK_ACTIVATED,
389
+ 'require_license_activation' => self::OPTION_LEVEL_NETWORK_ACTIVATED,
390
 
391
  // If not network activated OR delegated, then site level.
392
+ 'activation_timestamp' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
393
+ 'expired_license_notice_shown' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
394
+ 'is_whitelabeled' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
395
+ 'last_license_key' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
396
+ 'last_license_user_id' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
397
+ 'prev_user_id' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
398
+ 'sticky_optin_added' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
399
+ 'uninstall_reason' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
400
+ 'is_pending_activation' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
401
+ 'pending_license_key' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED,
 
402
 
403
  // Site level options.
404
+ 'is_anonymous' => self::OPTION_LEVEL_SITE,
405
+ 'clone_id' => self::OPTION_LEVEL_SITE,
406
  );
407
  }
408
 
413
  * @since 2.0.0
414
  *
415
  * @param string $key
416
+ * @param int $option_level Since 2.5.1
417
  *
418
+ * @return bool
419
  */
420
+ private function is_multisite_option( $key, $option_level = self::OPTION_LEVEL_UNDEFINED ) {
421
  if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
422
  self::load_network_options_map();
423
  }
424
 
425
+ if (
426
+ self::OPTION_LEVEL_UNDEFINED === $option_level &&
427
+ isset( self::$_NETWORK_OPTIONS_MAP[ $key ] )
428
+ ) {
429
+ $option_level = self::$_NETWORK_OPTIONS_MAP[ $key ];
430
+ }
431
+
432
+ if ( self::OPTION_LEVEL_UNDEFINED === $option_level ) {
433
  // Option not found -> use site level storage.
434
  return false;
435
  }
436
 
437
+ if ( self::OPTION_LEVEL_NETWORK === $option_level ) {
438
  // Option found and set to always use the network level storage on a multisite.
439
  return true;
440
  }
441
 
442
+ if ( self::OPTION_LEVEL_SITE === $option_level ) {
443
  // Option found and set to always use the site level storage on a multisite.
444
  return false;
445
  }
448
  return false;
449
  }
450
 
451
+ if ( self::OPTION_LEVEL_NETWORK_ACTIVATED === $option_level ) {
452
  // Network activated.
453
  return true;
454
  }
455
 
456
+ if (
457
+ self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED === $option_level &&
458
+ ! $this->_is_delegated_connection
459
+ ) {
460
  // Network activated and not delegated.
461
  return true;
462
  }
469
  *
470
  * @param string $key
471
  * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
472
+ * @param int $option_level Since 2.5.1
473
  *
474
  * @return bool
475
  */
476
+ private function should_use_network_storage(
477
+ $key,
478
+ $network_level_or_blog_id = null,
479
+ $option_level = self::OPTION_LEVEL_UNDEFINED
480
+ ) {
481
  if ( ! $this->_is_multisite ) {
482
  // Not a multisite environment.
483
  return false;
489
  }
490
 
491
  if ( is_bool( $network_level_or_blog_id ) ) {
492
+ // Explicitly specified whether it should use the network or blog level storage.
493
  return $network_level_or_blog_id;
494
  }
495
 
496
  // Determine which storage to use based on the option.
497
+ return $this->is_multisite_option( $key, $option_level );
498
  }
499
 
500
  /**
555
  }
556
 
557
  #endregion
558
+ }
freemius/includes/class-fs-user-lock.php CHANGED
@@ -10,18 +10,16 @@
10
  exit;
11
  }
12
 
 
 
13
  /**
14
  * Class FS_User_Lock
15
  */
16
  class FS_User_Lock {
17
  /**
18
- * @var int
19
- */
20
- private $_wp_user_id;
21
- /**
22
- * @var int
23
  */
24
- private $_thread_id;
25
 
26
  #--------------------------------------------------------------------------------
27
  #region Singleton
@@ -49,10 +47,10 @@
49
  #endregion
50
 
51
  private function __construct() {
52
- $this->_wp_user_id = Freemius::get_current_wp_user_id();
53
- $this->_thread_id = mt_rand( 0, 32000 );
54
- }
55
 
 
 
56
 
57
  /**
58
  * Try to acquire lock. If the lock is already set or is being acquired by another locker, don't do anything.
@@ -65,20 +63,7 @@
65
  * @return bool TRUE if successfully acquired lock.
66
  */
67
  function try_lock( $expiration = 0 ) {
68
- if ( $this->is_locked() ) {
69
- // Already locked.
70
- return false;
71
- }
72
-
73
- set_site_transient( "locked_{$this->_wp_user_id}", $this->_thread_id, $expiration );
74
-
75
- if ( $this->has_lock() ) {
76
- set_site_transient( "locked_{$this->_wp_user_id}", true, $expiration );
77
-
78
- return true;
79
- }
80
-
81
- return false;
82
  }
83
 
84
  /**
@@ -90,19 +75,7 @@
90
  * @param int $expiration
91
  */
92
  function lock( $expiration = 0 ) {
93
- set_site_transient( "locked_{$this->_wp_user_id}", true, $expiration );
94
- }
95
-
96
- /**
97
- * Checks if lock is currently acquired.
98
- *
99
- * @author Vova Feldman (@svovaf)
100
- * @since 2.1.0
101
- *
102
- * @return bool
103
- */
104
- function is_locked() {
105
- return ( false !== get_site_transient( "locked_{$this->_wp_user_id}" ) );
106
  }
107
 
108
  /**
@@ -112,15 +85,6 @@
112
  * @since 2.1.0
113
  */
114
  function unlock() {
115
- delete_site_transient( "locked_{$this->_wp_user_id}" );
116
- }
117
-
118
- /**
119
- * Checks if lock is currently acquired by the current locker.
120
- *
121
- * @return bool
122
- */
123
- private function has_lock() {
124
- return ( $this->_thread_id == get_site_transient( "locked_{$this->_wp_user_id}" ) );
125
  }
126
  }
10
  exit;
11
  }
12
 
13
+ require_once WP_FS__DIR_INCLUDES . '/class-fs-lock.php';
14
+
15
  /**
16
  * Class FS_User_Lock
17
  */
18
  class FS_User_Lock {
19
  /**
20
+ * @var FS_Lock
 
 
 
 
21
  */
22
+ private $_lock;
23
 
24
  #--------------------------------------------------------------------------------
25
  #region Singleton
47
  #endregion
48
 
49
  private function __construct() {
50
+ $current_user_id = Freemius::get_current_wp_user_id();
 
 
51
 
52
+ $this->_lock = new FS_Lock( "locked_{$current_user_id}" );
53
+ }
54
 
55
  /**
56
  * Try to acquire lock. If the lock is already set or is being acquired by another locker, don't do anything.
63
  * @return bool TRUE if successfully acquired lock.
64
  */
65
  function try_lock( $expiration = 0 ) {
66
+ return $this->_lock->try_lock( $expiration );
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
68
 
69
  /**
75
  * @param int $expiration
76
  */
77
  function lock( $expiration = 0 ) {
78
+ $this->_lock->lock( $expiration );
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
 
81
  /**
85
  * @since 2.1.0
86
  */
87
  function unlock() {
88
+ $this->_lock->unlock();
 
 
 
 
 
 
 
 
 
89
  }
90
  }
freemius/includes/customizer/class-fs-customizer-upsell-control.php CHANGED
@@ -101,11 +101,11 @@
101
  }
102
  }
103
  }
 
 
104
  }
105
  }
106
 
107
- $this->json['plans'] = $pricing->plans;
108
-
109
  $this->json['strings'] = array(
110
  'plan' => $this->fs->get_text_x_inline( 'Plan', 'as product pricing plan', 'plan' ),
111
  );
101
  }
102
  }
103
  }
104
+
105
+ $this->json['plans'] = $pricing->plans;
106
  }
107
  }
108
 
 
 
109
  $this->json['strings'] = array(
110
  'plan' => $this->fs->get_text_x_inline( 'Plan', 'as product pricing plan', 'plan' ),
111
  );
freemius/includes/entities/class-fs-affiliate-terms.php CHANGED
@@ -84,6 +84,10 @@
84
  * @var bool If `true`, allow referrals from any site.
85
  */
86
  public $is_any_site_allowed;
 
 
 
 
87
 
88
  #endregion Properties
89
 
84
  * @var bool If `true`, allow referrals from any site.
85
  */
86
  public $is_any_site_allowed;
87
+ /**
88
+ * @var string $plugin_title Title of the plugin. This is used in case we are showing affiliate form for a Bundle instead of the `plugin` in context.
89
+ */
90
+ public $plugin_title;
91
 
92
  #endregion Properties
93
 
freemius/includes/entities/class-fs-plugin-tag.php CHANGED
@@ -23,6 +23,10 @@
23
  * @var string
24
  */
25
  public $requires_platform_version;
 
 
 
 
26
  /**
27
  * @var string
28
  */
23
  * @var string
24
  */
25
  public $requires_platform_version;
26
+ /**
27
+ * @var string
28
+ */
29
+ public $requires_programming_language_version;
30
  /**
31
  * @var string
32
  */
freemius/includes/entities/class-fs-site.php CHANGED
@@ -10,6 +10,9 @@
10
  exit;
11
  }
12
 
 
 
 
13
  class FS_Site extends FS_Scope_Entity {
14
  /**
15
  * @var number
@@ -39,10 +42,6 @@
39
  * @var string E.g. en-GB
40
  */
41
  public $language;
42
- /**
43
- * @var string E.g. UTF-8
44
- */
45
- public $charset;
46
  /**
47
  * @var string Platform version (e.g WordPress version).
48
  */
@@ -86,6 +85,8 @@
86
  * @author Leo Fajardo (@leorw)
87
  *
88
  * @since 1.2.1.5
 
 
89
  *
90
  * @var bool
91
  */
@@ -190,7 +191,13 @@
190
  // DesktopServer
191
  fs_ends_with( $subdomain, '.dev.cc' ) ||
192
  // Pressable
193
- fs_ends_with( $subdomain, '.mystagingwebsite.com' )
 
 
 
 
 
 
194
  );
195
  }
196
 
@@ -223,31 +230,25 @@
223
  }
224
 
225
  /**
226
- * @author Vova Feldman (@svovaf)
227
- * @since 2.0.0
228
  *
229
  * @return bool
230
  */
231
- function is_tracking_allowed() {
232
- return ( true !== $this->is_disconnected );
233
  }
234
 
235
  /**
236
- * @author Vova Feldman (@svovaf)
237
- * @since 2.0.0
238
  *
239
- * @return bool
240
- */
241
- function is_tracking_prohibited() {
242
- return ! $this->is_tracking_allowed();
243
- }
244
-
245
- /**
246
- * @author Edgar Melkonyan
247
  *
248
  * @return bool
249
  */
250
- function is_beta() {
251
- return ( isset( $this->is_beta ) && true === $this->is_beta );
 
 
252
  }
253
  }
10
  exit;
11
  }
12
 
13
+ /**
14
+ * @property int $blog_id
15
+ */
16
  class FS_Site extends FS_Scope_Entity {
17
  /**
18
  * @var number
42
  * @var string E.g. en-GB
43
  */
44
  public $language;
 
 
 
 
45
  /**
46
  * @var string Platform version (e.g WordPress version).
47
  */
85
  * @author Leo Fajardo (@leorw)
86
  *
87
  * @since 1.2.1.5
88
+ * @deprecated Since 2.5.1
89
+ * @todo Remove after a few releases.
90
  *
91
  * @var bool
92
  */
191
  // DesktopServer
192
  fs_ends_with( $subdomain, '.dev.cc' ) ||
193
  // Pressable
194
+ fs_ends_with( $subdomain, '.mystagingwebsite.com' ) ||
195
+ // WPMU DEV
196
+ ( fs_ends_with( $subdomain, '.tempurl.host' ) || fs_ends_with( $subdomain, '.wpmudev.host' ) ) ||
197
+ // Vendasta
198
+ ( fs_ends_with( $subdomain, '.websitepro-staging.com' ) || fs_ends_with( $subdomain, '.websitepro.hosting' ) ) ||
199
+ // InstaWP
200
+ fs_ends_with( $subdomain, '.instawp.xyz' )
201
  );
202
  }
203
 
230
  }
231
 
232
  /**
233
+ * @author Edgar Melkonyan
 
234
  *
235
  * @return bool
236
  */
237
+ function is_beta() {
238
+ return ( isset( $this->is_beta ) && true === $this->is_beta );
239
  }
240
 
241
  /**
242
+ * @author Leo Fajardo (@leorw)
243
+ * @since 2.5.1
244
  *
245
+ * @param string $site_url
 
 
 
 
 
 
 
246
  *
247
  * @return bool
248
  */
249
+ function is_clone( $site_url ) {
250
+ $clone_install_url = trailingslashit( fs_strip_url_protocol( $this->url ) );
251
+
252
+ return ( $clone_install_url !== $site_url );
253
  }
254
  }
freemius/includes/entities/class-fs-user.php CHANGED
@@ -56,7 +56,18 @@
56
  return ( isset( $this->is_verified ) && true === $this->is_verified );
57
  }
58
 
59
- static function get_type() {
 
 
 
 
 
 
 
 
 
 
 
60
  return 'user';
61
  }
62
  }
56
  return ( isset( $this->is_verified ) && true === $this->is_verified );
57
  }
58
 
59
+ /**
60
+ * @author Leo Fajardo (@leorw)
61
+ * @since 2.4.2
62
+ *
63
+ * @return bool
64
+ */
65
+ function is_beta() {
66
+ // Return `false` since this is just for backward compatibility.
67
+ return false;
68
+ }
69
+
70
+ static function get_type() {
71
  return 'user';
72
  }
73
  }
freemius/includes/fs-core-functions.php CHANGED
@@ -756,7 +756,7 @@
756
  } // If b has a priority and a does not, b wins.
757
  elseif ( isset( $a['priority'] ) && ! isset( $b['priority'] ) ) {
758
  return - 1;
759
- } // If neither has a priority or both priorities are equal its a tie.
760
  elseif ( ( ! isset( $a['priority'] ) && ! isset( $b['priority'] ) ) || $a['priority'] === $b['priority'] ) {
761
  return 0;
762
  }
@@ -770,6 +770,12 @@
770
  #region Localization
771
  #--------------------------------------------------------------------------------
772
 
 
 
 
 
 
 
773
  if ( ! function_exists( 'fs_text' ) ) {
774
  /**
775
  * Retrieve a translated text by key.
@@ -782,12 +788,10 @@
782
  *
783
  * @return string
784
  *
785
- * @global $fs_text , $fs_text_overrides
786
  */
787
  function fs_text( $key, $slug = 'freemius' ) {
788
- global $fs_text,
789
- $fs_module_info_text,
790
- $fs_text_overrides;
791
 
792
  if ( isset( $fs_text_overrides[ $slug ] ) ) {
793
  if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
@@ -800,22 +804,6 @@
800
  }
801
  }
802
 
803
- if ( ! isset( $fs_text ) ) {
804
- $dir = defined( 'WP_FS__DIR_INCLUDES' ) ?
805
- WP_FS__DIR_INCLUDES :
806
- dirname( __FILE__ );
807
-
808
- require_once $dir . '/i18n.php';
809
- }
810
-
811
- if ( isset( $fs_text[ $key ] ) ) {
812
- return $fs_text[ $key ];
813
- }
814
-
815
- if ( isset( $fs_module_info_text[ $key ] ) ) {
816
- return $fs_module_info_text[ $key ];
817
- }
818
-
819
  return $key;
820
  }
821
 
@@ -1349,7 +1337,7 @@
1349
  function fs_is_plugin_uninstall() {
1350
  return (
1351
  defined( 'WP_UNINSTALL_PLUGIN' ) ||
1352
- ( 0 < did_action( 'update_option_uninstall_plugins' ) )
1353
  );
1354
  }
1355
  }
756
  } // If b has a priority and a does not, b wins.
757
  elseif ( isset( $a['priority'] ) && ! isset( $b['priority'] ) ) {
758
  return - 1;
759
+ } // If neither has a priority or both priorities are equal it's a tie.
760
  elseif ( ( ! isset( $a['priority'] ) && ! isset( $b['priority'] ) ) || $a['priority'] === $b['priority'] ) {
761
  return 0;
762
  }
770
  #region Localization
771
  #--------------------------------------------------------------------------------
772
 
773
+ global $fs_text_overrides;
774
+
775
+ if ( ! isset( $fs_text_overrides ) ) {
776
+ $fs_text_overrides = array();
777
+ }
778
+
779
  if ( ! function_exists( 'fs_text' ) ) {
780
  /**
781
  * Retrieve a translated text by key.
788
  *
789
  * @return string
790
  *
791
+ * @global $fs_text_overrides
792
  */
793
  function fs_text( $key, $slug = 'freemius' ) {
794
+ global $fs_text_overrides;
 
 
795
 
796
  if ( isset( $fs_text_overrides[ $slug ] ) ) {
797
  if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
804
  }
805
  }
806
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
807
  return $key;
808
  }
809
 
1337
  function fs_is_plugin_uninstall() {
1338
  return (
1339
  defined( 'WP_UNINSTALL_PLUGIN' ) ||
1340
+ ( 0 < did_action( 'pre_uninstall_plugin' ) )
1341
  );
1342
  }
1343
  }
freemius/includes/fs-essential-functions.php CHANGED
@@ -149,111 +149,21 @@
149
 
150
  #endregion Core Redirect (copied from BuddyPress) -----------------------------------------
151
 
152
- if ( ! function_exists( '__fs' ) ) {
153
- global $fs_text_overrides;
154
-
155
- if ( ! isset( $fs_text_overrides ) ) {
156
- $fs_text_overrides = array();
157
- }
158
-
159
- /**
160
- * Retrieve a translated text by key.
161
- *
162
- * @deprecated Use `fs_text()` instead since methods starting with `__` trigger warnings in Php 7.
163
- * @todo Remove this method in the future.
164
- *
165
- * @author Vova Feldman (@svovaf)
166
- * @since 1.1.4
167
- *
168
- * @param string $key
169
- * @param string $slug
170
- *
171
- * @return string
172
- *
173
- * @global $fs_text, $fs_text_overrides
174
- */
175
- function __fs( $key, $slug = 'freemius' ) {
176
- _deprecated_function( __FUNCTION__, '2.0.0', 'fs_text()' );
177
-
178
- global $fs_text,
179
- $fs_module_info_text,
180
- $fs_text_overrides;
181
-
182
- if ( isset( $fs_text_overrides[ $slug ] ) ) {
183
- if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
184
- return $fs_text_overrides[ $slug ][ $key ];
185
- }
186
-
187
- $lower_key = strtolower( $key );
188
- if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
189
- return $fs_text_overrides[ $slug ][ $lower_key ];
190
- }
191
- }
192
-
193
- if ( ! isset( $fs_text ) ) {
194
- $dir = defined( 'WP_FS__DIR_INCLUDES' ) ?
195
- WP_FS__DIR_INCLUDES :
196
- dirname( __FILE__ );
197
-
198
- require_once $dir . '/i18n.php';
199
- }
200
-
201
- if ( isset( $fs_text[ $key ] ) ) {
202
- return $fs_text[ $key ];
203
- }
204
-
205
- if ( isset( $fs_module_info_text[ $key ] ) ) {
206
- return $fs_module_info_text[ $key ];
207
- }
208
-
209
- return $key;
210
- }
211
-
212
- /**
213
- * Output a translated text by key.
214
- *
215
- * @deprecated Use `fs_echo()` instead for consistency with `fs_text()`.
216
- *
217
- * @todo Remove this method in the future.
218
- *
219
- * @author Vova Feldman (@svovaf)
220
- * @since 1.1.4
221
- *
222
- * @param string $key
223
- * @param string $slug
224
- */
225
- function _efs( $key, $slug = 'freemius' ) {
226
- fs_echo( $key, $slug );
227
- }
228
- }
229
-
230
  if ( ! function_exists( 'fs_get_ip' ) ) {
231
  /**
232
- * Get client IP.
233
- *
 
 
234
  * @author Vova Feldman (@svovaf)
235
  * @since 1.1.2
236
  *
237
  * @return string|null
238
  */
239
  function fs_get_ip() {
240
- $fields = array(
241
- 'HTTP_CF_CONNECTING_IP',
242
- 'HTTP_CLIENT_IP',
243
- 'HTTP_X_FORWARDED_FOR',
244
- 'HTTP_X_FORWARDED',
245
- 'HTTP_FORWARDED_FOR',
246
- 'HTTP_FORWARDED',
247
- 'REMOTE_ADDR',
248
- );
249
-
250
- foreach ( $fields as $ip_field ) {
251
- if ( ! empty( $_SERVER[ $ip_field ] ) ) {
252
- return $_SERVER[ $ip_field ];
253
- }
254
- }
255
-
256
- return null;
257
  }
258
  }
259
 
149
 
150
  #endregion Core Redirect (copied from BuddyPress) -----------------------------------------
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  if ( ! function_exists( 'fs_get_ip' ) ) {
153
  /**
154
+ * Get server IP.
155
+ *
156
+ * @since 2.5.1 This method returns the server IP.
157
+ *
158
  * @author Vova Feldman (@svovaf)
159
  * @since 1.1.2
160
  *
161
  * @return string|null
162
  */
163
  function fs_get_ip() {
164
+ return empty( $_SERVER[ 'SERVER_ADDR' ] ) ?
165
+ null :
166
+ $_SERVER[ 'SERVER_ADDR' ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  }
168
  }
169
 
freemius/includes/fs-plugin-info-dialog.php CHANGED
@@ -311,6 +311,7 @@
311
  $data->version = $latest->version;
312
  $data->last_updated = $latest->created;
313
  $data->requires = $latest->requires_platform_version;
 
314
  $data->tested = $latest->tested_up_to_version;
315
  } else if ( ! empty( $current_addon_version ) ) {
316
  $data->version = $current_addon_version;
@@ -1306,8 +1307,8 @@
1306
  <?php endif ?>
1307
  </div>
1308
  </div>
1309
- </div>
1310
  <?php endforeach ?>
 
1311
  <?php endif ?>
1312
  <?php endif ?>
1313
  <div>
@@ -1344,7 +1345,10 @@
1344
  ?>
1345
  <li>
1346
  <strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>
1347
- :</strong> <?php echo esc_html( sprintf( fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires ) ) ?>
 
 
 
1348
  </li>
1349
  <?php
1350
  }
@@ -1356,6 +1360,19 @@
1356
  </li>
1357
  <?php
1358
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1359
  if ( ! empty( $api->downloaded ) ) {
1360
  ?>
1361
  <li>
@@ -1485,9 +1502,43 @@
1485
  </div>
1486
  <div id="section-holder" class="wrap">
1487
  <?php
1488
- if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1489
  echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been tested with your current version of WordPress.', 'not-tested-warning', $api->slug ) . '</p></div>';
1490
- } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
1491
  echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been marked as compatible with your version of WordPress.', 'not-compatible-warning', $api->slug ) . '</p></div>';
1492
  }
1493
 
311
  $data->version = $latest->version;
312
  $data->last_updated = $latest->created;
313
  $data->requires = $latest->requires_platform_version;
314
+ $data->requires_php = $latest->requires_programming_language_version;
315
  $data->tested = $latest->tested_up_to_version;
316
  } else if ( ! empty( $current_addon_version ) ) {
317
  $data->version = $current_addon_version;
1307
  <?php endif ?>
1308
  </div>
1309
  </div>
 
1310
  <?php endforeach ?>
1311
+ </div>
1312
  <?php endif ?>
1313
  <?php endif ?>
1314
  <div>
1345
  ?>
1346
  <li>
1347
  <strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>
1348
+ :</strong> <?php echo esc_html( sprintf(
1349
+ /* translators: %s: Version number. */
1350
+ fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires )
1351
+ ) ?>
1352
  </li>
1353
  <?php
1354
  }
1360
  </li>
1361
  <?php
1362
  }
1363
+ if ( ! empty( $api->requires_php ) ) {
1364
+ ?>
1365
+ <li>
1366
+ <strong><?php fs_esc_html_echo_inline( 'Requires PHP Version', 'requires-php-version', $api->slug ); ?>:</strong>
1367
+ <?php
1368
+ echo esc_html( sprintf(
1369
+ /* translators: %s: Version number. */
1370
+ fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires_php )
1371
+ );
1372
+ ?>
1373
+ </li>
1374
+ <?php
1375
+ }
1376
  if ( ! empty( $api->downloaded ) ) {
1377
  ?>
1378
  <li>
1502
  </div>
1503
  <div id="section-holder" class="wrap">
1504
  <?php
1505
+ $requires_php = isset( $api->requires_php ) ? $api->requires_php : null;
1506
+ $requires_wp = isset( $api->requires ) ? $api->requires : null;
1507
+
1508
+ $compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' );
1509
+
1510
+ // Strip off any -alpha, -RC, -beta, -src suffixes.
1511
+ list( $wp_version ) = explode( '-', $GLOBALS['wp_version'] );
1512
+
1513
+ $compatible_wp = empty( $requires_wp ) || version_compare( $wp_version, $requires_wp, '>=' );
1514
+ $tested_wp = ( empty( $api->tested ) || version_compare( $wp_version, $api->tested, '<=' ) );
1515
+
1516
+ if ( ! $compatible_php ) {
1517
+ echo '<div class="notice notice-error notice-alt"><p><strong>' . fs_text_inline( 'Error', 'error', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin requires a newer version of PHP.', 'newer-php-required-error', $api->slug );
1518
+
1519
+ if ( current_user_can( 'update_php' ) ) {
1520
+ $wp_get_update_php_url = function_exists( 'wp_get_update_php_url' ) ?
1521
+ wp_get_update_php_url() :
1522
+ 'https://wordpress.org/support/update-php/';
1523
+
1524
+ printf(
1525
+ /* translators: %s: URL to Update PHP page. */
1526
+ ' ' . fs_text_inline( '<a href="%s" target="_blank">Click here to learn more about updating PHP</a>.', 'php-update-learn-more-link', $api->slug ),
1527
+ esc_url( $wp_get_update_php_url )
1528
+ );
1529
+
1530
+ if ( function_exists( 'wp_update_php_annotation' ) ) {
1531
+ wp_update_php_annotation( '</p><p><em>', '</em>' );
1532
+ }
1533
+ } else {
1534
+ echo '</p>';
1535
+ }
1536
+ echo '</div>';
1537
+ }
1538
+
1539
+ if ( ! $tested_wp ) {
1540
  echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been tested with your current version of WordPress.', 'not-tested-warning', $api->slug ) . '</p></div>';
1541
+ } else if ( ! $compatible_wp ) {
1542
  echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been marked as compatible with your version of WordPress.', 'not-compatible-warning', $api->slug ) . '</p></div>';
1543
  }
1544
 
freemius/includes/managers/class-fs-admin-notice-manager.php CHANGED
@@ -160,7 +160,10 @@
160
  false,
161
  isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null,
162
  ! empty( $msg['plugin'] ) ? $msg['plugin'] : null,
163
- $is_network_and_blog_admins
 
 
 
164
  );
165
  }
166
  }
@@ -224,9 +227,6 @@
224
  return;
225
  }
226
 
227
-
228
- $show_admin_notices = ( ! $this->is_gutenberg_page() );
229
-
230
  foreach ( $this->_notices as $id => $msg ) {
231
  if ( isset( $msg['wp_user_id'] ) && is_numeric( $msg['wp_user_id'] ) ) {
232
  if ( get_current_user_id() != $msg['wp_user_id'] ) {
@@ -269,7 +269,7 @@
269
  $show_notice = call_user_func_array( 'fs_apply_filter', array(
270
  $this->_module_unique_affix,
271
  'show_admin_notice',
272
- $show_admin_notices,
273
  $msg
274
  ) );
275
 
@@ -323,6 +323,30 @@
323
  return false;
324
  }
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  /**
327
  * Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked.
328
  *
@@ -339,6 +363,8 @@
339
  * @param string|null $plugin_title
340
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
341
  * and blog admin pages.
 
 
342
  *
343
  * @uses add_action()
344
  */
@@ -351,7 +377,9 @@
351
  $store_if_sticky = true,
352
  $wp_user_id = null,
353
  $plugin_title = null,
354
- $is_network_and_blog_admins = false
 
 
355
  ) {
356
  $notices_type = $this->get_notices_type();
357
 
@@ -371,14 +399,16 @@
371
  }
372
 
373
  $message_object = array(
374
- 'message' => $message,
375
- 'title' => $title,
376
- 'type' => $type,
377
- 'sticky' => $is_sticky,
378
- 'id' => $id,
379
- 'manager_id' => $this->_id,
380
- 'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ),
381
- 'wp_user_id' => $wp_user_id,
 
 
382
  );
383
 
384
  if ( $is_sticky && $store_if_sticky ) {
@@ -393,15 +423,16 @@
393
  * @since 1.0.7
394
  *
395
  * @param string|string[] $ids
 
396
  */
397
- function remove_sticky( $ids ) {
398
  if ( ! is_array( $ids ) ) {
399
  $ids = array( $ids );
400
  }
401
 
402
  foreach ( $ids as $id ) {
403
  // Remove from sticky storage.
404
- $this->_sticky_storage->remove( $id );
405
 
406
  if ( isset( $this->_notices[ $id ] ) ) {
407
  unset( $this->_notices[ $id ] );
@@ -437,14 +468,32 @@
437
  * @param string|null $plugin_title
438
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
439
  * and blog admin pages.
 
 
440
  */
441
- function add_sticky( $message, $id, $title = '', $type = 'success', $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false ) {
442
  if ( ! empty( $this->_module_unique_affix ) ) {
443
  $message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message );
444
  $title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title );
445
  }
446
 
447
- $this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  }
449
 
450
  /**
@@ -452,9 +501,15 @@
452
  *
453
  * @author Vova Feldman (@svovaf)
454
  * @since 1.0.8
 
 
455
  */
456
- function clear_all_sticky() {
457
- $this->_sticky_storage->clear_all();
 
 
 
 
458
  }
459
 
460
  #--------------------------------------------------------------------------------
160
  false,
161
  isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null,
162
  ! empty( $msg['plugin'] ) ? $msg['plugin'] : null,
163
+ $is_network_and_blog_admins,
164
+ isset( $msg['dismissible'] ) ?
165
+ $msg['dismissible'] :
166
+ null
167
  );
168
  }
169
  }
227
  return;
228
  }
229
 
 
 
 
230
  foreach ( $this->_notices as $id => $msg ) {
231
  if ( isset( $msg['wp_user_id'] ) && is_numeric( $msg['wp_user_id'] ) ) {
232
  if ( get_current_user_id() != $msg['wp_user_id'] ) {
269
  $show_notice = call_user_func_array( 'fs_apply_filter', array(
270
  $this->_module_unique_affix,
271
  'show_admin_notice',
272
+ $this->show_admin_notices(),
273
  $msg
274
  ) );
275
 
323
  return false;
324
  }
325
 
326
+ /**
327
+ * Check if admin notices should be shown on page. E.g., we don't want to show notices in the Visual Editor.
328
+ *
329
+ * @author Xiaheng Chen (@xhchen)
330
+ * @since 2.4.2
331
+ *
332
+ * @return bool
333
+ */
334
+ function show_admin_notices() {
335
+ global $pagenow;
336
+
337
+ if ( 'about.php' === $pagenow ) {
338
+ // Don't show admin notices on the About page.
339
+ return false;
340
+ }
341
+
342
+ if ( $this->is_gutenberg_page() ) {
343
+ // Don't show admin notices in Gutenberg (visual editor).
344
+ return false;
345
+ }
346
+
347
+ return true;
348
+ }
349
+
350
  /**
351
  * Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked.
352
  *
363
  * @param string|null $plugin_title
364
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
365
  * and blog admin pages.
366
+ * @param bool|null $is_dismissible
367
+ * @param array $data
368
  *
369
  * @uses add_action()
370
  */
377
  $store_if_sticky = true,
378
  $wp_user_id = null,
379
  $plugin_title = null,
380
+ $is_network_and_blog_admins = false,
381
+ $is_dismissible = null,
382
+ $data = array()
383
  ) {
384
  $notices_type = $this->get_notices_type();
385
 
399
  }
400
 
401
  $message_object = array(
402
+ 'message' => $message,
403
+ 'title' => $title,
404
+ 'type' => $type,
405
+ 'sticky' => $is_sticky,
406
+ 'id' => $id,
407
+ 'manager_id' => $this->_id,
408
+ 'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ),
409
+ 'wp_user_id' => $wp_user_id,
410
+ 'dismissible' => $is_dismissible,
411
+ 'data' => $data
412
  );
413
 
414
  if ( $is_sticky && $store_if_sticky ) {
423
  * @since 1.0.7
424
  *
425
  * @param string|string[] $ids
426
+ * @param bool $store
427
  */
428
+ function remove_sticky( $ids, $store = true ) {
429
  if ( ! is_array( $ids ) ) {
430
  $ids = array( $ids );
431
  }
432
 
433
  foreach ( $ids as $id ) {
434
  // Remove from sticky storage.
435
+ $this->_sticky_storage->remove( $id, $store );
436
 
437
  if ( isset( $this->_notices[ $id ] ) ) {
438
  unset( $this->_notices[ $id ] );
468
  * @param string|null $plugin_title
469
  * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
470
  * and blog admin pages.
471
+ * @param bool $is_dimissible
472
+ * @param array $data
473
  */
474
+ function add_sticky( $message, $id, $title = '', $type = 'success', $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false, $is_dimissible = true, $data = array() ) {
475
  if ( ! empty( $this->_module_unique_affix ) ) {
476
  $message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message );
477
  $title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title );
478
  }
479
 
480
+ $this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dimissible, $data );
481
+ }
482
+
483
+ /**
484
+ * Retrieves the data of an sticky notice.
485
+ *
486
+ * @author Leo Fajardo (@leorw)
487
+ * @since 2.4.3
488
+ *
489
+ * @param string $id Message ID.
490
+ *
491
+ * @return array|null
492
+ */
493
+ function get_sticky( $id ) {
494
+ return isset( $this->_sticky_storage->{$id} ) ?
495
+ $this->_sticky_storage->{$id} :
496
+ null;
497
  }
498
 
499
  /**
501
  *
502
  * @author Vova Feldman (@svovaf)
503
  * @since 1.0.8
504
+ *
505
+ * @param bool $is_temporary @since 2.5.1
506
  */
507
+ function clear_all_sticky( $is_temporary = false ) {
508
+ if ( $is_temporary ) {
509
+ $this->_notices = array();
510
+ } else {
511
+ $this->_sticky_storage->clear_all();
512
+ }
513
  }
514
 
515
  #--------------------------------------------------------------------------------
freemius/includes/managers/class-fs-clone-manager.php ADDED
@@ -0,0 +1,1660 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @author Leo Fajardo (@leorw)
7
+ * @since 2.5.0
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Manages the detection of clones and provides the logged-in WordPress user with options for manually resolving them.
16
+ *
17
+ * @since 2.5.0
18
+ *
19
+ * @property int $clone_identification_timestamp
20
+ * @property int $temporary_duplicate_mode_selection_timestamp
21
+ * @property int $temporary_duplicate_notice_shown_timestamp
22
+ * @property string $request_handler_id
23
+ * @property int $request_handler_timestamp
24
+ * @property int $request_handler_retries_count
25
+ * @property bool $hide_manual_resolution
26
+ * @property array $new_blog_install_map
27
+ */
28
+ class FS_Clone_Manager {
29
+ /**
30
+ * @var FS_Option_Manager
31
+ */
32
+ private $_storage;
33
+ /**
34
+ * @var FS_Option_Manager
35
+ */
36
+ private $_network_storage;
37
+ /**
38
+ * @var FS_Admin_Notices
39
+ */
40
+ private $_notices;
41
+ /**
42
+ * @var FS_Logger
43
+ */
44
+ protected $_logger;
45
+
46
+ /**
47
+ * @var int 3 minutes
48
+ */
49
+ const CLONE_RESOLUTION_MAX_EXECUTION_TIME = 180;
50
+ /**
51
+ * @var int
52
+ */
53
+ const CLONE_RESOLUTION_MAX_RETRIES = 3;
54
+ /**
55
+ * @var int
56
+ */
57
+ const TEMPORARY_DUPLICATE_PERIOD = WP_FS__TIME_WEEK_IN_SEC * 2;
58
+ /**
59
+ * @var string
60
+ */
61
+ const OPTION_NAME = 'clone_resolution';
62
+ /**
63
+ * @var string
64
+ */
65
+ const OPTION_MANAGER_NAME = 'clone_management';
66
+ /**
67
+ * @var string
68
+ */
69
+ const OPTION_TEMPORARY_DUPLICATE = 'temporary_duplicate';
70
+ /**
71
+ * @var string
72
+ */
73
+ const OPTION_LONG_TERM_DUPLICATE = 'long_term_duplicate';
74
+ /**
75
+ * @var string
76
+ */
77
+ const OPTION_NEW_HOME = 'new_home';
78
+
79
+ #--------------------------------------------------------------------------------
80
+ #region Singleton
81
+ #--------------------------------------------------------------------------------
82
+
83
+ /**
84
+ * @var FS_Clone_Manager
85
+ */
86
+ private static $_instance;
87
+
88
+ /**
89
+ * @return FS_Clone_Manager
90
+ */
91
+ static function instance() {
92
+ if ( ! isset( self::$_instance ) ) {
93
+ self::$_instance = new self();
94
+ }
95
+
96
+ return self::$_instance;
97
+ }
98
+
99
+ #endregion
100
+
101
+ private function __construct() {
102
+ $this->_storage = FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true );
103
+ $this->_network_storage = FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true, true );
104
+
105
+ $this->maybe_migrate_options();
106
+
107
+ $this->_notices = FS_Admin_Notices::instance( 'global_clone_resolution_notices', '', '', true );
108
+ $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . '_clone_manager', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
109
+ }
110
+
111
+ /**
112
+ * Migrate clone resolution options from 2.5.0 array-based structure, to a new flat structure.
113
+ *
114
+ * The reason this logic is not in a separate migration script is that we want to be 100% sure data is migrated before any execution of clone logic.
115
+ *
116
+ * @todo Delete this one in the future.
117
+ */
118
+ private function maybe_migrate_options() {
119
+ $storages = array(
120
+ $this->_storage,
121
+ $this->_network_storage
122
+ );
123
+
124
+ foreach ( $storages as $storage ) {
125
+ $clone_data = $storage->get_option( self::OPTION_NAME );
126
+ if ( is_array( $clone_data ) && ! empty( $clone_data ) ) {
127
+ foreach ( $clone_data as $key => $val ) {
128
+ if ( ! is_null( $val ) ) {
129
+ $storage->set_option( $key, $val );
130
+ }
131
+ }
132
+
133
+ $storage->unset_option( self::OPTION_NAME, true );
134
+ }
135
+ }
136
+ }
137
+
138
+ /**
139
+ * @author Leo Fajardo (@leorw)
140
+ * @since 2.5.0
141
+ */
142
+ function _init() {
143
+ if ( is_admin() ) {
144
+ if ( Freemius::is_admin_post() ) {
145
+ add_action( 'admin_post_fs_clone_resolution', array( $this, '_handle_clone_resolution' ) );
146
+ }
147
+
148
+ if ( Freemius::is_ajax() ) {
149
+ Freemius::add_ajax_action_static( 'handle_clone_resolution', array( $this, '_clone_resolution_action_ajax_handler' ) );
150
+ } else {
151
+ if (
152
+ empty( $this->get_clone_identification_timestamp() ) &&
153
+ (
154
+ ! fs_is_network_admin() ||
155
+ ! ( $this->is_clone_resolution_options_notice_shown() || $this->is_temporary_duplicate_notice_shown() )
156
+ )
157
+ ) {
158
+ $this->hide_clone_admin_notices();
159
+ } else if ( ! Freemius::is_cron() && ! Freemius::is_admin_post() ) {
160
+ $this->try_resolve_clone_automatically();
161
+ $this->maybe_show_clone_admin_notice();
162
+
163
+ add_action( 'admin_footer', array( $this, '_add_clone_resolution_javascript' ) );
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Retrieves the timestamp that was stored when a clone was identified.
171
+ *
172
+ * @return int|null
173
+ */
174
+ function get_clone_identification_timestamp() {
175
+ return $this->get_option( 'clone_identification_timestamp', true );
176
+ }
177
+
178
+ /**
179
+ * @author Leo Fajardo (@leorw)
180
+ * @since 2.5.1
181
+ *
182
+ * @param string $sdk_last_version
183
+ */
184
+ function maybe_update_clone_resolution_support_flag( $sdk_last_version ) {
185
+ if ( null !== $this->hide_manual_resolution ) {
186
+ return;
187
+ }
188
+
189
+ $this->hide_manual_resolution = (
190
+ ! empty( $sdk_last_version ) &&
191
+ version_compare( $sdk_last_version, '2.5.0', '<' )
192
+ );
193
+ }
194
+
195
+ /**
196
+ * Stores the time when a clone was identified.
197
+ */
198
+ function store_clone_identification_timestamp() {
199
+ $this->clone_identification_timestamp = time();
200
+ }
201
+
202
+ /**
203
+ * Retrieves the timestamp for the temporary duplicate mode's expiration.
204
+ *
205
+ * @return int
206
+ */
207
+ function get_temporary_duplicate_expiration_timestamp() {
208
+ $temporary_duplicate_mode_start_timestamp = $this->was_temporary_duplicate_mode_selected() ?
209
+ $this->temporary_duplicate_mode_selection_timestamp :
210
+ $this->get_clone_identification_timestamp();
211
+
212
+ return ( $temporary_duplicate_mode_start_timestamp + self::TEMPORARY_DUPLICATE_PERIOD );
213
+ }
214
+
215
+ /**
216
+ * Determines if the SDK should handle clones. The SDK handles clones only up to 3 times with 3 min interval.
217
+ *
218
+ * @return bool
219
+ */
220
+ private function should_handle_clones() {
221
+ if ( ! isset( $this->request_handler_timestamp ) ) {
222
+ return true;
223
+ }
224
+
225
+ if ( $this->request_handler_retries_count >= self::CLONE_RESOLUTION_MAX_RETRIES ) {
226
+ return false;
227
+ }
228
+
229
+ // Give the logic that handles clones enough time to finish (it is given 3 minutes for now).
230
+ return ( time() > ( $this->request_handler_timestamp + self::CLONE_RESOLUTION_MAX_EXECUTION_TIME ) );
231
+ }
232
+
233
+ /**
234
+ * @author Leo Fajardo (@leorw)
235
+ * @since 2.5.1
236
+ *
237
+ * @return bool
238
+ */
239
+ function should_hide_manual_resolution() {
240
+ return ( true === $this->hide_manual_resolution );
241
+ }
242
+
243
+ /**
244
+ * Executes the clones handler logic if it should be executed, i.e., based on the return value of the should_handle_clones() method.
245
+ *
246
+ * @author Leo Fajardo (@leorw)
247
+ * @since 2.5.0
248
+ */
249
+ function maybe_run_clone_resolution() {
250
+ if ( ! $this->should_handle_clones() ) {
251
+ return;
252
+ }
253
+
254
+ $this->request_handler_retries_count = isset( $this->request_handler_retries_count ) ?
255
+ ( $this->request_handler_retries_count + 1 ) :
256
+ 1;
257
+
258
+ $this->request_handler_timestamp = time();
259
+
260
+ $handler_id = ( rand() . microtime() );
261
+ $this->request_handler_id = $handler_id;
262
+
263
+ // Add cookies to trigger request with the same user access permissions.
264
+ $cookies = array();
265
+ foreach ( $_COOKIE as $name => $value ) {
266
+ $cookies[] = new WP_Http_Cookie( array(
267
+ 'name' => $name,
268
+ 'value' => $value,
269
+ ) );
270
+ }
271
+
272
+ wp_remote_post(
273
+ admin_url( 'admin-post.php' ),
274
+ array(
275
+ 'method' => 'POST',
276
+ 'body' => array(
277
+ 'action' => 'fs_clone_resolution',
278
+ 'handler_id' => $handler_id,
279
+ ),
280
+ 'timeout' => 0.01,
281
+ 'blocking' => false,
282
+ 'sslverify' => false,
283
+ 'cookies' => $cookies,
284
+ )
285
+ );
286
+ }
287
+
288
+ /**
289
+ * Executes the clones handler logic.
290
+ *
291
+ * @author Leo Fajardo (@leorw)
292
+ * @since 2.5.0
293
+ */
294
+ function _handle_clone_resolution() {
295
+ $handler_id = fs_request_get( 'handler_id' );
296
+
297
+ if ( empty( $handler_id ) ) {
298
+ return;
299
+ }
300
+
301
+ if (
302
+ ! isset( $this->request_handler_id ) ||
303
+ $this->request_handler_id !== $handler_id
304
+ ) {
305
+ return;
306
+ }
307
+
308
+ if ( ! $this->try_automatic_resolution() ) {
309
+ $this->clear_temporary_duplicate_notice_shown_timestamp();
310
+ }
311
+ }
312
+
313
+ #--------------------------------------------------------------------------------
314
+ #region Automatic Clone Resolution
315
+ #--------------------------------------------------------------------------------
316
+
317
+ /**
318
+ * @var array All installs cache.
319
+ */
320
+ private $all_installs;
321
+
322
+ /**
323
+ * Checks if a given instance's install is a clone of another subsite in the network.
324
+ *
325
+ * @author Vova Feldman (@svovaf)
326
+ *
327
+ * @return FS_Site
328
+ */
329
+ private function find_network_subsite_clone_install( Freemius $instance ) {
330
+ if ( ! is_multisite() ) {
331
+ // Not a multi-site network.
332
+ return null;
333
+ }
334
+
335
+ if ( ! isset( $this->all_installs ) ) {
336
+ $this->all_installs = Freemius::get_all_modules_sites();
337
+ }
338
+
339
+ // Check if there's another blog that has the same site.
340
+ $module_type = $instance->get_module_type();
341
+ $sites_by_module_type = ! empty( $this->all_installs[ $module_type ] ) ?
342
+ $this->all_installs[ $module_type ] :
343
+ array();
344
+
345
+ $slug = $instance->get_slug();
346
+ $sites_by_slug = ! empty( $sites_by_module_type[ $slug ] ) ?
347
+ $sites_by_module_type[ $slug ] :
348
+ array();
349
+
350
+ $current_blog_id = get_current_blog_id();
351
+
352
+ $current_install = $instance->get_site();
353
+
354
+ foreach ( $sites_by_slug as $site ) {
355
+ if (
356
+ $current_install->id == $site->id &&
357
+ $current_blog_id != $site->blog_id
358
+ ) {
359
+ // Clone is identical to an install on another subsite in the network.
360
+ return $site;
361
+ }
362
+ }
363
+
364
+ return null;
365
+ }
366
+
367
+ /**
368
+ * Tries to find a different install of the context product that is associated with the current URL and loads it.
369
+ *
370
+ * @author Leo Fajardo (@leorw)
371
+ * @since 2.5.0
372
+ *
373
+ * @param Freemius $instance
374
+ * @param string $url
375
+ *
376
+ * @return object
377
+ */
378
+ private function find_other_install_by_url( Freemius $instance, $url ) {
379
+ $result = $instance->get_api_user_scope()->get( "/plugins/{$instance->get_id()}/installs.json?url=" . urlencode( $url ) . "&all=true", true );
380
+
381
+ $current_install = $instance->get_site();
382
+
383
+ if ( $instance->is_api_result_object( $result, 'installs' ) ) {
384
+ foreach ( $result->installs as $install ) {
385
+ if ( $install->id == $current_install->id ) {
386
+ continue;
387
+ }
388
+
389
+ if (
390
+ $instance->is_only_premium() &&
391
+ ! FS_Plugin_License::is_valid_id( $install->license_id )
392
+ ) {
393
+ continue;
394
+ }
395
+
396
+ // When searching for installs by a URL, the API will first strip any paths and search for any matching installs by the subdomain. Therefore, we need to test if there's a match between the current URL and the install's URL before continuing.
397
+ if ( $url !== fs_strip_url_protocol( untrailingslashit( $install->url ) ) ) {
398
+ continue;
399
+ }
400
+
401
+ // Found a different install that is associated with the current URL, load it and replace the current install with it if no updated install is found.
402
+ return $install;
403
+ }
404
+ }
405
+
406
+ return null;
407
+ }
408
+
409
+ /**
410
+ * Delete the current install associated with a given instance and opt-in/activate-license to create a fresh install.
411
+ *
412
+ * @author Vova Feldman (@svovaf)
413
+ * @since 2.5.0
414
+ *
415
+ * @param Freemius $instance
416
+ * @param string|false $license_key
417
+ *
418
+ * @return bool TRUE if successfully connected. FALSE if failed and had to restore install from backup.
419
+ */
420
+ private function delete_install_and_connect( Freemius $instance, $license_key = false ) {
421
+ $user = Freemius::_get_user_by_id( $instance->get_site()->user_id );
422
+
423
+ $instance->delete_current_install( true );
424
+
425
+ if ( ! is_object( $user ) ) {
426
+ // Get logged-in WordPress user.
427
+ $current_user = Freemius::_get_current_wp_user();
428
+
429
+ // Find the relevant FS user by email address.
430
+ $user = Freemius::_get_user_by_email( $current_user->user_email );
431
+ }
432
+
433
+ if ( is_object( $user ) ) {
434
+ // When a clone is found, we prefer to use the same user of the original install for the opt-in.
435
+ $instance->install_with_user( $user, $license_key, false, false );
436
+ } else {
437
+ // If no user is found, activate with the license.
438
+ $instance->opt_in(
439
+ false,
440
+ false,
441
+ false,
442
+ $license_key
443
+ );
444
+ }
445
+
446
+ if ( is_object( $instance->get_site() ) ) {
447
+ // Install successfully created.
448
+ return true;
449
+ }
450
+
451
+ // Restore from backup.
452
+ $instance->restore_backup_site();
453
+
454
+ return false;
455
+ }
456
+
457
+ /**
458
+ * Try to resolve the clone situation automatically.
459
+ *
460
+ * @param Freemius $instance
461
+ * @param string $current_url
462
+ * @param bool $is_localhost
463
+ * @param bool|null $is_clone_of_network_subsite
464
+ *
465
+ * @return bool If managed to automatically resolve the clone.
466
+ */
467
+ private function try_resolve_clone_automatically_by_instance(
468
+ Freemius $instance,
469
+ $current_url,
470
+ $is_localhost,
471
+ $is_clone_of_network_subsite = null
472
+ ) {
473
+ // Try to find a different install of the context product that is associated with the current URL.
474
+ $associated_install = $this->find_other_install_by_url( $instance, $current_url );
475
+
476
+ if ( is_object( $associated_install ) ) {
477
+ // Replace the current install with a different install that is associated with the current URL.
478
+ $instance->store_site( new FS_Site( clone $associated_install ) );
479
+ $instance->sync_install( array( 'is_new_site' => true ), true );
480
+
481
+ return true;
482
+ }
483
+
484
+ if ( ! $instance->is_premium() ) {
485
+ // For free products, opt-in with the context user to create new install.
486
+ return $this->delete_install_and_connect( $instance );
487
+ }
488
+
489
+ $license = $instance->_get_license();
490
+ $can_activate_license = ( is_object( $license ) && ! $license->is_utilized( $is_localhost ) );
491
+
492
+ if ( ! $can_activate_license ) {
493
+ // License can't be activated, therefore, can't be automatically resolved.
494
+ return false;
495
+ }
496
+
497
+ if ( ! WP_FS__IS_LOCALHOST_FOR_SERVER && ! $is_localhost ) {
498
+ $is_clone_of_network_subsite = ( ! is_null( $is_clone_of_network_subsite ) ) ?
499
+ $is_clone_of_network_subsite :
500
+ is_object( $this->find_network_subsite_clone_install( $instance ) );
501
+
502
+ if ( ! $is_clone_of_network_subsite ) {
503
+ return false;
504
+ }
505
+ }
506
+
507
+ // If the site is a clone of another subsite in the network, or a localhost one, try to auto activate the license.
508
+ return $this->delete_install_and_connect( $instance, $license->secret_key );
509
+ }
510
+
511
+ /**
512
+ * @author Leo Fajardo (@leorw)
513
+ * @since 2.5.0
514
+ */
515
+ private function try_resolve_clone_automatically() {
516
+ $clone_action = $this->get_clone_resolution_action_from_config();
517
+
518
+ if ( ! empty( $clone_action ) ) {
519
+ $this->try_resolve_clone_automatically_by_config( $clone_action );
520
+ return;
521
+ }
522
+
523
+ $this->try_automatic_resolution();
524
+ }
525
+
526
+ /**
527
+ * Tries to resolve the clone situation automatically based on the config in the wp-config.php file.
528
+ *
529
+ * @author Leo Fajardo (@leorw)
530
+ * @since 2.5.0
531
+ *
532
+ * @param string $clone_action
533
+ */
534
+ private function try_resolve_clone_automatically_by_config( $clone_action ) {
535
+ $fs_instances = array();
536
+
537
+ if ( self::OPTION_LONG_TERM_DUPLICATE === $clone_action ) {
538
+ $instances = Freemius::_get_all_instances();
539
+
540
+ foreach ( $instances as $instance ) {
541
+ if ( ! $instance->is_registered() ) {
542
+ continue;
543
+ }
544
+
545
+ if ( ! $instance->is_clone() ) {
546
+ continue;
547
+ }
548
+
549
+ $license = $instance->has_features_enabled_license() ?
550
+ $instance->_get_license() :
551
+ null;
552
+
553
+ if (
554
+ is_object( $license ) &&
555
+ ! $license->is_utilized(
556
+ ( WP_FS__IS_LOCALHOST_FOR_SERVER || FS_Site::is_localhost_by_address( Freemius::get_unfiltered_site_url() ) )
557
+ )
558
+ ) {
559
+ $fs_instances[] = $instance;
560
+ }
561
+ }
562
+
563
+ if ( empty( $fs_instances ) ) {
564
+ return;
565
+ }
566
+ }
567
+
568
+ $this->resolve_cloned_sites( $clone_action, $fs_instances );
569
+ }
570
+
571
+ /**
572
+ * @author Leo Fajard (@leorw)
573
+ * @since 2.5.0
574
+ *
575
+ * @return string|null
576
+ */
577
+ private function get_clone_resolution_action_from_config() {
578
+ if ( ! defined( 'FS__RESOLVE_CLONE_AS' ) ) {
579
+ return null;
580
+ }
581
+
582
+ if ( ! in_array(
583
+ FS__RESOLVE_CLONE_AS,
584
+ array(
585
+ self::OPTION_NEW_HOME,
586
+ self::OPTION_TEMPORARY_DUPLICATE,
587
+ self::OPTION_LONG_TERM_DUPLICATE,
588
+ )
589
+ ) ) {
590
+ return null;
591
+ }
592
+
593
+ return FS__RESOLVE_CLONE_AS;
594
+ }
595
+
596
+ /**
597
+ * Tries to recover the install of a newly created subsite or resolve it if it's a clone.
598
+ *
599
+ * @author Leo Fajardo (@leorw)
600
+ * @since 2.5.0
601
+ *
602
+ * @param Freemius $instance
603
+ */
604
+ function maybe_resolve_new_subsite_install_automatically( Freemius $instance ) {
605
+ if ( ! $instance->is_user_in_admin() ) {
606
+ // Try to recover an install or resolve a clone only when there's a user in admin to prevent doing it prematurely (e.g., the install can get replaced with clone data again).
607
+ return;
608
+ }
609
+
610
+ if ( ! is_multisite() ) {
611
+ return;
612
+ }
613
+
614
+ $new_blog_install_map = $this->new_blog_install_map;
615
+
616
+ if ( empty( $new_blog_install_map ) || ! is_array( $new_blog_install_map ) ) {
617
+ return;
618
+ }
619
+
620
+ $is_network_admin = fs_is_network_admin();
621
+
622
+ if ( ! $is_network_admin ) {
623
+ // If not in network admin, handle the current site.
624
+ $blog_id = get_current_blog_id();
625
+ } else {
626
+ // If in network admin, handle only the first site.
627
+ $blog_ids = array_keys( $new_blog_install_map );
628
+ $blog_id = $blog_ids[0];
629
+ }
630
+
631
+ if ( ! isset( $new_blog_install_map[ $blog_id ] ) ) {
632
+ // There's no site to handle.
633
+ return;
634
+ }
635
+
636
+ $expected_install_id = $new_blog_install_map[ $blog_id ]['install_id'];
637
+
638
+ $current_install = $instance->get_install_by_blog_id( $blog_id );
639
+ $current_install_id = is_object( $current_install ) ?
640
+ $current_install->id :
641
+ null;
642
+
643
+ if ( $expected_install_id == $current_install_id ) {
644
+ // Remove the current site's information from the map to prevent handling it again.
645
+ $this->remove_new_blog_install_info_from_storage( $blog_id );
646
+
647
+ return;
648
+ }
649
+
650
+ require_once WP_FS__DIR_INCLUDES . '/class-fs-lock.php';
651
+
652
+ $lock = new FS_Lock( self::OPTION_NAME . '_subsite' );
653
+
654
+ if ( ! $lock->try_lock(60) ) {
655
+ return;
656
+ }
657
+
658
+ $instance->switch_to_blog( $blog_id );
659
+
660
+ $current_url = untrailingslashit( Freemius::get_unfiltered_site_url( null, true ) );
661
+ $current_install_url = is_object( $current_install ) ?
662
+ fs_strip_url_protocol( untrailingslashit( $current_install->url ) ) :
663
+ null;
664
+
665
+ // This can be `false` even if the install is a clone as the URL can be updated as part of the cloning process.
666
+ $is_clone = ( ! is_null( $current_install_url ) && $current_url !== $current_install_url );
667
+
668
+ if ( ! FS_Site::is_valid_id( $expected_install_id ) ) {
669
+ $expected_install = null;
670
+ } else {
671
+ $expected_install = $instance->fetch_install_by_id( $expected_install_id );
672
+ }
673
+
674
+ if ( FS_Api::is_api_result_entity( $expected_install ) ) {
675
+ // Replace the current install with the expected install.
676
+ $instance->store_site( new FS_Site( clone $expected_install ) );
677
+ $instance->sync_install( array( 'is_new_site' => true ), true );
678
+ } else {
679
+ $network_subsite_clone_install = null;
680
+
681
+ if ( ! $is_clone ) {
682
+ // It is possible that `$is_clone` is `false` but the install is actually a clone as the following call checks the install ID and not the URL.
683
+ $network_subsite_clone_install = $this->find_network_subsite_clone_install( $instance );
684
+ }
685
+
686
+ if ( $is_clone || is_object( $network_subsite_clone_install ) ) {
687
+ // If there's no expected install (or it couldn't be fetched) and the current install is a clone, try to resolve the clone automatically.
688
+ $is_localhost = FS_Site::is_localhost_by_address( $current_url );
689
+
690
+ $resolved = $this->try_resolve_clone_automatically_by_instance( $instance, $current_url, $is_localhost, is_object( $network_subsite_clone_install ) );
691
+
692
+ if ( ! $resolved && is_object( $network_subsite_clone_install ) ) {
693
+ if ( empty( $this->get_clone_identification_timestamp() ) ) {
694
+ $this->store_clone_identification_timestamp();
695
+ }
696
+
697
+ // Since the clone couldn't be identified based on the URL, replace the stored install with the cloned install so that the manual clone resolution notice will appear.
698
+ $instance->store_site( clone $network_subsite_clone_install );
699
+ }
700
+ }
701
+ }
702
+
703
+ $instance->restore_current_blog();
704
+
705
+ // Remove the current site's information from the map to prevent handling it again.
706
+ $this->remove_new_blog_install_info_from_storage( $blog_id );
707
+
708
+ $lock->unlock();
709
+ }
710
+
711
+ /**
712
+ * If a new install was created after creating a new subsite, its ID is stored in the blog-install map so that it can be recovered in case it's replaced with a clone install (e.g., when the newly created subsite is a clone). The IDs of the clone subsites that were created while not running this version of the SDK or a higher version will also be stored in the said map so that the clone manager can also try to resolve them later on.
713
+ *
714
+ * @author Leo Fajardo (@leorw)
715
+ * @since 2.5.0
716
+ *
717
+ * @param int $blog_id
718
+ * @param FS_Site $site
719
+ */
720
+ function store_blog_install_info( $blog_id, $site = null ) {
721
+ $new_blog_install_map = $this->new_blog_install_map;
722
+
723
+ if (
724
+ empty( $new_blog_install_map ) ||
725
+ ! is_array( $new_blog_install_map )
726
+ ) {
727
+ $new_blog_install_map = array();
728
+ }
729
+
730
+ $install_id = null;
731
+
732
+ if ( is_object( $site ) ) {
733
+ $install_id = $site->id;
734
+ }
735
+
736
+ $new_blog_install_map[ $blog_id ] = array( 'install_id' => $install_id );
737
+
738
+ $this->new_blog_install_map = $new_blog_install_map;
739
+ }
740
+
741
+ /**
742
+ * @author Leo Fajardo (@leorw)
743
+ * @since 2.5.0
744
+ *
745
+ * @param int $blog_id
746
+ */
747
+ private function remove_new_blog_install_info_from_storage( $blog_id ) {
748
+ $new_blog_install_map = $this->new_blog_install_map;
749
+
750
+ unset( $new_blog_install_map[ $blog_id ] );
751
+ $this->new_blog_install_map = $new_blog_install_map;
752
+ }
753
+
754
+ /**
755
+ * Tries to resolve all clones automatically.
756
+ *
757
+ * @author Leo Fajardo (@leorw)
758
+ * @since 2.5.0
759
+ *
760
+ * @return bool If managed to automatically resolve all clones.
761
+ */
762
+ private function try_automatic_resolution() {
763
+ $this->_logger->entrance();
764
+
765
+ require_once WP_FS__DIR_INCLUDES . '/class-fs-lock.php';
766
+
767
+ $lock = new FS_Lock( self::OPTION_NAME );
768
+
769
+ /**
770
+ * Try to acquire lock for the next 60 sec based on the thread ID.
771
+ */
772
+ if ( ! $lock->try_lock( 60 ) ) {
773
+ return false;
774
+ }
775
+
776
+ $current_url = untrailingslashit( Freemius::get_unfiltered_site_url( null, true ) );
777
+ $is_localhost = FS_Site::is_localhost_by_address( $current_url );
778
+
779
+ $require_manual_resolution = false;
780
+
781
+ $instances = Freemius::_get_all_instances();
782
+
783
+ foreach ( $instances as $instance ) {
784
+ if ( ! $instance->is_registered() ) {
785
+ continue;
786
+ }
787
+
788
+ if ( ! $instance->is_clone() ) {
789
+ continue;
790
+ }
791
+
792
+ if ( ! $this->try_resolve_clone_automatically_by_instance( $instance, $current_url, $is_localhost ) ) {
793
+ $require_manual_resolution = true;
794
+ }
795
+ }
796
+
797
+ // Create a 1-day lock.
798
+ $lock->lock( WP_FS__TIME_24_HOURS_IN_SEC );
799
+
800
+ return ( ! $require_manual_resolution );
801
+ }
802
+
803
+ #endregion
804
+
805
+ #--------------------------------------------------------------------------------
806
+ #region Manual Clone Resolution
807
+ #--------------------------------------------------------------------------------
808
+
809
+ /**
810
+ * @author Leo Fajardo (@leorw)
811
+ * @since 2.5.0
812
+ */
813
+ function _add_clone_resolution_javascript() {
814
+ $vars = array( 'ajax_action' => Freemius::get_ajax_action_static( 'handle_clone_resolution' ) );
815
+
816
+ fs_require_once_template( 'clone-resolution-js.php', $vars );
817
+ }
818
+
819
+ /**
820
+ * @author Leo Fajardo (@leorw)
821
+ * @since 2.5.0
822
+ */
823
+ function _clone_resolution_action_ajax_handler() {
824
+ $this->_logger->entrance();
825
+
826
+ check_ajax_referer( Freemius::get_ajax_action_static( 'handle_clone_resolution' ), 'security' );
827
+
828
+ $clone_action = fs_request_get( 'clone_action' );
829
+ $blog_id = is_multisite() ?
830
+ fs_request_get( 'blog_id' ) :
831
+ 0;
832
+
833
+ if ( is_multisite() && $blog_id == get_current_blog_id() ) {
834
+ $blog_id = 0;
835
+ }
836
+
837
+ if ( empty( $clone_action ) ) {
838
+ Freemius::shoot_ajax_failure( array(
839
+ 'message' => fs_text_inline( 'Invalid clone resolution action.', 'invalid-clone-resolution-action-error' ),
840
+ 'redirect_url' => '',
841
+ ) );
842
+ }
843
+
844
+ $result = $this->resolve_cloned_sites( $clone_action, array(), $blog_id );
845
+
846
+ Freemius::shoot_ajax_success( $result );
847
+ }
848
+
849
+ /**
850
+ * @author Leo Fajardo (@leorw)
851
+ * @since 2.5.0
852
+ *
853
+ * @param string $clone_action
854
+ * @param Freemius[] $fs_instances
855
+ * @param int $blog_id
856
+ *
857
+ * @return array
858
+ */
859
+ private function resolve_cloned_sites( $clone_action, $fs_instances = array(), $blog_id = 0 ) {
860
+ $this->_logger->entrance();
861
+
862
+ $result = array();
863
+
864
+ $instances_with_clone = array();
865
+ $instances_with_clone_count = 0;
866
+ $install_by_instance_id = array();
867
+
868
+ $instances = ( ! empty( $fs_instances ) ) ?
869
+ $fs_instances :
870
+ Freemius::_get_all_instances();
871
+
872
+ $should_switch_to_blog = ( $blog_id > 0 );
873
+
874
+ foreach ( $instances as $instance ) {
875
+ if ( $should_switch_to_blog ) {
876
+ $instance->switch_to_blog( $blog_id );
877
+ }
878
+
879
+ if ( $instance->is_registered() && $instance->is_clone() ) {
880
+ $instances_with_clone[] = $instance;
881
+
882
+ $instances_with_clone_count ++;
883
+
884
+ $install_by_instance_id[ $instance->get_id() ] = $instance->get_site();
885
+ }
886
+ }
887
+
888
+ if ( self::OPTION_TEMPORARY_DUPLICATE === $clone_action ) {
889
+ $this->store_temporary_duplicate_timestamp();
890
+ } else {
891
+ $redirect_url = '';
892
+
893
+ foreach ( $instances_with_clone as $instance ) {
894
+ if ( $should_switch_to_blog ) {
895
+ $instance->switch_to_blog( $blog_id );
896
+ }
897
+
898
+ $has_error = false;
899
+
900
+ if ( self::OPTION_NEW_HOME === $clone_action ) {
901
+ $instance->sync_install( array( 'is_new_site' => true ), true );
902
+
903
+ if ( $instance->is_clone() ) {
904
+ $has_error = true;
905
+ }
906
+ } else {
907
+ $instance->_handle_long_term_duplicate();
908
+
909
+ if ( ! is_object( $instance->get_site() ) ) {
910
+ $has_error = true;
911
+ }
912
+ }
913
+
914
+ if ( $has_error && 1 === $instances_with_clone_count ) {
915
+ $redirect_url = $instance->get_activation_url();
916
+ }
917
+ }
918
+
919
+ $result = ( array( 'redirect_url' => $redirect_url ) );
920
+ }
921
+
922
+ foreach ( $instances_with_clone as $instance ) {
923
+ if ( $should_switch_to_blog ) {
924
+ $instance->switch_to_blog( $blog_id );
925
+ }
926
+
927
+ // No longer a clone, send an update.
928
+ if ( ! $instance->is_clone() ) {
929
+ $instance->send_clone_resolution_update(
930
+ $clone_action,
931
+ $install_by_instance_id[ $instance->get_id() ]
932
+ );
933
+ }
934
+ }
935
+
936
+ if ( 'temporary_duplicate_license_activation' !== $clone_action ) {
937
+ $this->remove_clone_resolution_options_notice();
938
+ } else {
939
+ $this->remove_temporary_duplicate_notice();
940
+ }
941
+
942
+ if ( $should_switch_to_blog ) {
943
+ foreach ( $instances as $instance ) {
944
+ $instance->restore_current_blog();
945
+ }
946
+ }
947
+
948
+ return $result;
949
+ }
950
+
951
+ /**
952
+ * @author Leo Fajardo (@leorw)
953
+ * @since 2.5.0
954
+ */
955
+ private function hide_clone_admin_notices() {
956
+ $this->remove_clone_resolution_options_notice( false );
957
+ $this->remove_temporary_duplicate_notice( false );
958
+ }
959
+
960
+ /**
961
+ * @author Leo Fajardo (@leorw)
962
+ * @since 2.5.0
963
+ */
964
+ function maybe_show_clone_admin_notice() {
965
+ $this->_logger->entrance();
966
+
967
+ if ( fs_is_network_admin() ) {
968
+ $existing_notice_ids = $this->maybe_remove_notices();
969
+
970
+ if ( ! empty( $existing_notice_ids ) ) {
971
+ fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' );
972
+ }
973
+
974
+ return;
975
+ }
976
+
977
+ $first_instance_with_clone = null;
978
+
979
+ $site_urls = array();
980
+ $sites_with_license_urls = array();
981
+ $sites_with_premium_version_count = 0;
982
+ $product_ids = array();
983
+ $product_titles = array();
984
+
985
+ $instances = Freemius::_get_all_instances();
986
+
987
+ foreach ( $instances as $instance ) {
988
+ if ( ! $instance->is_registered() ) {
989
+ continue;
990
+ }
991
+
992
+ if ( ! $instance->is_clone( true ) ) {
993
+ continue;
994
+ }
995
+
996
+ $install = $instance->get_site();
997
+
998
+ $site_urls[] = $install->url;
999
+ $product_ids[] = $instance->get_id();
1000
+ $product_titles[] = $instance->get_plugin_title();
1001
+
1002
+ if ( is_null( $first_instance_with_clone ) ) {
1003
+ $first_instance_with_clone = $instance;
1004
+ }
1005
+
1006
+ if ( is_object( $instance->_get_license() ) ) {
1007
+ $sites_with_license_urls[] = $install->url;
1008
+ }
1009
+
1010
+ if ( $instance->is_premium() ) {
1011
+ $sites_with_premium_version_count ++;
1012
+ }
1013
+ }
1014
+
1015
+ if ( empty( $site_urls ) && empty( $sites_with_license_urls ) ) {
1016
+ $this->hide_clone_admin_notices();
1017
+
1018
+ return;
1019
+ }
1020
+
1021
+ $site_urls = array_unique( $site_urls );
1022
+ $sites_with_license_urls = array_unique( $sites_with_license_urls );
1023
+
1024
+ $module_label = fs_text_inline( 'products', 'products' );
1025
+ $admin_notice_module_title = null;
1026
+
1027
+ $has_temporary_duplicate_mode_expired = $this->has_temporary_duplicate_mode_expired();
1028
+
1029
+ if (
1030
+ ! $this->was_temporary_duplicate_mode_selected() ||
1031
+ $has_temporary_duplicate_mode_expired
1032
+ ) {
1033
+ if ( ! empty( $site_urls ) ) {
1034
+ fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' );
1035
+
1036
+ $doc_url = 'https://freemius.com/help/documentation/wordpress-sdk/safe-mode-clone-resolution-duplicate-website/';
1037
+
1038
+ if ( 1 === count( $instances ) ) {
1039
+ $doc_url = fs_apply_filter(
1040
+ $first_instance_with_clone->get_unique_affix(),
1041
+ 'clone_resolution_documentation_url',
1042
+ $doc_url
1043
+ );
1044
+ }
1045
+
1046
+ $this->add_manual_clone_resolution_admin_notice(
1047
+ $product_ids,
1048
+ $product_titles,
1049
+ $site_urls,
1050
+ Freemius::get_unfiltered_site_url(),
1051
+ ( count( $site_urls ) === count( $sites_with_license_urls ) ),
1052
+ ( count( $site_urls ) === $sites_with_premium_version_count ),
1053
+ $doc_url
1054
+ );
1055
+ }
1056
+
1057
+ return;
1058
+ }
1059
+
1060
+ if ( empty( $sites_with_license_urls ) ) {
1061
+ return;
1062
+ }
1063
+
1064
+ if ( ! $this->is_temporary_duplicate_notice_shown() ) {
1065
+ $last_time_temporary_duplicate_notice_shown = $this->temporary_duplicate_notice_shown_timestamp;
1066
+ $was_temporary_duplicate_notice_shown_before = is_numeric( $last_time_temporary_duplicate_notice_shown );
1067
+
1068
+ if ( $was_temporary_duplicate_notice_shown_before ) {
1069
+ $temporary_duplicate_mode_expiration_timestamp = $this->get_temporary_duplicate_expiration_timestamp();
1070
+ $current_time = time();
1071
+
1072
+ if (
1073
+ $current_time > $temporary_duplicate_mode_expiration_timestamp ||
1074
+ $current_time < ( $temporary_duplicate_mode_expiration_timestamp - ( 2 * WP_FS__TIME_24_HOURS_IN_SEC ) )
1075
+ ) {
1076
+ // Do not show the notice if the temporary duplicate mode has already expired or it will expire more than 2 days from now.
1077
+ return;
1078
+ }
1079
+ }
1080
+ }
1081
+
1082
+ if ( 1 === count( $sites_with_license_urls ) ) {
1083
+ $module_label = $first_instance_with_clone->get_module_label( true );
1084
+ $admin_notice_module_title = $first_instance_with_clone->get_plugin_title();
1085
+ }
1086
+
1087
+ fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' );
1088
+
1089
+ $this->add_temporary_duplicate_sticky_notice(
1090
+ $product_ids,
1091
+ $this->get_temporary_duplicate_admin_notice_string( $sites_with_license_urls, $product_titles, $module_label ),
1092
+ $admin_notice_module_title
1093
+ );
1094
+ }
1095
+
1096
+ /**
1097
+ * Removes the notices from the storage if the context product is either no longer active on the context subsite or it's active but there's no longer any clone. This prevents the notices from being shown on the network-level admin page when they are no longer relevant.
1098
+ *
1099
+ * @author Leo Fajardo (@leorw)
1100
+ * @since 2.5.1
1101
+ *
1102
+ * @return string[]
1103
+ */
1104
+ private function maybe_remove_notices() {
1105
+ $notices = array(
1106
+ 'clone_resolution_options_notice' => $this->_notices->get_sticky( 'clone_resolution_options_notice', true ),
1107
+ 'temporary_duplicate_notice' => $this->_notices->get_sticky( 'temporary_duplicate_notice', true ),
1108
+ );
1109
+
1110
+ $instances = Freemius::_get_all_instances();
1111
+
1112
+ foreach ( $notices as $id => $notice ) {
1113
+ if ( ! is_array( $notice ) ) {
1114
+ unset( $notices[ $id ] );
1115
+ continue;
1116
+ }
1117
+
1118
+ if ( empty( $notice['data'] ) || ! is_array( $notice['data'] ) ) {
1119
+ continue;
1120
+ }
1121
+
1122
+ if ( empty( $notice['data']['product_ids'] ) || empty( $notice['data']['blog_id'] ) ) {
1123
+ continue;
1124
+ }
1125
+
1126
+ $product_ids = $notice['data']['product_ids'];
1127
+ $blog_id = $notice['data']['blog_id'];
1128
+ $has_clone = false;
1129
+
1130
+ if ( ! is_null( get_site( $blog_id ) ) ) {
1131
+ foreach ( $product_ids as $product_id ) {
1132
+ if ( ! isset( $instances[ 'm_' . $product_id ] ) ) {
1133
+ continue;
1134
+ }
1135
+
1136
+ $instance = $instances[ 'm_' . $product_id ];
1137
+
1138
+ $plugin_basename = $instance->get_plugin_basename();
1139
+
1140
+ $is_plugin_active = is_plugin_active_for_network( $plugin_basename );
1141
+
1142
+ if ( ! $is_plugin_active ) {
1143
+ switch_to_blog( $blog_id );
1144
+
1145
+ $is_plugin_active = is_plugin_active( $plugin_basename );
1146
+
1147
+ restore_current_blog();
1148
+ }
1149
+
1150
+ if ( ! $is_plugin_active ) {
1151
+ continue;
1152
+ }
1153
+
1154
+ $install = $instance->get_install_by_blog_id( $blog_id );
1155
+
1156
+ if ( ! is_object( $install ) ) {
1157
+ continue;
1158
+ }
1159
+
1160
+ $subsite_url = Freemius::get_unfiltered_site_url( $blog_id, true, true );
1161
+
1162
+ $has_clone = ( fs_strip_url_protocol( trailingslashit( $install->url ) ) !== $subsite_url );
1163
+ }
1164
+ }
1165
+
1166
+ if ( ! $has_clone ) {
1167
+ $this->_notices->remove_sticky( $id, true, false );
1168
+ unset( $notices[ $id ] );
1169
+ }
1170
+ }
1171
+
1172
+ return array_keys( $notices );
1173
+ }
1174
+
1175
+ /**
1176
+ * Adds a notice that provides the logged-in WordPress user with manual clone resolution options.
1177
+ *
1178
+ * @param number[] $product_ids
1179
+ * @param string[] $site_urls
1180
+ * @param string $current_url
1181
+ * @param bool $has_license
1182
+ * @param bool $is_premium
1183
+ * @param string $doc_url
1184
+ */
1185
+ private function add_manual_clone_resolution_admin_notice(
1186
+ $product_ids,
1187
+ $product_titles,
1188
+ $site_urls,
1189
+ $current_url,
1190
+ $has_license,
1191
+ $is_premium,
1192
+ $doc_url
1193
+ ) {
1194
+ $this->_logger->entrance();
1195
+
1196
+ $total_sites = count( $site_urls );
1197
+ $sites_list = '';
1198
+
1199
+ $total_products = count( $product_titles );
1200
+ $products_list = '';
1201
+
1202
+ if ( 1 === $total_products ) {
1203
+ $notice_header = sprintf(
1204
+ '<div class="fs-notice-header"><p>%s</p></div>',
1205
+ fs_esc_html_inline( '%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s.', 'single-cloned-site-safe-mode-message' )
1206
+ );
1207
+ } else {
1208
+ $notice_header = sprintf(
1209
+ '<div class="fs-notice-header"><p>%s</p></div>',
1210
+ ( 1 === $total_sites ) ?
1211
+ fs_esc_html_inline( 'The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$s', 'multiple-products-cloned-site-safe-mode-message' ) :
1212
+ fs_esc_html_inline( 'The products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$s', 'multiple-products-multiple-cloned-sites-safe-mode-message' )
1213
+ );
1214
+
1215
+ foreach ( $product_titles as $product_title ) {
1216
+ $products_list .= sprintf( '<li>%s</li>', $product_title );
1217
+ }
1218
+
1219
+ $products_list = '<ol>' . $products_list . '</ol>';
1220
+
1221
+ foreach ( $site_urls as $site_url ) {
1222
+ $sites_list .= sprintf(
1223
+ '<li><a href="%s" target="_blank">%s</a></li>',
1224
+ $site_url,
1225
+ fs_strip_url_protocol( $site_url )
1226
+ );
1227
+ }
1228
+
1229
+ $sites_list = '<ol>' . $sites_list . '</ol>';
1230
+ }
1231
+
1232
+ $remote_site_link = '<b>' . (1 === $total_sites ?
1233
+ sprintf(
1234
+ '<a href="%s" target="_blank">%s</a>',
1235
+ $site_urls[0],
1236
+ fs_strip_url_protocol( $site_urls[0] )
1237
+ ) :
1238
+ fs_text_inline( 'the above-mentioned sites', 'above-mentioned-sites' )) . '</b>';
1239
+
1240
+ $current_site_link = sprintf(
1241
+ '<b><a href="%s" target="_blank">%s</a></b>',
1242
+ $current_url,
1243
+ fs_strip_url_protocol( $current_url )
1244
+ );
1245
+
1246
+ $button_template = '<button class="button" data-clone-action="%s">%s</button>';
1247
+ $option_template = '<div class="fs-clone-resolution-option"><strong>%s</strong><p>%s</p><div>%s</div></div>';
1248
+
1249
+ $duplicate_option = sprintf(
1250
+ $option_template,
1251
+ fs_esc_html_inline( 'Is %2$s a duplicate of %4$s?', 'duplicate-site-confirmation-message' ),
1252
+ fs_esc_html_inline( 'Yes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development.', 'duplicate-site-message' ),
1253
+ ( $this->has_temporary_duplicate_mode_expired() ?
1254
+ sprintf(
1255
+ $button_template,
1256
+ 'long_term_duplicate',
1257
+ fs_text_inline( 'Long-Term Duplicate', 'long-term-duplicate' )
1258
+ ) :
1259
+ sprintf(
1260
+ $button_template,
1261
+ 'temporary_duplicate',
1262
+ fs_text_inline( 'Duplicate Website', 'duplicate-site' )
1263
+ ) )
1264
+ );
1265
+
1266
+ $migration_option = sprintf(
1267
+ $option_template,
1268
+ fs_esc_html_inline( 'Is %2$s the new home of %4$s?', 'migrate-site-confirmation-message' ),
1269
+ sprintf(
1270
+ fs_esc_html_inline( 'Yes, %%2$s is replacing %%4$s. I would like to migrate my %s from %%4$s to %%2$s.', 'migrate-site-message' ),
1271
+ ( $has_license ? fs_text_inline( 'license', 'license' ) : fs_text_inline( 'data', 'data' ) )
1272
+ ),
1273
+ sprintf(
1274
+ $button_template,
1275
+ 'new_home',
1276
+ $has_license ?
1277
+ fs_text_inline( 'Migrate License', 'migrate-product-license' ) :
1278
+ fs_text_inline( 'Migrate', 'migrate-product-data' )
1279
+ )
1280
+ );
1281
+
1282
+ $new_website = sprintf(
1283
+ $option_template,
1284
+ fs_esc_html_inline( 'Is %2$s a new website?', 'new-site-confirmation-message' ),
1285
+ fs_esc_html_inline( 'Yes, %2$s is a new and different website that is separate from %4$s.', 'new-site-message' ) .
1286
+ ($is_premium ?
1287
+ ' ' . fs_text_inline( 'It requires license activation.', 'new-site-requires-license-activation-message' ) :
1288
+ ''
1289
+ ),
1290
+ sprintf(
1291
+ $button_template,
1292
+ 'new_website',
1293
+ ( ! $is_premium || ! $has_license ) ?
1294
+ fs_text_inline( 'New Website', 'new-website' ) :
1295
+ fs_text_inline( 'Activate License', 'activate-license' )
1296
+ )
1297
+ );
1298
+
1299
+ $blog_id = get_current_blog_id();
1300
+
1301
+ /**
1302
+ * %1$s - single product's title or product titles list.
1303
+ * %2$s - site's URL.
1304
+ * %3$s - single install's URL or install URLs list.
1305
+ * %4$s - Clone site's link or "the above-mentioned sites" if there are multiple clone sites.
1306
+ */
1307
+ $message = sprintf(
1308
+ $notice_header .
1309
+ '<div class="fs-clone-resolution-options-container" data-ajax-url="' . esc_attr( admin_url( 'admin-ajax.php?_fs_network_admin=false', 'relative' ) ) . '" data-blog-id="' . $blog_id . '">' .
1310
+ $duplicate_option .
1311
+ $migration_option .
1312
+ $new_website . '</div>' .
1313
+ sprintf( '<div class="fs-clone-documentation-container">Unsure what to do? <a href="%s" target="_blank">Read more here</a>.</div>', $doc_url ),
1314
+ // %1$s
1315
+ ( 1 === $total_products ?
1316
+ sprintf( '<b>%s</b>', $product_titles[0] ) :
1317
+ ( 1 === $total_sites ?
1318
+ sprintf( '<div>%s</div>', $products_list ) :
1319
+ sprintf( '<div><p><strong>%s</strong>:</p>%s</div>', fs_esc_html_x_inline( 'Products', 'Clone resolution admin notice products list label', 'products' ), $products_list ) )
1320
+ ),
1321
+ // %2$s
1322
+ $current_site_link,
1323
+ // %3$s
1324
+ ( 1 === $total_sites ?
1325
+ $remote_site_link :
1326
+ $sites_list ),
1327
+ // %4$s
1328
+ $remote_site_link
1329
+ );
1330
+
1331
+ $this->_notices->add_sticky(
1332
+ $message,
1333
+ 'clone_resolution_options_notice',
1334
+ '',
1335
+ 'warn',
1336
+ true,
1337
+ null,
1338
+ null,
1339
+ true,
1340
+ // Intentionally not dismissible.
1341
+ false,
1342
+ array(
1343
+ 'product_ids' => $product_ids,
1344
+ 'blog_id' => $blog_id
1345
+ )
1346
+ );
1347
+ }
1348
+
1349
+ #endregion
1350
+
1351
+ #--------------------------------------------------------------------------------
1352
+ #region Temporary Duplicate (Short Term)
1353
+ #--------------------------------------------------------------------------------
1354
+
1355
+ /**
1356
+ * @author Leo Fajardo (@leorw)
1357
+ * @since 2.5.0
1358
+ *
1359
+ * @return string
1360
+ */
1361
+ private function get_temporary_duplicate_admin_notice_string(
1362
+ $site_urls,
1363
+ $product_titles,
1364
+ $module_label
1365
+ ) {
1366
+ $this->_logger->entrance();
1367
+
1368
+ $temporary_duplicate_end_date = $this->get_temporary_duplicate_expiration_timestamp();
1369
+ $temporary_duplicate_end_date = date( 'M j, Y', $temporary_duplicate_end_date );
1370
+
1371
+ $current_url = Freemius::get_unfiltered_site_url();
1372
+ $current_site_link = sprintf(
1373
+ '<b><a href="%s" target="_blank">%s</a></b>',
1374
+ $current_url,
1375
+ fs_strip_url_protocol( $current_url )
1376
+ );
1377
+
1378
+ $total_sites = count( $site_urls );
1379
+ $sites_list = '';
1380
+
1381
+ $total_products = count( $product_titles );
1382
+ $products_list = '';
1383
+
1384
+ if ( $total_sites > 1 ) {
1385
+ foreach ( $site_urls as $site_url ) {
1386
+ $sites_list .= sprintf(
1387
+ '<li><a href="%s" target="_blank">%s</a></li>',
1388
+ $site_url,
1389
+ fs_strip_url_protocol( $site_url )
1390
+ );
1391
+ }
1392
+
1393
+ $sites_list = '<ol class="fs-sites-list">' . $sites_list . '</ol>';
1394
+ }
1395
+
1396
+ if ( $total_products > 1 ) {
1397
+ foreach ( $product_titles as $product_title ) {
1398
+ $products_list .= sprintf( '<li>%s</li>', $product_title );
1399
+ }
1400
+
1401
+ $products_list = '<ol>' . $products_list . '</ol>';
1402
+ }
1403
+
1404
+ return sprintf(
1405
+ sprintf(
1406
+ '<div>%s</div>',
1407
+ ( 1 === $total_sites ?
1408
+ sprintf( '<p>%s</p>', fs_esc_html_inline( 'You marked this website, %s, as a temporary duplicate of %s.', 'temporary-duplicate-message' ) ) :
1409
+ sprintf( '<p>%s:</p>', fs_esc_html_inline( 'You marked this website, %s, as a temporary duplicate of these sites', 'temporary-duplicate-of-sites-message' ) ) . '%s' )
1410
+ ) . '%s',
1411
+ $current_site_link,
1412
+ ( 1 === $total_sites ?
1413
+ sprintf(
1414
+ '<b><a href="%s" target="_blank">%s</a></b>',
1415
+ $site_urls[0],
1416
+ fs_strip_url_protocol( $site_urls[0] )
1417
+ ) :
1418
+ $sites_list ),
1419
+ sprintf(
1420
+ '<div class="fs-clone-resolution-options-container fs-duplicate-site-options" data-ajax-url="%s" data-blog-id="' . get_current_blog_id() . '"><p>%s</p>%s<p>%s</p></div>',
1421
+ esc_attr( admin_url( 'admin-ajax.php?_fs_network_admin=false', 'relative' ) ),
1422
+ sprintf(
1423
+ fs_esc_html_inline( "%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first).", 'duplicate-site-confirmation-message' ),
1424
+ ( 1 === $total_products ?
1425
+ sprintf(
1426
+ fs_esc_html_x_inline( "The %s's", '"The <product_label>", e.g.: "The plugin"', 'the-product-x'),
1427
+ "<strong>{$module_label}</strong>"
1428
+ ) :
1429
+ fs_esc_html_inline( "The following products'", 'the-following-products' ) ),
1430
+ sprintf( '<strong>%s</strong>', $temporary_duplicate_end_date )
1431
+ ),
1432
+ ( 1 === $total_products ?
1433
+ '' :
1434
+ sprintf( '<div>%s</div>', $products_list )
1435
+ ),
1436
+ sprintf(
1437
+ fs_esc_html_inline( 'If this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s.', 'duplicate-site-message' ),
1438
+ sprintf( '<strong>%s</strong>', $temporary_duplicate_end_date),
1439
+ sprintf( '<a href="#" id="fs_temporary_duplicate_license_activation_link" data-clone-action="temporary_duplicate_license_activation">%s</a>', fs_esc_html_inline( 'activate a license here', 'activate-license-here' ) )
1440
+ )
1441
+ )
1442
+ );
1443
+ }
1444
+
1445
+ /**
1446
+ * Determines if the temporary duplicate mode has already expired.
1447
+ *
1448
+ * @return bool
1449
+ */
1450
+ function has_temporary_duplicate_mode_expired() {
1451
+ $temporary_duplicate_mode_start_timestamp = $this->was_temporary_duplicate_mode_selected() ?
1452
+ $this->get_option( 'temporary_duplicate_mode_selection_timestamp', true ) :
1453
+ $this->get_clone_identification_timestamp();
1454
+
1455
+ if ( ! is_numeric( $temporary_duplicate_mode_start_timestamp ) ) {
1456
+ return false;
1457
+ }
1458
+
1459
+ return ( time() > ( $temporary_duplicate_mode_start_timestamp + self::TEMPORARY_DUPLICATE_PERIOD ) );
1460
+ }
1461
+
1462
+ /**
1463
+ * Determines if the logged-in WordPress user manually selected the temporary duplicate mode for the site.
1464
+ *
1465
+ * @return bool
1466
+ */
1467
+ function was_temporary_duplicate_mode_selected() {
1468
+ return is_numeric( $this->temporary_duplicate_mode_selection_timestamp );
1469
+ }
1470
+
1471
+ /**
1472
+ * Stores the time when the logged-in WordPress user selected the temporary duplicate mode for the site.
1473
+ */
1474
+ private function store_temporary_duplicate_timestamp() {
1475
+ $this->temporary_duplicate_mode_selection_timestamp = time();
1476
+ }
1477
+
1478
+ /**
1479
+ * Removes the notice that is shown when the logged-in WordPress user has selected the temporary duplicate mode for the site.
1480
+ *
1481
+ * @param bool $store
1482
+ */
1483
+ function remove_clone_resolution_options_notice( $store = true ) {
1484
+ $this->_notices->remove_sticky( 'clone_resolution_options_notice', true, $store );
1485
+ }
1486
+
1487
+ /**
1488
+ * Removes the notice that is shown when the logged-in WordPress user has selected the temporary duplicate mode for the site.
1489
+ *
1490
+ * @param bool $store
1491
+ */
1492
+ function remove_temporary_duplicate_notice( $store = true ) {
1493
+ $this->_notices->remove_sticky( 'temporary_duplicate_notice', true, $store );
1494
+ }
1495
+
1496
+ /**
1497
+ * Determines if the manual clone resolution options notice is currently being shown.
1498
+ *
1499
+ * @return bool
1500
+ */
1501
+ function is_clone_resolution_options_notice_shown() {
1502
+ return $this->_notices->has_sticky( 'clone_resolution_options_notice', true );
1503
+ }
1504
+
1505
+ /**
1506
+ * Determines if the temporary duplicate notice is currently being shown.
1507
+ *
1508
+ * @return bool
1509
+ */
1510
+ function is_temporary_duplicate_notice_shown() {
1511
+ return $this->_notices->has_sticky( 'temporary_duplicate_notice', true );
1512
+ }
1513
+
1514
+ /**
1515
+ * Determines if a site was marked as a temporary duplicate and if it's still a temporary duplicate.
1516
+ *
1517
+ * @return bool
1518
+ */
1519
+ function is_temporary_duplicate_by_blog_id( $blog_id ) {
1520
+ $timestamp = $this->get_option( 'temporary_duplicate_mode_selection_timestamp', false, $blog_id );
1521
+
1522
+ return (
1523
+ is_numeric( $timestamp ) &&
1524
+ time() < ( $timestamp + self::TEMPORARY_DUPLICATE_PERIOD )
1525
+ );
1526
+ }
1527
+
1528
+ /**
1529
+ * Determines the last time the temporary duplicate notice was shown.
1530
+ *
1531
+ * @return int|null
1532
+ */
1533
+ function last_time_temporary_duplicate_notice_was_shown() {
1534
+ return $this->temporary_duplicate_notice_shown_timestamp;
1535
+ }
1536
+
1537
+ /**
1538
+ * Clears the time that has been stored when the temporary duplicate notice was shown.
1539
+ */
1540
+ function clear_temporary_duplicate_notice_shown_timestamp() {
1541
+ unset( $this->temporary_duplicate_notice_shown_timestamp );
1542
+ }
1543
+
1544
+ /**
1545
+ * Adds a temporary duplicate notice that provides the logged-in WordPress user with an option to activate a license for the site.
1546
+ *
1547
+ * @param number[] $product_ids
1548
+ * @param string $message
1549
+ * @param string|null $plugin_title
1550
+ */
1551
+ function add_temporary_duplicate_sticky_notice(
1552
+ $product_ids,
1553
+ $message,
1554
+ $plugin_title = null
1555
+ ) {
1556
+ $this->_logger->entrance();
1557
+
1558
+ $this->_notices->add_sticky(
1559
+ $message,
1560
+ 'temporary_duplicate_notice',
1561
+ '',
1562
+ 'promotion',
1563
+ true,
1564
+ null,
1565
+ $plugin_title,
1566
+ true,
1567
+ true,
1568
+ array(
1569
+ 'product_ids' => $product_ids,
1570
+ 'blog_id' => get_current_blog_id()
1571
+ )
1572
+ );
1573
+
1574
+ $this->temporary_duplicate_notice_shown_timestamp = time();
1575
+ }
1576
+
1577
+ #endregion
1578
+
1579
+ /**
1580
+ * @author Leo Fajardo
1581
+ * @since 2.5.0
1582
+ *
1583
+ * @param string $key
1584
+ *
1585
+ * @return bool
1586
+ */
1587
+ private function should_use_network_storage( $key ) {
1588
+ return ( 'new_blog_install_map' === $key );
1589
+ }
1590
+
1591
+ /**
1592
+ * @param string $key
1593
+ * @param number|null $blog_id
1594
+ *
1595
+ * @return FS_Option_Manager
1596
+ */
1597
+ private function get_storage( $key, $blog_id = null ) {
1598
+ if ( is_numeric( $blog_id ) ){
1599
+ return FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true, $blog_id );
1600
+ }
1601
+
1602
+ return $this->should_use_network_storage( $key ) ?
1603
+ $this->_network_storage :
1604
+ $this->_storage;
1605
+ }
1606
+
1607
+ /**
1608
+ * @param string $name
1609
+ * @param bool $flush
1610
+ * @param number|null $blog_id
1611
+ *
1612
+ * @return mixed
1613
+ */
1614
+ private function get_option( $name, $flush = false, $blog_id = null ) {
1615
+ return $this->get_storage( $name, $blog_id )->get_option( $name, null, $flush );
1616
+ }
1617
+
1618
+ #--------------------------------------------------------------------------------
1619
+ #region Magic methods
1620
+ #--------------------------------------------------------------------------------
1621
+
1622
+ /**
1623
+ * @param string $name
1624
+ * @param int|string $value
1625
+ */
1626
+ function __set( $name, $value ) {
1627
+ $this->get_storage( $name )->set_option( $name, $value, true );
1628
+ }
1629
+
1630
+ /**
1631
+ * @param string $name
1632
+ *
1633
+ * @return bool
1634
+ */
1635
+ function __isset( $name ) {
1636
+ return $this->get_storage( $name )->has_option( $name, true );
1637
+ }
1638
+
1639
+ /**
1640
+ * @param string $name
1641
+ */
1642
+ function __unset( $name ) {
1643
+ $this->get_storage( $name )->unset_option( $name, true );
1644
+ }
1645
+
1646
+ /**
1647
+ * @param string $name
1648
+ *
1649
+ * @return null|int|string
1650
+ */
1651
+ function __get( $name ) {
1652
+ return $this->get_option(
1653
+ $name,
1654
+ // Reload storage from DB when accessing request_handler_* options to avoid race conditions.
1655
+ fs_starts_with( $name, 'request_handler' )
1656
+ );
1657
+ }
1658
+
1659
+ #endregion
1660
+ }
freemius/includes/managers/class-fs-key-value-storage.php CHANGED
@@ -298,6 +298,7 @@
298
  return $this->get( $k, null );
299
  }
300
 
 
301
  function offsetSet( $k, $v ) {
302
  if ( is_null( $k ) ) {
303
  throw new Exception( 'Can\'t append value to request params.' );
@@ -306,14 +307,17 @@
306
  }
307
  }
308
 
 
309
  function offsetExists( $k ) {
310
  return array_key_exists( $k, $this->_data );
311
  }
312
 
 
313
  function offsetUnset( $k ) {
314
  unset( $this->$k );
315
  }
316
 
 
317
  function offsetGet( $k ) {
318
  return $this->get( $k, null );
319
  }
@@ -325,6 +329,7 @@
325
  * @link http://php.net/manual/en/iterator.current.php
326
  * @return mixed Can return any type.
327
  */
 
328
  public function current() {
329
  return current( $this->_data );
330
  }
@@ -336,6 +341,7 @@
336
  * @link http://php.net/manual/en/iterator.next.php
337
  * @return void Any returned value is ignored.
338
  */
 
339
  public function next() {
340
  next( $this->_data );
341
  }
@@ -347,6 +353,7 @@
347
  * @link http://php.net/manual/en/iterator.key.php
348
  * @return mixed scalar on success, or null on failure.
349
  */
 
350
  public function key() {
351
  return key( $this->_data );
352
  }
@@ -359,6 +366,7 @@
359
  * @return boolean The return value will be casted to boolean and then evaluated.
360
  * Returns true on success or false on failure.
361
  */
 
362
  public function valid() {
363
  $key = key( $this->_data );
364
 
@@ -372,6 +380,7 @@
372
  * @link http://php.net/manual/en/iterator.rewind.php
373
  * @return void Any returned value is ignored.
374
  */
 
375
  public function rewind() {
376
  reset( $this->_data );
377
  }
@@ -386,6 +395,7 @@
386
  * <p>
387
  * The return value is cast to an integer.
388
  */
 
389
  public function count() {
390
  return count( $this->_data );
391
  }
298
  return $this->get( $k, null );
299
  }
300
 
301
+ #[ReturnTypeWillChange]
302
  function offsetSet( $k, $v ) {
303
  if ( is_null( $k ) ) {
304
  throw new Exception( 'Can\'t append value to request params.' );
307
  }
308
  }
309
 
310
+ #[ReturnTypeWillChange]
311
  function offsetExists( $k ) {
312
  return array_key_exists( $k, $this->_data );
313
  }
314
 
315
+ #[ReturnTypeWillChange]
316
  function offsetUnset( $k ) {
317
  unset( $this->$k );
318
  }
319
 
320
+ #[ReturnTypeWillChange]
321
  function offsetGet( $k ) {
322
  return $this->get( $k, null );
323
  }
329
  * @link http://php.net/manual/en/iterator.current.php
330
  * @return mixed Can return any type.
331
  */
332
+ #[ReturnTypeWillChange]
333
  public function current() {
334
  return current( $this->_data );
335
  }
341
  * @link http://php.net/manual/en/iterator.next.php
342
  * @return void Any returned value is ignored.
343
  */
344
+ #[ReturnTypeWillChange]
345
  public function next() {
346
  next( $this->_data );
347
  }
353
  * @link http://php.net/manual/en/iterator.key.php
354
  * @return mixed scalar on success, or null on failure.
355
  */
356
+ #[ReturnTypeWillChange]
357
  public function key() {
358
  return key( $this->_data );
359
  }
366
  * @return boolean The return value will be casted to boolean and then evaluated.
367
  * Returns true on success or false on failure.
368
  */
369
+ #[ReturnTypeWillChange]
370
  public function valid() {
371
  $key = key( $this->_data );
372
 
380
  * @link http://php.net/manual/en/iterator.rewind.php
381
  * @return void Any returned value is ignored.
382
  */
383
+ #[ReturnTypeWillChange]
384
  public function rewind() {
385
  reset( $this->_data );
386
  }
395
  * <p>
396
  * The return value is cast to an integer.
397
  */
398
+ #[ReturnTypeWillChange]
399
  public function count() {
400
  return count( $this->_data );
401
  }
freemius/includes/managers/class-fs-option-manager.php CHANGED
@@ -11,14 +11,11 @@
11
  }
12
 
13
  /**
14
- * 3-layer lazy options manager.
15
- * layer 3: Memory
16
- * layer 2: Cache (if there's any caching plugin and if WP_FS__DEBUG_SDK is FALSE)
17
- * layer 1: Database (options table). All options stored as one option record in the DB to reduce number of DB
18
- * queries.
19
  *
20
- * If load() is not explicitly called, starts as empty manager. Same thing about saving the data - you have to
21
- * explicitly call store().
22
  *
23
  * Class Freemius_Option_Manager
24
  */
@@ -157,59 +154,33 @@
157
  function load( $flush = false ) {
158
  $this->_logger->entrance();
159
 
160
- $option_name = $this->get_option_manager_name();
161
-
162
- if ( $flush || ! isset( $this->_options ) ) {
163
- if ( isset( $this->_options ) ) {
164
- // Clear prev options.
165
- $this->clear();
166
- }
167
-
168
- $cache_group = $this->get_cache_group();
169
-
170
- if ( WP_FS__DEBUG_SDK ) {
171
-
172
- // Don't use cache layer in DEBUG mode.
173
- $load_options = empty( $this->_options );
174
-
175
- } else {
176
-
177
- $this->_options = wp_cache_get(
178
- $option_name,
179
- $cache_group
180
- );
181
 
182
- $load_options = ( false === $this->_options );
183
- }
 
 
184
 
185
- $cached = true;
186
 
187
- if ( $load_options ) {
188
- if ( $this->_is_network_storage ) {
189
- $this->_options = get_site_option( $option_name );
190
- } else if ( $this->_blog_id > 0 ) {
191
- $this->_options = get_blog_option( $this->_blog_id, $option_name );
192
- } else {
193
- $this->_options = get_option( $option_name );
194
- }
195
 
196
- if ( is_string( $this->_options ) ) {
197
- $this->_options = json_decode( $this->_options );
198
- }
199
 
200
  // $this->_logger->info('get_option = ' . var_export($this->_options, true));
201
 
202
- if ( false === $this->_options ) {
203
- $this->clear();
204
- }
205
-
206
- $cached = false;
207
- }
208
-
209
- if ( ! WP_FS__DEBUG_SDK && ! $cached ) {
210
- // Set non encoded cache.
211
- wp_cache_set( $option_name, $this->_options, $cache_group );
212
- }
213
  }
214
  }
215
 
@@ -272,10 +243,15 @@
272
  * @since 1.0.6
273
  *
274
  * @param string $option
 
275
  *
276
  * @return bool
277
  */
278
- function has_option( $option ) {
 
 
 
 
279
  return array_key_exists( $option, $this->_options );
280
  }
281
 
@@ -285,14 +261,15 @@
285
  *
286
  * @param string $option
287
  * @param mixed $default
 
288
  *
289
  * @return mixed
290
  */
291
- function get_option( $option, $default = null ) {
292
  $this->_logger->entrance( 'option = ' . $option );
293
 
294
- if ( ! $this->is_loaded() ) {
295
- $this->load();
296
  }
297
 
298
  if ( is_array( $this->_options ) ) {
@@ -310,7 +287,7 @@
310
  /**
311
  * If it's an object, return a clone of the object, otherwise,
312
  * external changes of the object will actually change the value
313
- * of the object in the options manager which may lead to an unexpected
314
  * behaviour and data integrity when a store() call is triggered.
315
  *
316
  * Example:
@@ -436,10 +413,6 @@
436
  } else {
437
  update_option( $option_name, $this->_options, $this->_autoload );
438
  }
439
-
440
- if ( ! WP_FS__DEBUG_SDK ) {
441
- wp_cache_set( $option_name, $this->_options, $this->get_cache_group() );
442
- }
443
  }
444
 
445
  /**
@@ -499,23 +472,5 @@
499
  return $this->_id;
500
  }
501
 
502
- /**
503
- * @author Vova Feldman (@svovaf)
504
- * @since 2.0.0
505
- *
506
- * @return string
507
- */
508
- private function get_cache_group() {
509
- $group = WP_FS__SLUG;
510
-
511
- if ( $this->_is_network_storage ) {
512
- $group .= '_ms';
513
- } else if ( $this->_blog_id > 0 ) {
514
- $group .= "_s{$this->_blog_id}";
515
- }
516
-
517
- return $group;
518
- }
519
-
520
  #endregion
521
  }
11
  }
12
 
13
  /**
14
+ * 2-layer lazy options manager.
15
+ * layer 2: Memory
16
+ * layer 1: Database (options table). All options stored as one option record in the DB to reduce number of DB queries.
 
 
17
  *
18
+ * If load() is not explicitly called, starts as empty manager. Same thing about saving the data - you have to explicitly call store().
 
19
  *
20
  * Class Freemius_Option_Manager
21
  */
154
  function load( $flush = false ) {
155
  $this->_logger->entrance();
156
 
157
+ if ( ! $flush && isset( $this->_options ) ) {
158
+ return;
159
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
+ if ( isset( $this->_options ) ) {
162
+ // Clear prev options.
163
+ $this->clear();
164
+ }
165
 
166
+ $option_name = $this->get_option_manager_name();
167
 
168
+ if ( $this->_is_network_storage ) {
169
+ $this->_options = get_site_option( $option_name );
170
+ } else if ( $this->_blog_id > 0 ) {
171
+ $this->_options = get_blog_option( $this->_blog_id, $option_name );
172
+ } else {
173
+ $this->_options = get_option( $option_name );
174
+ }
 
175
 
176
+ if ( is_string( $this->_options ) ) {
177
+ $this->_options = json_decode( $this->_options );
178
+ }
179
 
180
  // $this->_logger->info('get_option = ' . var_export($this->_options, true));
181
 
182
+ if ( false === $this->_options ) {
183
+ $this->clear();
 
 
 
 
 
 
 
 
 
184
  }
185
  }
186
 
243
  * @since 1.0.6
244
  *
245
  * @param string $option
246
+ * @param bool $flush
247
  *
248
  * @return bool
249
  */
250
+ function has_option( $option, $flush = false ) {
251
+ if ( ! $this->is_loaded() || $flush ) {
252
+ $this->load( $flush );
253
+ }
254
+
255
  return array_key_exists( $option, $this->_options );
256
  }
257
 
261
  *
262
  * @param string $option
263
  * @param mixed $default
264
+ * @param bool $flush
265
  *
266
  * @return mixed
267
  */
268
+ function get_option( $option, $default = null, $flush = false ) {
269
  $this->_logger->entrance( 'option = ' . $option );
270
 
271
+ if ( ! $this->is_loaded() || $flush ) {
272
+ $this->load( $flush );
273
  }
274
 
275
  if ( is_array( $this->_options ) ) {
287
  /**
288
  * If it's an object, return a clone of the object, otherwise,
289
  * external changes of the object will actually change the value
290
+ * of the object in the option manager which may lead to an unexpected
291
  * behaviour and data integrity when a store() call is triggered.
292
  *
293
  * Example:
413
  } else {
414
  update_option( $option_name, $this->_options, $this->_autoload );
415
  }
 
 
 
 
416
  }
417
 
418
  /**
472
  return $this->_id;
473
  }
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
  #endregion
476
  }
freemius/includes/managers/class-fs-permission-manager.php ADDED
@@ -0,0 +1,698 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2022, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.1
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * This class is responsible for managing the user permissions.
15
+ *
16
+ * @author Vova Feldman (@svovaf)
17
+ * @since 2.5.1
18
+ */
19
+ class FS_Permission_Manager {
20
+ /**
21
+ * @var Freemius
22
+ */
23
+ private $_fs;
24
+ /**
25
+ * @var FS_Storage
26
+ */
27
+ private $_storage;
28
+
29
+ /**
30
+ * @var array<number,self>
31
+ */
32
+ private static $_instances = array();
33
+
34
+ const PERMISSION_USER = 'user';
35
+ const PERMISSION_SITE = 'site';
36
+ const PERMISSION_EVENTS = 'events';
37
+ const PERMISSION_ESSENTIALS = 'essentials';
38
+ const PERMISSION_DIAGNOSTIC = 'diagnostic';
39
+ const PERMISSION_EXTENSIONS = 'extensions';
40
+ const PERMISSION_NEWSLETTER = 'newsletter';
41
+
42
+ /**
43
+ * @param Freemius $fs
44
+ *
45
+ * @return self
46
+ */
47
+ static function instance( Freemius $fs ) {
48
+ $id = $fs->get_id();
49
+
50
+ if ( ! isset( self::$_instances[ $id ] ) ) {
51
+ self::$_instances[ $id ] = new self( $fs );
52
+ }
53
+
54
+ return self::$_instances[ $id ];
55
+ }
56
+
57
+ /**
58
+ * @param Freemius $fs
59
+ */
60
+ protected function __construct( Freemius $fs ) {
61
+ $this->_fs = $fs;
62
+ $this->_storage = FS_Storage::instance( $fs->get_module_type(), $fs->get_slug() );
63
+ }
64
+
65
+ /**
66
+ * @return string[]
67
+ */
68
+ static function get_all_permission_ids() {
69
+ return array(
70
+ self::PERMISSION_USER,
71
+ self::PERMISSION_SITE,
72
+ self::PERMISSION_EVENTS,
73
+ self::PERMISSION_ESSENTIALS,
74
+ self::PERMISSION_DIAGNOSTIC,
75
+ self::PERMISSION_EXTENSIONS,
76
+ self::PERMISSION_NEWSLETTER,
77
+ );
78
+ }
79
+
80
+ /**
81
+ * @return string[]
82
+ */
83
+ static function get_api_managed_permission_ids() {
84
+ return array(
85
+ self::PERMISSION_USER,
86
+ self::PERMISSION_SITE,
87
+ self::PERMISSION_EXTENSIONS,
88
+ );
89
+ }
90
+
91
+ /**
92
+ * @param string $permission
93
+ *
94
+ * @return bool
95
+ */
96
+ static function is_supported_permission( $permission ) {
97
+ return in_array( $permission, self::get_all_permission_ids() );
98
+ }
99
+
100
+ /**
101
+ * @param bool $is_license_activation
102
+ * @param array[] $extra_permissions
103
+ *
104
+ * @return array[]
105
+ */
106
+ function get_permissions( $is_license_activation, array $extra_permissions = array() ) {
107
+ return $is_license_activation ?
108
+ $this->get_license_activation_permissions( $extra_permissions ) :
109
+ $this->get_opt_in_permissions( $extra_permissions );
110
+ }
111
+
112
+ #--------------------------------------------------------------------------------
113
+ #region Opt-In Permissions
114
+ #--------------------------------------------------------------------------------
115
+
116
+ /**
117
+ * @param array[] $extra_permissions
118
+ *
119
+ * @return array[]
120
+ */
121
+ function get_opt_in_permissions(
122
+ array $extra_permissions = array(),
123
+ $load_default_from_storage = false,
124
+ $is_optional = false
125
+ ) {
126
+ $permissions = array_merge(
127
+ $this->get_opt_in_required_permissions( $load_default_from_storage ),
128
+ $this->get_opt_in_optional_permissions( $load_default_from_storage, $is_optional ),
129
+ $extra_permissions
130
+ );
131
+
132
+ return $this->get_sorted_permissions_by_priority( $permissions );
133
+ }
134
+
135
+ /**
136
+ * @param bool $load_default_from_storage
137
+ *
138
+ * @return array[]
139
+ */
140
+ function get_opt_in_required_permissions( $load_default_from_storage = false ) {
141
+ return array( $this->get_user_permission( $load_default_from_storage ) );
142
+ }
143
+
144
+ /**
145
+ * @param bool $load_default_from_storage
146
+ * @param bool $is_optional
147
+ *
148
+ * @return array[]
149
+ */
150
+ function get_opt_in_optional_permissions(
151
+ $load_default_from_storage = false,
152
+ $is_optional = false
153
+ ) {
154
+ return array_merge(
155
+ $this->get_opt_in_diagnostic_permissions( $load_default_from_storage, $is_optional ),
156
+ array( $this->get_extensions_permission(
157
+ false,
158
+ false,
159
+ $load_default_from_storage
160
+ ) )
161
+ );
162
+ }
163
+
164
+ /**
165
+ * @param bool $load_default_from_storage
166
+ * @param bool $is_optional
167
+ *
168
+ * @return array[]
169
+ */
170
+ function get_opt_in_diagnostic_permissions(
171
+ $load_default_from_storage = false,
172
+ $is_optional = false
173
+ ) {
174
+ // Alias.
175
+ $fs = $this->_fs;
176
+
177
+ $permissions = array();
178
+
179
+ $permissions[] = $this->get_permission(
180
+ self::PERMISSION_SITE,
181
+ 'admin-links',
182
+ $fs->get_text_inline( 'View Basic Website Info', 'permissions-site' ),
183
+ $fs->get_text_inline( 'Homepage URL & title, WP & PHP versions, and site language', 'permissions-site_desc' ),
184
+ sprintf(
185
+ /* translators: %s: 'Plugin' or 'Theme' */
186
+ $fs->get_text_inline( 'To provide additional functionality that\'s relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-site_tooltip' ),
187
+ $fs->get_module_label( true )
188
+ ),
189
+ 10,
190
+ $is_optional,
191
+ true,
192
+ $load_default_from_storage
193
+ );
194
+
195
+ $permissions[] = $this->get_permission(
196
+ self::PERMISSION_EVENTS,
197
+ 'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ),
198
+ sprintf( $fs->get_text_inline( 'View Basic %s Info', 'permissions-events' ), $fs->get_module_label() ),
199
+ sprintf(
200
+ /* translators: %s: 'Plugin' or 'Theme' */
201
+ $fs->get_text_inline( 'Current %s & SDK versions, and if active or uninstalled', 'permissions-events_desc' ),
202
+ $fs->get_module_label( true )
203
+ ),
204
+ '',
205
+ 20,
206
+ $is_optional,
207
+ true,
208
+ $load_default_from_storage
209
+ );
210
+
211
+ return $permissions;
212
+ }
213
+
214
+ #endregion
215
+
216
+ #--------------------------------------------------------------------------------
217
+ #region License Activation Permissions
218
+ #--------------------------------------------------------------------------------
219
+
220
+ /**
221
+ * @param array[] $extra_permissions
222
+ *
223
+ * @return array[]
224
+ */
225
+ function get_license_activation_permissions(
226
+ array $extra_permissions = array(),
227
+ $include_optional_label = true
228
+ ) {
229
+ $permissions = array_merge(
230
+ $this->get_license_required_permissions(),
231
+ $this->get_license_optional_permissions( $include_optional_label ),
232
+ $extra_permissions
233
+ );
234
+
235
+ return $this->get_sorted_permissions_by_priority( $permissions );
236
+ }
237
+
238
+ /**
239
+ * @param bool $load_default_from_storage
240
+ *
241
+ * @return array[]
242
+ */
243
+ function get_license_required_permissions( $load_default_from_storage = false ) {
244
+ // Alias.
245
+ $fs = $this->_fs;
246
+
247
+ $permissions = array();
248
+
249
+ $permissions[] = $this->get_permission(
250
+ self::PERMISSION_ESSENTIALS,
251
+ 'admin-links',
252
+ $fs->get_text_inline( 'View License Essentials', 'permissions-essentials' ),
253
+ $fs->get_text_inline(
254
+ sprintf(
255
+ /* translators: %s: 'Plugin' or 'Theme' */
256
+ 'Homepage URL, %s version, SDK version',
257
+ $fs->get_module_label()
258
+ ),
259
+ 'permissions-essentials_desc'
260
+ ),
261
+ sprintf(
262
+ /* translators: %s: 'Plugin' or 'Theme' */
263
+ $fs->get_text_inline( 'To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize.', 'permissions-essentials_tooltip' ),
264
+ $fs->get_module_label( true )
265
+ ),
266
+ 10,
267
+ false,
268
+ true,
269
+ $load_default_from_storage
270
+ );
271
+
272
+ $permissions[] = $this->get_permission(
273
+ self::PERMISSION_EVENTS,
274
+ 'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ),
275
+ sprintf( $fs->get_text_inline( 'View %s State', 'permissions-events' ), $fs->get_module_label() ),
276
+ sprintf(
277
+ /* translators: %s: 'Plugin' or 'Theme' */
278
+ $fs->get_text_inline( 'Is active, deactivated, or uninstalled', 'permissions-events_desc-paid' ),
279
+ $fs->get_module_label( true )
280
+ ),
281
+ sprintf( $fs->get_text_inline( 'So you can reuse the license when the %s is no longer active.', 'permissions-events_tooltip' ), $fs->get_module_label( true ) ),
282
+ 20,
283
+ false,
284
+ true,
285
+ $load_default_from_storage
286
+ );
287
+
288
+ return $permissions;
289
+ }
290
+
291
+ /**
292
+ * @return array[]
293
+ */
294
+ function get_license_optional_permissions(
295
+ $include_optional_label = false,
296
+ $load_default_from_storage = false
297
+ ) {
298
+ return array(
299
+ $this->get_diagnostic_permission( $include_optional_label, $load_default_from_storage ),
300
+ $this->get_extensions_permission( true, $include_optional_label, $load_default_from_storage ),
301
+ );
302
+ }
303
+
304
+ /**
305
+ * @param bool $include_optional_label
306
+ * @param bool $load_default_from_storage
307
+ *
308
+ * @return array
309
+ */
310
+ function get_diagnostic_permission(
311
+ $include_optional_label = false,
312
+ $load_default_from_storage = false
313
+ ) {
314
+ return $this->get_permission(
315
+ self::PERMISSION_DIAGNOSTIC,
316
+ 'wordpress-alt',
317
+ $this->_fs->get_text_inline( 'View Diagnostic Info', 'permissions-diagnostic' ) . ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ),
318
+ $this->_fs->get_text_inline( 'WordPress & PHP versions, site language & title', 'permissions-diagnostic_desc' ),
319
+ sprintf(
320
+ /* translators: %s: 'Plugin' or 'Theme' */
321
+ $this->_fs->get_text_inline( 'To avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-diagnostic_tooltip' ),
322
+ $this->_fs->get_module_label( true )
323
+ ),
324
+ 25,
325
+ true,
326
+ true,
327
+ $load_default_from_storage
328
+ );
329
+ }
330
+
331
+ #endregion
332
+
333
+ #--------------------------------------------------------------------------------
334
+ #region Common Permissions
335
+ #--------------------------------------------------------------------------------
336
+
337
+ /**
338
+ * @param bool $is_license_activation
339
+ * @param bool $include_optional_label
340
+ * @param bool $load_default_from_storage
341
+ *
342
+ * @return array
343
+ */
344
+ function get_extensions_permission(
345
+ $is_license_activation,
346
+ $include_optional_label = false,
347
+ $load_default_from_storage = false
348
+ ) {
349
+ $is_on_by_default = ! $is_license_activation;
350
+
351
+ return $this->get_permission(
352
+ self::PERMISSION_EXTENSIONS,
353
+ 'block-default',
354
+ $this->_fs->get_text_inline( 'View Plugins & Themes List', 'permissions-extensions' ) . ( $is_license_activation ? ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ) : '' ),
355
+ $this->_fs->get_text_inline( 'Names, slugs, versions, and if active or not', 'permissions-extensions_desc' ),
356
+ $this->_fs->get_text_inline( 'To ensure compatibility and avoid conflicts with your installed plugins and themes.', 'permissions-events_tooltip' ),
357
+ 25,
358
+ true,
359
+ $is_on_by_default,
360
+ $load_default_from_storage
361
+ );
362
+ }
363
+
364
+ /**
365
+ * @param bool $load_default_from_storage
366
+ *
367
+ * @return array
368
+ */
369
+ function get_user_permission( $load_default_from_storage = false ) {
370
+ return $this->get_permission(
371
+ self::PERMISSION_USER,
372
+ 'admin-users',
373
+ $this->_fs->get_text_inline( 'View Basic Profile Info', 'permissions-profile' ),
374
+ $this->_fs->get_text_inline( 'Your WordPress user\'s: first & last name, and email address', 'permissions-profile_desc' ),
375
+ $this->_fs->get_text_inline( 'Never miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features.', 'permissions-profile_tooltip' ),
376
+ 5,
377
+ false,
378
+ true,
379
+ $load_default_from_storage
380
+ );
381
+ }
382
+
383
+ #endregion
384
+
385
+ #--------------------------------------------------------------------------------
386
+ #region Optional Permissions
387
+ #--------------------------------------------------------------------------------
388
+
389
+ /**
390
+ * @return array[]
391
+ */
392
+ function get_newsletter_permission() {
393
+ return $this->get_permission(
394
+ self::PERMISSION_NEWSLETTER,
395
+ 'email-alt',
396
+ $this->_fs->get_text_inline( 'Newsletter', 'permissions-newsletter' ),
397
+ $this->_fs->get_text_inline( 'Updates, announcements, marketing, no spam', 'permissions-newsletter_desc' ),
398
+ '',
399
+ 15
400
+ );
401
+ }
402
+
403
+ #endregion
404
+
405
+ #--------------------------------------------------------------------------------
406
+ #region Permissions Storage
407
+ #--------------------------------------------------------------------------------
408
+
409
+ /**
410
+ * @param int|null $blog_id
411
+ *
412
+ * @return bool
413
+ */
414
+ function is_extensions_tracking_allowed( $blog_id = null ) {
415
+ return $this->is_permission_allowed( self::PERMISSION_EXTENSIONS, ! $this->_fs->is_premium(), $blog_id );
416
+ }
417
+
418
+ /**
419
+ * @param int|null $blog_id
420
+ *
421
+ * @return bool
422
+ */
423
+ function is_essentials_tracking_allowed( $blog_id = null ) {
424
+ return $this->is_permission_allowed( self::PERMISSION_ESSENTIALS, true, $blog_id );
425
+ }
426
+
427
+ /**
428
+ * @param bool $default
429
+ *
430
+ * @return bool
431
+ */
432
+ function is_diagnostic_tracking_allowed( $default = true ) {
433
+ return $this->_fs->is_premium() ?
434
+ $this->is_permission_allowed( self::PERMISSION_DIAGNOSTIC, $default ) :
435
+ $this->is_permission_allowed( self::PERMISSION_SITE, $default );
436
+ }
437
+
438
+ /**
439
+ * @param int|null $blog_id
440
+ *
441
+ * @return bool
442
+ */
443
+ function is_homepage_url_tracking_allowed( $blog_id = null ) {
444
+ return $this->is_permission_allowed( $this->get_site_permission_name(), true, $blog_id );
445
+ }
446
+
447
+ /**
448
+ * @param int|null $blog_id
449
+ *
450
+ * @return bool
451
+ */
452
+ function update_site_tracking( $is_enabled, $blog_id = null, $only_if_not_set = false ) {
453
+ $permissions = $this->get_site_tracking_permission_names();
454
+
455
+ $result = true;
456
+ foreach ( $permissions as $permission ) {
457
+ if ( ! $only_if_not_set || ! $this->is_permission_set( $permission, $blog_id ) ) {
458
+ $result = ( $result && $this->update_permission_tracking_flag( $permission, $is_enabled, $blog_id ) );
459
+ }
460
+ }
461
+
462
+ return $result;
463
+ }
464
+
465
+ /**
466
+ * @param string $permission
467
+ * @param bool $default
468
+ * @param int|null $blog_id
469
+ *
470
+ * @return bool
471
+ */
472
+ function is_permission_allowed( $permission, $default = false, $blog_id = null ) {
473
+ if ( ! self::is_supported_permission( $permission ) ) {
474
+ return $default;
475
+ }
476
+
477
+ return $this->is_permission( $permission, true, $blog_id );
478
+ }
479
+
480
+ /**
481
+ * @param string $permission
482
+ * @param bool $is_allowed
483
+ * @param int|null $blog_id
484
+ *
485
+ * @return bool
486
+ */
487
+ function is_permission( $permission, $is_allowed, $blog_id = null ) {
488
+ if ( ! self::is_supported_permission( $permission ) ) {
489
+ return false;
490
+ }
491
+
492
+ $tag = "is_{$permission}_tracking_allowed";
493
+
494
+ return ( $is_allowed === $this->_fs->apply_filters(
495
+ $tag,
496
+ $this->_storage->get(
497
+ $tag,
498
+ $this->get_permission_default( $permission ),
499
+ $blog_id,
500
+ FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
501
+ )
502
+ ) );
503
+ }
504
+
505
+ /**
506
+ * @param string $permission
507
+ * @param int|null $blog_id
508
+ *
509
+ * @return bool
510
+ */
511
+ function is_permission_set( $permission, $blog_id = null ) {
512
+ $tag = "is_{$permission}_tracking_allowed";
513
+
514
+ $permission = $this->_storage->get(
515
+ $tag,
516
+ null,
517
+ $blog_id,
518
+ FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
519
+ );
520
+
521
+ return is_bool( $permission );
522
+ }
523
+
524
+ /**
525
+ * @param string[] $permissions
526
+ * @param bool $is_allowed
527
+ *
528
+ * @return bool `true` if all given permissions are in sync with `$is_allowed`.
529
+ */
530
+ function are_permissions( $permissions, $is_allowed, $blog_id = null ) {
531
+ foreach ( $permissions as $permission ) {
532
+ if ( ! $this->is_permission( $permission, $is_allowed, $blog_id ) ) {
533
+ return false;
534
+ }
535
+ }
536
+
537
+ return true;
538
+ }
539
+
540
+ /**
541
+ * @param string $permission
542
+ * @param bool $is_enabled
543
+ * @param int|null $blog_id
544
+ *
545
+ * @return bool `false` if permission not supported or `$is_enabled` is not a boolean.
546
+ */
547
+ function update_permission_tracking_flag( $permission, $is_enabled, $blog_id = null ) {
548
+ if ( is_bool( $is_enabled ) && self::is_supported_permission( $permission ) ) {
549
+ $this->_storage->store(
550
+ "is_{$permission}_tracking_allowed",
551
+ $is_enabled,
552
+ $blog_id,
553
+ FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED
554
+ );
555
+
556
+ return true;
557
+ }
558
+
559
+ return false;
560
+ }
561
+
562
+ /**
563
+ * @param array<string,bool> $permissions
564
+ */
565
+ function update_permissions_tracking_flag( $permissions ) {
566
+ foreach ( $permissions as $permission => $is_enabled ) {
567
+ $this->update_permission_tracking_flag( $permission, $is_enabled );
568
+ }
569
+ }
570
+
571
+ #endregion
572
+
573
+
574
+ /**
575
+ * @param string $permission
576
+ *
577
+ * @return bool
578
+ */
579
+ function get_permission_default( $permission ) {
580
+ if (
581
+ $this->_fs->is_premium() &&
582
+ self::PERMISSION_EXTENSIONS === $permission
583
+ ) {
584
+ return false;
585
+ }
586
+
587
+ // All permissions except for the extensions in paid version are on by default when the user opts in to usage tracking.
588
+ return true;
589
+ }
590
+
591
+ /**
592
+ * @return string
593
+ */
594
+ function get_site_permission_name() {
595
+ return $this->_fs->is_premium() ?
596
+ self::PERMISSION_ESSENTIALS :
597
+ self::PERMISSION_SITE;
598
+ }
599
+
600
+ /**
601
+ * @return string[]
602
+ */
603
+ function get_site_tracking_permission_names() {
604
+ return $this->_fs->is_premium() ?
605
+ array(
606
+ FS_Permission_Manager::PERMISSION_ESSENTIALS,
607
+ FS_Permission_Manager::PERMISSION_EVENTS,
608
+ ) :
609
+ array( FS_Permission_Manager::PERMISSION_SITE );
610
+ }
611
+
612
+ #--------------------------------------------------------------------------------
613
+ #region Rendering
614
+ #--------------------------------------------------------------------------------
615
+
616
+ /**
617
+ * @param array $permission
618
+ */
619
+ function render_permission( array $permission ) {
620
+ fs_require_template( 'connect/permission.php', $permission );
621
+ }
622
+
623
+ /**
624
+ * @param array $permissions_group
625
+ */
626
+ function render_permissions_group( array $permissions_group ) {
627
+ $permissions_group[ 'fs' ] = $this->_fs;
628
+
629
+ fs_require_template( 'connect/permissions-group.php', $permissions_group );
630
+ }
631
+
632
+ function require_permissions_js() {
633
+ fs_require_once_template( 'js/permissions.php', $params );
634
+ }
635
+
636
+ #endregion
637
+
638
+ #--------------------------------------------------------------------------------
639
+ #region Helper Methods
640
+ #--------------------------------------------------------------------------------
641
+
642
+ /**
643
+ * @param string $id
644
+ * @param string $dashicon
645
+ * @param string $label
646
+ * @param string $desc
647
+ * @param string $tooltip
648
+ * @param int $priority
649
+ * @param bool $is_optional
650
+ * @param bool $is_on_by_default
651
+ * @param bool $load_from_storage
652
+ *
653
+ * @return array
654
+ */
655
+ private function get_permission(
656
+ $id,
657
+ $dashicon,
658
+ $label,
659
+ $desc,
660
+ $tooltip = '',
661
+ $priority = 10,
662
+ $is_optional = false,
663
+ $is_on_by_default = true,
664
+ $load_from_storage = false
665
+ ) {
666
+ $is_on = $load_from_storage ?
667
+ $this->is_permission_allowed( $id, $is_on_by_default ) :
668
+ $is_on_by_default;
669
+
670
+ return array(
671
+ 'id' => $id,
672
+ 'icon-class' => $this->_fs->apply_filters( "permission_{$id}_icon", "dashicons dashicons-{$dashicon}" ),
673
+ 'label' => $this->_fs->apply_filters( "permission_{$id}_label", $label ),
674
+ 'tooltip' => $this->_fs->apply_filters( "permission_{$id}_tooltip", $tooltip ),
675
+ 'desc' => $this->_fs->apply_filters( "permission_{$id}_desc", $desc ),
676
+ 'priority' => $this->_fs->apply_filters( "permission_{$id}_priority", $priority ),
677
+ 'optional' => $is_optional,
678
+ 'default' => $this->_fs->apply_filters( "permission_{$id}_default", $is_on ),
679
+ );
680
+ }
681
+
682
+ /**
683
+ * @param array $permissions
684
+ *
685
+ * @return array[]
686
+ */
687
+ private function get_sorted_permissions_by_priority( array $permissions ) {
688
+ // Allow filtering of the permissions list.
689
+ $permissions = $this->_fs->apply_filters( 'permission_list', $permissions );
690
+
691
+ // Sort by priority.
692
+ uasort( $permissions, 'fs_sort_by_priority' );
693
+
694
+ return $permissions;
695
+ }
696
+
697
+ #endregion
698
+ }
freemius/includes/managers/class-fs-plugin-manager.php CHANGED
@@ -211,10 +211,23 @@
211
  * @return bool|\FS_Plugin
212
  */
213
  function get() {
214
- return isset( $this->_module ) ?
215
- $this->_module :
216
- false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
-
219
-
220
  }
211
  * @return bool|\FS_Plugin
212
  */
213
  function get() {
214
+ if ( isset( $this->_module ) ) {
215
+ return $this->_module;
216
+ }
217
+
218
+ if ( empty( $this->_module_id ) ) {
219
+ return false;
220
+ }
221
+
222
+ /**
223
+ * Return an FS_Plugin entity that has its `id` and `is_live` properties set (`is_live` is initialized in the FS_Plugin constructor) to avoid triggering an error that is relevant to these properties when the FS_Plugin entity is used before the `parse_settings()` method is called. This can happen when creating a regular WordPress site by cloning a subsite of a multisite network and the data that is stored in the network-level storage is not cloned.
224
+ *
225
+ * @author Leo Fajardo (@leorw)
226
+ * @since 2.5.0
227
+ */
228
+ $plugin = new FS_Plugin();
229
+ $plugin->id = $this->_module_id;
230
+
231
+ return $plugin;
232
  }
 
 
233
  }
freemius/includes/sdk/FreemiusBase.php CHANGED
@@ -46,174 +46,172 @@
46
  require_once FS_SDK__EXCEPTIONS_PATH . $e . '.php';
47
  }
48
 
49
- if ( class_exists( 'Freemius_Api_Base' ) ) {
50
- return;
51
- }
52
-
53
- abstract class Freemius_Api_Base {
54
- const VERSION = '1.0.4';
55
- const FORMAT = 'json';
56
-
57
- protected $_id;
58
- protected $_public;
59
- protected $_secret;
60
- protected $_scope;
61
- protected $_isSandbox;
62
-
63
- /**
64
- * @param string $pScope 'app', 'developer', 'plugin', 'user' or 'install'.
65
- * @param number $pID Element's id.
66
- * @param string $pPublic Public key.
67
- * @param string $pSecret Element's secret key.
68
- * @param bool $pIsSandbox Whether or not to run API in sandbox mode.
69
- */
70
- public function Init( $pScope, $pID, $pPublic, $pSecret, $pIsSandbox = false ) {
71
- $this->_id = $pID;
72
- $this->_public = $pPublic;
73
- $this->_secret = $pSecret;
74
- $this->_scope = $pScope;
75
- $this->_isSandbox = $pIsSandbox;
76
- }
77
-
78
- public function IsSandbox() {
79
- return $this->_isSandbox;
80
- }
81
-
82
- function CanonizePath( $pPath ) {
83
- $pPath = trim( $pPath, '/' );
84
- $query_pos = strpos( $pPath, '?' );
85
- $query = '';
86
-
87
- if ( false !== $query_pos ) {
88
- $query = substr( $pPath, $query_pos );
89
- $pPath = substr( $pPath, 0, $query_pos );
90
- }
91
-
92
- // Trim '.json' suffix.
93
- $format_length = strlen( '.' . self::FORMAT );
94
- $start = $format_length * ( - 1 ); //negative
95
- if ( substr( strtolower( $pPath ), $start ) === ( '.' . self::FORMAT ) ) {
96
- $pPath = substr( $pPath, 0, strlen( $pPath ) - $format_length );
97
- }
98
-
99
- switch ( $this->_scope ) {
100
- case 'app':
101
- $base = '/apps/' . $this->_id;
102
- break;
103
- case 'developer':
104
- $base = '/developers/' . $this->_id;
105
- break;
106
- case 'user':
107
- $base = '/users/' . $this->_id;
108
- break;
109
- case 'plugin':
110
- $base = '/plugins/' . $this->_id;
111
- break;
112
- case 'install':
113
- $base = '/installs/' . $this->_id;
114
- break;
115
- default:
116
- throw new Freemius_Exception( 'Scope not implemented.' );
117
- }
118
-
119
- return '/v' . FS_API__VERSION . $base .
120
- ( ! empty( $pPath ) ? '/' : '' ) . $pPath .
121
- ( ( false === strpos( $pPath, '.' ) ) ? '.' . self::FORMAT : '' ) . $query;
122
- }
123
-
124
- abstract function MakeRequest( $pCanonizedPath, $pMethod = 'GET', $pParams = array() );
125
-
126
- /**
127
- * @param string $pPath
128
- * @param string $pMethod
129
- * @param array $pParams
130
- *
131
- * @return object[]|object|null
132
- */
133
- private function _Api( $pPath, $pMethod = 'GET', $pParams = array() ) {
134
- $pMethod = strtoupper( $pMethod );
135
-
136
- try {
137
- $result = $this->MakeRequest( $pPath, $pMethod, $pParams );
138
- } catch ( Freemius_Exception $e ) {
139
- // Map to error object.
140
- $result = (object) $e->getResult();
141
- } catch ( Exception $e ) {
142
- // Map to error object.
143
- $result = (object) array(
144
- 'error' => (object) array(
145
- 'type' => 'Unknown',
146
- 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')',
147
- 'code' => 'unknown',
148
- 'http' => 402
149
- )
150
- );
151
- }
152
-
153
- return $result;
154
- }
155
-
156
- public function Api( $pPath, $pMethod = 'GET', $pParams = array() ) {
157
- return $this->_Api( $this->CanonizePath( $pPath ), $pMethod, $pParams );
158
- }
159
-
160
- /**
161
- * Base64 decoding that does not need to be urldecode()-ed.
162
- *
163
- * Exactly the same as PHP base64 encode except it uses
164
- * `-` instead of `+`
165
- * `_` instead of `/`
166
- * No padded =
167
- *
168
- * @param string $input Base64UrlEncoded() string
169
- *
170
- * @return string
171
- */
172
- protected static function Base64UrlDecode( $input ) {
173
- /**
174
- * IMPORTANT NOTE:
175
- * This is a hack suggested by @otto42 and @greenshady from
176
- * the theme's review team. The usage of base64 for API
177
- * signature encoding was approved in a Slack meeting
178
- * held on Tue (10/25 2016).
179
- *
180
- * @todo Remove this hack once the base64 error is removed from the Theme Check.
181
- *
182
- * @since 1.2.2
183
- * @author Vova Feldman (@svovaf)
184
- */
185
- $fn = 'base64' . '_decode';
186
- return $fn( strtr( $input, '-_', '+/' ) );
187
- }
188
-
189
- /**
190
- * Base64 encoding that does not need to be urlencode()ed.
191
- *
192
- * Exactly the same as base64 encode except it uses
193
- * `-` instead of `+
194
- * `_` instead of `/`
195
- *
196
- * @param string $input string
197
- *
198
- * @return string Base64 encoded string
199
- */
200
- protected static function Base64UrlEncode( $input ) {
201
- /**
202
- * IMPORTANT NOTE:
203
- * This is a hack suggested by @otto42 and @greenshady from
204
- * the theme's review team. The usage of base64 for API
205
- * signature encoding was approved in a Slack meeting
206
- * held on Tue (10/25 2016).
207
- *
208
- * @todo Remove this hack once the base64 error is removed from the Theme Check.
209
- *
210
- * @since 1.2.2
211
- * @author Vova Feldman (@svovaf)
212
- */
213
- $fn = 'base64' . '_encode';
214
- $str = strtr( $fn( $input ), '+/', '-_' );
215
- $str = str_replace( '=', '', $str );
216
-
217
- return $str;
218
- }
219
- }
46
  require_once FS_SDK__EXCEPTIONS_PATH . $e . '.php';
47
  }
48
 
49
+ if ( ! class_exists( 'Freemius_Api_Base' ) ) {
50
+ abstract class Freemius_Api_Base {
51
+ const VERSION = '1.0.4';
52
+ const FORMAT = 'json';
53
+
54
+ protected $_id;
55
+ protected $_public;
56
+ protected $_secret;
57
+ protected $_scope;
58
+ protected $_isSandbox;
59
+
60
+ /**
61
+ * @param string $pScope 'app', 'developer', 'plugin', 'user' or 'install'.
62
+ * @param number $pID Element's id.
63
+ * @param string $pPublic Public key.
64
+ * @param string $pSecret Element's secret key.
65
+ * @param bool $pIsSandbox Whether or not to run API in sandbox mode.
66
+ */
67
+ public function Init( $pScope, $pID, $pPublic, $pSecret, $pIsSandbox = false ) {
68
+ $this->_id = $pID;
69
+ $this->_public = $pPublic;
70
+ $this->_secret = $pSecret;
71
+ $this->_scope = $pScope;
72
+ $this->_isSandbox = $pIsSandbox;
73
+ }
74
+
75
+ public function IsSandbox() {
76
+ return $this->_isSandbox;
77
+ }
78
+
79
+ function CanonizePath( $pPath ) {
80
+ $pPath = trim( $pPath, '/' );
81
+ $query_pos = strpos( $pPath, '?' );
82
+ $query = '';
83
+
84
+ if ( false !== $query_pos ) {
85
+ $query = substr( $pPath, $query_pos );
86
+ $pPath = substr( $pPath, 0, $query_pos );
87
+ }
88
+
89
+ // Trim '.json' suffix.
90
+ $format_length = strlen( '.' . self::FORMAT );
91
+ $start = $format_length * ( - 1 ); //negative
92
+ if ( substr( strtolower( $pPath ), $start ) === ( '.' . self::FORMAT ) ) {
93
+ $pPath = substr( $pPath, 0, strlen( $pPath ) - $format_length );
94
+ }
95
+
96
+ switch ( $this->_scope ) {
97
+ case 'app':
98
+ $base = '/apps/' . $this->_id;
99
+ break;
100
+ case 'developer':
101
+ $base = '/developers/' . $this->_id;
102
+ break;
103
+ case 'user':
104
+ $base = '/users/' . $this->_id;
105
+ break;
106
+ case 'plugin':
107
+ $base = '/plugins/' . $this->_id;
108
+ break;
109
+ case 'install':
110
+ $base = '/installs/' . $this->_id;
111
+ break;
112
+ default:
113
+ throw new Freemius_Exception( 'Scope not implemented.' );
114
+ }
115
+
116
+ return '/v' . FS_API__VERSION . $base .
117
+ ( ! empty( $pPath ) ? '/' : '' ) . $pPath .
118
+ ( ( false === strpos( $pPath, '.' ) ) ? '.' . self::FORMAT : '' ) . $query;
119
+ }
120
+
121
+ abstract function MakeRequest( $pCanonizedPath, $pMethod = 'GET', $pParams = array() );
122
+
123
+ /**
124
+ * @param string $pPath
125
+ * @param string $pMethod
126
+ * @param array $pParams
127
+ *
128
+ * @return object[]|object|null
129
+ */
130
+ private function _Api( $pPath, $pMethod = 'GET', $pParams = array() ) {
131
+ $pMethod = strtoupper( $pMethod );
132
+
133
+ try {
134
+ $result = $this->MakeRequest( $pPath, $pMethod, $pParams );
135
+ } catch ( Freemius_Exception $e ) {
136
+ // Map to error object.
137
+ $result = (object) $e->getResult();
138
+ } catch ( Exception $e ) {
139
+ // Map to error object.
140
+ $result = (object) array(
141
+ 'error' => (object) array(
142
+ 'type' => 'Unknown',
143
+ 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')',
144
+ 'code' => 'unknown',
145
+ 'http' => 402
146
+ )
147
+ );
148
+ }
149
+
150
+ return $result;
151
+ }
152
+
153
+ public function Api( $pPath, $pMethod = 'GET', $pParams = array() ) {
154
+ return $this->_Api( $this->CanonizePath( $pPath ), $pMethod, $pParams );
155
+ }
156
+
157
+ /**
158
+ * Base64 decoding that does not need to be urldecode()-ed.
159
+ *
160
+ * Exactly the same as PHP base64 encode except it uses
161
+ * `-` instead of `+`
162
+ * `_` instead of `/`
163
+ * No padded =
164
+ *
165
+ * @param string $input Base64UrlEncoded() string
166
+ *
167
+ * @return string
168
+ */
169
+ protected static function Base64UrlDecode( $input ) {
170
+ /**
171
+ * IMPORTANT NOTE:
172
+ * This is a hack suggested by @otto42 and @greenshady from
173
+ * the theme's review team. The usage of base64 for API
174
+ * signature encoding was approved in a Slack meeting
175
+ * held on Tue (10/25 2016).
176
+ *
177
+ * @todo Remove this hack once the base64 error is removed from the Theme Check.
178
+ *
179
+ * @since 1.2.2
180
+ * @author Vova Feldman (@svovaf)
181
+ */
182
+ $fn = 'base64' . '_decode';
183
+ return $fn( strtr( $input, '-_', '+/' ) );
184
+ }
185
+
186
+ /**
187
+ * Base64 encoding that does not need to be urlencode()ed.
188
+ *
189
+ * Exactly the same as base64 encode except it uses
190
+ * `-` instead of `+
191
+ * `_` instead of `/`
192
+ *
193
+ * @param string $input string
194
+ *
195
+ * @return string Base64 encoded string
196
+ */
197
+ protected static function Base64UrlEncode( $input ) {
198
+ /**
199
+ * IMPORTANT NOTE:
200
+ * This is a hack suggested by @otto42 and @greenshady from
201
+ * the theme's review team. The usage of base64 for API
202
+ * signature encoding was approved in a Slack meeting
203
+ * held on Tue (10/25 2016).
204
+ *
205
+ * @todo Remove this hack once the base64 error is removed from the Theme Check.
206
+ *
207
+ * @since 1.2.2
208
+ * @author Vova Feldman (@svovaf)
209
+ */
210
+ $fn = 'base64' . '_encode';
211
+ $str = strtr( $fn( $input ), '+/', '-_' );
212
+ $str = str_replace( '=', '', $str );
213
+
214
+ return $str;
215
+ }
216
+ }
217
+ }
 
 
freemius/includes/sdk/FreemiusWordPress.php CHANGED
@@ -85,10 +85,7 @@
85
  define( 'FS_API__SANDBOX_ADDRESS', '://sandbox-api.freemius.com' );
86
  }
87
 
88
- if ( class_exists( 'Freemius_Api_WordPress' ) ) {
89
- return;
90
- }
91
-
92
  class Freemius_Api_WordPress extends Freemius_Api_Base {
93
  private static $_logger = array();
94
 
@@ -305,6 +302,8 @@
305
  * @return mixed
306
  */
307
  private static function ExecuteRequest( $pUrl, &$pWPRemoteArgs ) {
 
 
308
  $start = microtime( true );
309
 
310
  $response = wp_remote_request( $pUrl, $pWPRemoteArgs );
@@ -327,7 +326,7 @@
327
  $response['body'] :
328
  json_encode( $response->get_error_messages() ),
329
  'code' => ! $is_http_error ? $response['response']['code'] : null,
330
- 'backtrace' => debug_backtrace(),
331
  );
332
  }
333
 
@@ -713,3 +712,4 @@
713
 
714
  #endregion
715
  }
 
85
  define( 'FS_API__SANDBOX_ADDRESS', '://sandbox-api.freemius.com' );
86
  }
87
 
88
+ if ( ! class_exists( 'Freemius_Api_WordPress' ) ) {
 
 
 
89
  class Freemius_Api_WordPress extends Freemius_Api_Base {
90
  private static $_logger = array();
91
 
302
  * @return mixed
303
  */
304
  private static function ExecuteRequest( $pUrl, &$pWPRemoteArgs ) {
305
+ $bt = debug_backtrace();
306
+
307
  $start = microtime( true );
308
 
309
  $response = wp_remote_request( $pUrl, $pWPRemoteArgs );
326
  $response['body'] :
327
  json_encode( $response->get_error_messages() ),
328
  'code' => ! $is_http_error ? $response['response']['code'] : null,
329
+ 'backtrace' => $bt,
330
  );
331
  }
332
 
712
 
713
  #endregion
714
  }
715
+ }
freemius/includes/supplements/fs-migration-2.5.1.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.1
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ if ( ! function_exists( 'fs_migrate_251' ) ) {
14
+ function fs_migrate_251( Freemius $fs, $install_by_blog_id ) {
15
+ $permission_manager = FS_Permission_Manager::instance( $fs );
16
+
17
+ /**
18
+ * @var FS_Site $install
19
+ */
20
+ foreach ( $install_by_blog_id as $blog_id => $install ) {
21
+ if ( true === $install->is_disconnected ) {
22
+ $permission_manager->update_site_tracking(
23
+ false,
24
+ ( 0 == $blog_id ) ? null : $blog_id,
25
+ // Update only if permissions are not yet set.
26
+ true
27
+ );
28
+ }
29
+ }
30
+ }
31
+ }
freemius/languages/freemius-cs_CZ.mo CHANGED
Binary file
freemius/languages/freemius-da_DK.mo CHANGED
Binary file
freemius/languages/freemius-de_DE.mo ADDED
Binary file
freemius/languages/freemius-en.mo CHANGED
Binary file
freemius/languages/freemius-es_ES.mo CHANGED
Binary file
freemius/languages/freemius-fr_FR.mo CHANGED
Binary file
freemius/languages/freemius-he_IL.mo CHANGED
Binary file
freemius/languages/freemius-hu_HU.mo CHANGED
Binary file
freemius/languages/freemius-it_IT.mo CHANGED
Binary file
freemius/languages/freemius-ja.mo CHANGED
Binary file
freemius/languages/freemius-nl_NL.mo CHANGED
Binary file
freemius/languages/freemius-ru_RU.mo CHANGED
Binary file
freemius/languages/freemius-ta.mo CHANGED
Binary file
freemius/languages/freemius-zh_CN.mo CHANGED
Binary file
freemius/languages/freemius.pot CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2021 freemius
2
  # This file is distributed under the same license as the freemius package.
3
  msgid ""
4
  msgstr ""
@@ -16,795 +16,807 @@ msgstr ""
16
  "X-Poedit-SourceCharset: UTF-8\n"
17
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18
 
19
- #: includes/class-freemius.php:1919, templates/account.php:912
20
  msgid "An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned."
21
  msgstr ""
22
 
23
- #: includes/class-freemius.php:1926
24
  msgid "Would you like to proceed with the update?"
25
  msgstr ""
26
 
27
- #: includes/class-freemius.php:2138
28
  msgid "Freemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error."
29
  msgstr ""
30
 
31
- #: includes/class-freemius.php:2140
32
  msgid "Error"
33
  msgstr ""
34
 
35
- #: includes/class-freemius.php:2540
36
  msgid "I found a better %s"
37
  msgstr ""
38
 
39
- #: includes/class-freemius.php:2542
40
  msgid "What's the %s's name?"
41
  msgstr ""
42
 
43
- #: includes/class-freemius.php:2548
44
- msgid "It's a temporary %s. I'm just debugging an issue."
45
  msgstr ""
46
 
47
- #: includes/class-freemius.php:2550
48
  msgid "Deactivation"
49
  msgstr ""
50
 
51
- #: includes/class-freemius.php:2551
52
  msgid "Theme Switch"
53
  msgstr ""
54
 
55
- #: includes/class-freemius.php:2560, templates/forms/resend-key.php:24, templates/forms/user-change.php:29
56
  msgid "Other"
57
  msgstr ""
58
 
59
- #: includes/class-freemius.php:2568
60
  msgid "I no longer need the %s"
61
  msgstr ""
62
 
63
- #: includes/class-freemius.php:2575
64
  msgid "I only needed the %s for a short period"
65
  msgstr ""
66
 
67
- #: includes/class-freemius.php:2581
68
  msgid "The %s broke my site"
69
  msgstr ""
70
 
71
- #: includes/class-freemius.php:2588
72
  msgid "The %s suddenly stopped working"
73
  msgstr ""
74
 
75
- #: includes/class-freemius.php:2598
76
  msgid "I can't pay for it anymore"
77
  msgstr ""
78
 
79
- #: includes/class-freemius.php:2600
80
  msgid "What price would you feel comfortable paying?"
81
  msgstr ""
82
 
83
- #: includes/class-freemius.php:2606
84
  msgid "I don't like to share my information with you"
85
  msgstr ""
86
 
87
- #: includes/class-freemius.php:2627
88
  msgid "The %s didn't work"
89
  msgstr ""
90
 
91
- #: includes/class-freemius.php:2637
92
  msgid "I couldn't understand how to make it work"
93
  msgstr ""
94
 
95
- #: includes/class-freemius.php:2645
96
  msgid "The %s is great, but I need specific feature that you don't support"
97
  msgstr ""
98
 
99
- #: includes/class-freemius.php:2647
100
  msgid "What feature?"
101
  msgstr ""
102
 
103
- #: includes/class-freemius.php:2651
104
  msgid "The %s is not working"
105
  msgstr ""
106
 
107
- #: includes/class-freemius.php:2653
108
  msgid "Kindly share what didn't work so we can fix it for future users..."
109
  msgstr ""
110
 
111
- #: includes/class-freemius.php:2657
112
  msgid "It's not what I was looking for"
113
  msgstr ""
114
 
115
- #: includes/class-freemius.php:2659
116
  msgid "What you've been looking for?"
117
  msgstr ""
118
 
119
- #: includes/class-freemius.php:2663
120
  msgid "The %s didn't work as expected"
121
  msgstr ""
122
 
123
- #: includes/class-freemius.php:2665
124
  msgid "What did you expect?"
125
  msgstr ""
126
 
127
- #: includes/class-freemius.php:3520, templates/debug.php:20
128
  msgid "Freemius Debug"
129
  msgstr ""
130
 
131
- #: includes/class-freemius.php:4272
132
  msgid "I don't know what is cURL or how to install it, help me!"
133
  msgstr ""
134
 
135
- #: includes/class-freemius.php:4274
136
  msgid "We'll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update."
137
  msgstr ""
138
 
139
- #: includes/class-freemius.php:4281
140
  msgid "Great, please install cURL and enable it in your php.ini file. In addition, search for the 'disable_functions' directive in your php.ini file and remove any disabled methods starting with 'curl_'. To make sure it was successfully activated, use 'phpinfo()'. Once activated, deactivate the %s and reactivate it back again."
141
  msgstr ""
142
 
143
- #: includes/class-freemius.php:4386
144
  msgid "Yes - do your thing"
145
  msgstr ""
146
 
147
- #: includes/class-freemius.php:4391
148
  msgid "No - just deactivate"
149
  msgstr ""
150
 
151
- #: includes/class-freemius.php:4436, includes/class-freemius.php:4930, includes/class-freemius.php:6191, includes/class-freemius.php:13368, includes/class-freemius.php:14110, includes/class-freemius.php:17542, includes/class-freemius.php:17647, includes/class-freemius.php:17822, includes/class-freemius.php:20056, includes/class-freemius.php:20414, includes/class-freemius.php:20424, includes/class-freemius.php:21109, includes/class-freemius.php:22015, includes/class-freemius.php:22148, includes/class-freemius.php:22304, templates/add-ons.php:57
152
  msgctxt "exclamation"
153
  msgid "Oops"
154
  msgstr ""
155
 
156
- #: includes/class-freemius.php:4505
157
  msgid "Thank for giving us the chance to fix it! A message was just sent to our technical staff. We will get back to you as soon as we have an update to %s. Appreciate your patience."
158
  msgstr ""
159
 
160
- #: includes/class-freemius.php:4927
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  msgctxt "addonX cannot run without pluginY"
162
  msgid "%s cannot run without %s."
163
  msgstr ""
164
 
165
- #: includes/class-freemius.php:4928
166
  msgctxt "addonX cannot run..."
167
  msgid "%s cannot run without the plugin."
168
  msgstr ""
169
 
170
- #: includes/class-freemius.php:5127, includes/class-freemius.php:5152, includes/class-freemius.php:21180
171
- msgid "Unexpected API error. Please contact the %s's author with the following error."
172
  msgstr ""
173
 
174
- #: includes/class-freemius.php:5857
175
  msgid "Premium %s version was successfully activated."
176
  msgstr ""
177
 
178
- #: includes/class-freemius.php:5869, includes/class-freemius.php:7774
179
  msgctxt "Used to express elation, enthusiasm, or triumph (especially in electronic communication)."
180
  msgid "W00t"
181
  msgstr ""
182
 
183
- #: includes/class-freemius.php:5884
184
  msgid "You have a %s license."
185
  msgstr ""
186
 
187
- #: includes/class-freemius.php:5888, includes/class-freemius.php:16947, includes/class-freemius.php:16958, includes/class-freemius.php:20325, includes/class-freemius.php:20689, includes/class-freemius.php:20758, includes/class-freemius.php:20930
188
- msgctxt "interjection expressing joy or exuberance"
189
- msgid "Yee-haw"
190
- msgstr ""
191
-
192
- #: includes/class-freemius.php:6174
193
  msgid "%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license."
194
  msgstr ""
195
 
196
- #: includes/class-freemius.php:6178
197
  msgid "%s is a premium only add-on. You have to purchase a license first before activating the plugin."
198
  msgstr ""
199
 
200
- #: includes/class-freemius.php:6187, templates/add-ons.php:186, templates/account/partials/addon.php:381
201
  msgid "More information about %s"
202
  msgstr ""
203
 
204
- #: includes/class-freemius.php:6188
205
  msgid "Purchase License"
206
  msgstr ""
207
 
208
- #: includes/class-freemius.php:7125, templates/connect.php:171
209
- msgid "You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s."
210
  msgstr ""
211
 
212
- #: includes/class-freemius.php:7129
213
  msgid "start the trial"
214
  msgstr ""
215
 
216
- #: includes/class-freemius.php:7130, templates/connect.php:175
217
- msgid "complete the install"
218
  msgstr ""
219
 
220
- #: includes/class-freemius.php:7249
221
  msgid "You are just one step away - %s"
222
  msgstr ""
223
 
224
- #: includes/class-freemius.php:7252
225
  msgctxt "%s - plugin name. As complete \"PluginX\" activation now"
226
  msgid "Complete \"%s\" Activation Now"
227
  msgstr ""
228
 
229
- #: includes/class-freemius.php:7334
230
  msgid "We made a few tweaks to the %s, %s"
231
  msgstr ""
232
 
233
- #: includes/class-freemius.php:7338
234
  msgid "Opt in to make \"%s\" better!"
235
  msgstr ""
236
 
237
- #: includes/class-freemius.php:7773
238
  msgid "The upgrade of %s was successfully completed."
239
  msgstr ""
240
 
241
- #: includes/class-freemius.php:10255, includes/class-fs-plugin-updater.php:1087, includes/class-fs-plugin-updater.php:1282, includes/class-fs-plugin-updater.php:1289, templates/auto-installation.php:32
242
  msgid "Add-On"
243
  msgstr ""
244
 
245
- #: includes/class-freemius.php:10257, templates/account.php:394, templates/account.php:402, templates/debug.php:358, templates/debug.php:549
246
  msgid "Plugin"
247
  msgstr ""
248
 
249
- #: includes/class-freemius.php:10258, templates/account.php:395, templates/account.php:403, templates/debug.php:358, templates/debug.php:549, templates/forms/deactivation/form.php:71
250
  msgid "Theme"
251
  msgstr ""
252
 
253
- #: includes/class-freemius.php:13188
254
  msgid "An unknown error has occurred while trying to toggle the license's white-label mode."
255
  msgstr ""
256
 
257
- #: includes/class-freemius.php:13202
258
  msgid "Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s."
259
  msgstr ""
260
 
261
- #: includes/class-freemius.php:13207
262
  msgid "User Dashboard"
263
  msgstr ""
264
 
265
- #: includes/class-freemius.php:13208
266
  msgid "revert it now"
267
  msgstr ""
268
 
269
- #: includes/class-freemius.php:13266
270
  msgid "An unknown error has occurred while trying to set the user's beta mode."
271
  msgstr ""
272
 
273
- #: includes/class-freemius.php:13339
274
  msgid "Invalid new user ID or email address."
275
  msgstr ""
276
 
277
- #: includes/class-freemius.php:13369, includes/class-freemius.php:22259
278
  msgid "Sorry, we could not complete the email update. Another user with the same email is already registered."
279
  msgstr ""
280
 
281
- #: includes/class-freemius.php:13370, includes/class-freemius.php:22260
282
  msgid "If you would like to give up the ownership of the %s's account to %s click the Change Ownership button."
283
  msgstr ""
284
 
285
- #: includes/class-freemius.php:13377, includes/class-freemius.php:22267
286
  msgid "Change Ownership"
287
  msgstr ""
288
 
289
- #: includes/class-freemius.php:13977
290
  msgid "Invalid site details collection."
291
  msgstr ""
292
 
293
- #: includes/class-freemius.php:14097
294
  msgid "We couldn't find your email address in the system, are you sure it's the right address?"
295
  msgstr ""
296
 
297
- #: includes/class-freemius.php:14099
298
  msgid "We can't see any active licenses associated with that email address, are you sure it's the right address?"
299
  msgstr ""
300
 
301
- #: includes/class-freemius.php:14373
302
- msgid "Account is pending activation."
303
  msgstr ""
304
 
305
- #: includes/class-freemius.php:14485, templates/forms/premium-versions-upgrade-handler.php:47
306
  msgid "Buy a license now"
307
  msgstr ""
308
 
309
- #: includes/class-freemius.php:14497, templates/forms/premium-versions-upgrade-handler.php:46
310
  msgid "Renew your license now"
311
  msgstr ""
312
 
313
- #: includes/class-freemius.php:14501
314
  msgid "%s to access version %s security & feature updates, and support."
315
  msgstr ""
316
 
317
- #: includes/class-freemius.php:16929
318
- msgid "%s activation was successfully completed."
319
  msgstr ""
320
 
321
- #: includes/class-freemius.php:16943
322
  msgid "Your account was successfully activated with the %s plan."
323
  msgstr ""
324
 
325
- #: includes/class-freemius.php:16954, includes/class-freemius.php:20754
326
  msgid "Your trial has been successfully started."
327
  msgstr ""
328
 
329
- #: includes/class-freemius.php:17540, includes/class-freemius.php:17645, includes/class-freemius.php:17820
330
  msgid "Couldn't activate %s."
331
  msgstr ""
332
 
333
- #: includes/class-freemius.php:17541, includes/class-freemius.php:17646, includes/class-freemius.php:17821
334
  msgid "Please contact us with the following message:"
335
  msgstr ""
336
 
337
- #: includes/class-freemius.php:17642, templates/forms/data-debug-mode.php:162
338
  msgid "An unknown error has occurred."
339
  msgstr ""
340
 
341
- #: includes/class-freemius.php:18178, includes/class-freemius.php:23340
342
  msgid "Upgrade"
343
  msgstr ""
344
 
345
- #: includes/class-freemius.php:18184
346
  msgid "Start Trial"
347
  msgstr ""
348
 
349
- #: includes/class-freemius.php:18186
350
  msgid "Pricing"
351
  msgstr ""
352
 
353
- #: includes/class-freemius.php:18266, includes/class-freemius.php:18268
354
  msgid "Affiliation"
355
  msgstr ""
356
 
357
- #: includes/class-freemius.php:18296, includes/class-freemius.php:18298, templates/account.php:242, templates/debug.php:324
358
  msgid "Account"
359
  msgstr ""
360
 
361
- #: includes/class-freemius.php:18312, includes/class-freemius.php:18314, includes/customizer/class-fs-customizer-support-section.php:60
362
  msgid "Contact Us"
363
  msgstr ""
364
 
365
- #: includes/class-freemius.php:18325, includes/class-freemius.php:18327, includes/class-freemius.php:23354, templates/account.php:121, templates/account/partials/addon.php:44
366
  msgid "Add-Ons"
367
  msgstr ""
368
 
369
- #: includes/class-freemius.php:18361
370
  msgctxt "ASCII arrow left icon"
371
  msgid "&#x2190;"
372
  msgstr ""
373
 
374
- #: includes/class-freemius.php:18361
375
  msgctxt "ASCII arrow right icon"
376
  msgid "&#x27a4;"
377
  msgstr ""
378
 
379
- #: includes/class-freemius.php:18363, templates/pricing.php:109
380
  msgctxt "noun"
381
  msgid "Pricing"
382
  msgstr ""
383
 
384
- #: includes/class-freemius.php:18576, includes/customizer/class-fs-customizer-support-section.php:67
385
  msgid "Support Forum"
386
  msgstr ""
387
 
388
- #: includes/class-freemius.php:19550
389
  msgid "Your email has been successfully verified - you are AWESOME!"
390
  msgstr ""
391
 
392
- #: includes/class-freemius.php:19551
393
  msgctxt "a positive response"
394
  msgid "Right on"
395
  msgstr ""
396
 
397
- #: includes/class-freemius.php:20057
398
  msgid "seems like the key you entered doesn't match our records."
399
  msgstr ""
400
 
401
- #: includes/class-freemius.php:20081
402
  msgid "Debug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the \"Stop Debug\" link."
403
  msgstr ""
404
 
405
- #: includes/class-freemius.php:20316
406
  msgid "Your %s Add-on plan was successfully upgraded."
407
  msgstr ""
408
 
409
- #: includes/class-freemius.php:20318
410
  msgid "%s Add-on was successfully purchased."
411
  msgstr ""
412
 
413
- #: includes/class-freemius.php:20321
414
  msgid "Download the latest version"
415
  msgstr ""
416
 
417
- #: includes/class-freemius.php:20407
418
  msgid "Your server is blocking the access to Freemius' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s"
419
  msgstr ""
420
 
421
- #: includes/class-freemius.php:20413, includes/class-freemius.php:20423, includes/class-freemius.php:20889, includes/class-freemius.php:20978
422
  msgid "Error received from the server:"
423
  msgstr ""
424
 
425
- #: includes/class-freemius.php:20423
426
  msgid "It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again."
427
  msgstr ""
428
 
429
- #: includes/class-freemius.php:20651, includes/class-freemius.php:20894, includes/class-freemius.php:20949, includes/class-freemius.php:21056
430
  msgctxt "something somebody says when they are thinking about what you have just said."
431
  msgid "Hmm"
432
  msgstr ""
433
 
434
- #: includes/class-freemius.php:20664
435
  msgid "It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry."
436
  msgstr ""
437
 
438
- #: includes/class-freemius.php:20665, templates/account.php:123, templates/add-ons.php:250, templates/account/partials/addon.php:46
439
  msgctxt "trial period"
440
  msgid "Trial"
441
  msgstr ""
442
 
443
- #: includes/class-freemius.php:20670
444
  msgid "I have upgraded my account but when I try to Sync the License, the plan remains %s."
445
  msgstr ""
446
 
447
- #: includes/class-freemius.php:20674, includes/class-freemius.php:20733
448
  msgid "Please contact us here"
449
  msgstr ""
450
 
451
- #: includes/class-freemius.php:20685
452
  msgid "Your plan was successfully activated."
453
  msgstr ""
454
 
455
- #: includes/class-freemius.php:20686
456
  msgid "Your plan was successfully upgraded."
457
  msgstr ""
458
 
459
- #: includes/class-freemius.php:20703
460
  msgid "Your plan was successfully changed to %s."
461
  msgstr ""
462
 
463
- #: includes/class-freemius.php:20719
464
  msgid "Your license has expired. You can still continue using the free %s forever."
465
  msgstr ""
466
 
467
- #: includes/class-freemius.php:20721
468
  msgid "Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
469
  msgstr ""
470
 
471
- #: includes/class-freemius.php:20729
472
  msgid "Your license has been cancelled. If you think it's a mistake, please contact support."
473
  msgstr ""
474
 
475
- #: includes/class-freemius.php:20742
476
  msgid "Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support."
477
  msgstr ""
478
 
479
- #: includes/class-freemius.php:20768
480
  msgid "Your free trial has expired. You can still continue using all our free features."
481
  msgstr ""
482
 
483
- #: includes/class-freemius.php:20770
484
  msgid "Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
485
  msgstr ""
486
 
487
- #: includes/class-freemius.php:20885
488
  msgid "It looks like the license could not be activated."
489
  msgstr ""
490
 
491
- #: includes/class-freemius.php:20927
492
  msgid "Your license was successfully activated."
493
  msgstr ""
494
 
495
- #: includes/class-freemius.php:20953
496
  msgid "It looks like your site currently doesn't have an active license."
497
  msgstr ""
498
 
499
- #: includes/class-freemius.php:20977
500
  msgid "It looks like the license deactivation failed."
501
  msgstr ""
502
 
503
- #: includes/class-freemius.php:21006
504
  msgid "Your %s license was successfully deactivated."
505
  msgstr ""
506
 
507
- #: includes/class-freemius.php:21007
508
  msgid "Your license was successfully deactivated, you are back to the %s plan."
509
  msgstr ""
510
 
511
- #: includes/class-freemius.php:21010
512
  msgid "O.K"
513
  msgstr ""
514
 
515
- #: includes/class-freemius.php:21063
516
  msgid "Seems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes."
517
  msgstr ""
518
 
519
- #: includes/class-freemius.php:21072
520
  msgid "Your subscription was successfully cancelled. Your %s plan license will expire in %s."
521
  msgstr ""
522
 
523
- #: includes/class-freemius.php:21114
524
  msgid "You are already running the %s in a trial mode."
525
  msgstr ""
526
 
527
- #: includes/class-freemius.php:21125
528
  msgid "You already utilized a trial before."
529
  msgstr ""
530
 
531
- #: includes/class-freemius.php:21139
532
  msgid "Plan %s do not exist, therefore, can't start a trial."
533
  msgstr ""
534
 
535
- #: includes/class-freemius.php:21150
536
  msgid "Plan %s does not support a trial period."
537
  msgstr ""
538
 
539
- #: includes/class-freemius.php:21161
540
  msgid "None of the %s's plans supports a trial period."
541
  msgstr ""
542
 
543
- #: includes/class-freemius.php:21211
544
  msgid "It looks like you are not in trial mode anymore so there's nothing to cancel :)"
545
  msgstr ""
546
 
547
- #: includes/class-freemius.php:21247
548
  msgid "Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes."
549
  msgstr ""
550
 
551
- #: includes/class-freemius.php:21266
552
  msgid "Your %s free trial was successfully cancelled."
553
  msgstr ""
554
 
555
- #: includes/class-freemius.php:21582
556
  msgid "Version %s was released."
557
  msgstr ""
558
 
559
- #: includes/class-freemius.php:21582
560
  msgid "Please download %s."
561
  msgstr ""
562
 
563
- #: includes/class-freemius.php:21589
564
  msgid "the latest %s version here"
565
  msgstr ""
566
 
567
- #: includes/class-freemius.php:21594
568
  msgid "New"
569
  msgstr ""
570
 
571
- #: includes/class-freemius.php:21599
572
  msgid "Seems like you got the latest release."
573
  msgstr ""
574
 
575
- #: includes/class-freemius.php:21600
576
  msgid "You are all good!"
577
  msgstr ""
578
 
579
- #: includes/class-freemius.php:21903
580
  msgid "Verification mail was just sent to %s. If you can't find it after 5 min, please check your spam box."
581
  msgstr ""
582
 
583
- #: includes/class-freemius.php:22043
584
  msgid "Site successfully opted in."
585
  msgstr ""
586
 
587
- #: includes/class-freemius.php:22044, includes/class-freemius.php:23050
588
  msgid "Awesome"
589
  msgstr ""
590
 
591
- #: includes/class-freemius.php:22060, templates/forms/optout.php:41
592
- msgid "We appreciate your help in making the %s better by letting us track some usage data."
593
  msgstr ""
594
 
595
- #: includes/class-freemius.php:22061
596
  msgid "Thank you!"
597
  msgstr ""
598
 
599
- #: includes/class-freemius.php:22068
600
- msgid "We will no longer be sending any usage data of %s on %s to %s."
601
  msgstr ""
602
 
603
- #: includes/class-freemius.php:22226
604
- msgid "Please check your mailbox, you should receive an email via %s to confirm the ownership change. From security reasons, you must confirm the change within the next 15 min. If you cannot find the email, please check your spam folder."
605
  msgstr ""
606
 
607
- #: includes/class-freemius.php:22232
 
 
 
 
608
  msgid "Thanks for confirming the ownership change. An email was just sent to %s for final approval."
609
  msgstr ""
610
 
611
- #: includes/class-freemius.php:22237
612
  msgid "%s is the new owner of the account."
613
  msgstr ""
614
 
615
- #: includes/class-freemius.php:22239
616
  msgctxt "as congratulations"
617
  msgid "Congrats"
618
  msgstr ""
619
 
620
- #: includes/class-freemius.php:22275
621
- msgid "Your email was successfully updated. You should receive an email with confirmation instructions in few moments."
622
- msgstr ""
623
-
624
- #: includes/class-freemius.php:22287
625
  msgid "Please provide your full name."
626
  msgstr ""
627
 
628
- #: includes/class-freemius.php:22292
629
  msgid "Your name was successfully updated."
630
  msgstr ""
631
 
632
- #: includes/class-freemius.php:22353
633
  msgid "You have successfully updated your %s."
634
  msgstr ""
635
 
636
- #: includes/class-freemius.php:22412
637
  msgid "Is this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin."
638
  msgstr ""
639
 
640
- #: includes/class-freemius.php:22415
641
  msgid "Click here"
642
  msgstr ""
643
 
644
- #: includes/class-freemius.php:22513
 
 
 
 
645
  msgid "Just letting you know that the add-ons information of %s is being pulled from an external server."
646
  msgstr ""
647
 
648
- #: includes/class-freemius.php:22514
649
  msgctxt "advance notice of something that will need attention."
650
  msgid "Heads up"
651
  msgstr ""
652
 
653
- #: includes/class-freemius.php:23090
654
  msgctxt "exclamation"
655
  msgid "Hey"
656
  msgstr ""
657
 
658
- #: includes/class-freemius.php:23090
659
  msgid "How do you like %s so far? Test all our %s premium features with a %d-day free trial."
660
  msgstr ""
661
 
662
- #: includes/class-freemius.php:23098
663
  msgid "No commitment for %s days - cancel anytime!"
664
  msgstr ""
665
 
666
- #: includes/class-freemius.php:23099
667
  msgid "No credit card required"
668
  msgstr ""
669
 
670
- #: includes/class-freemius.php:23106, templates/forms/trial-start.php:53
671
  msgctxt "call to action"
672
  msgid "Start free trial"
673
  msgstr ""
674
 
675
- #: includes/class-freemius.php:23183
676
  msgid "Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!"
677
  msgstr ""
678
 
679
- #: includes/class-freemius.php:23192
680
  msgid "Learn more"
681
  msgstr ""
682
 
683
- #: includes/class-freemius.php:23378, templates/account.php:558, templates/account.php:708, templates/connect.php:179, templates/connect.php:461, templates/forms/license-activation.php:27, templates/account/partials/addon.php:321
684
  msgid "Activate License"
685
  msgstr ""
686
 
687
- #: includes/class-freemius.php:23379, templates/account.php:652, templates/account.php:707, templates/account/partials/addon.php:322, templates/account/partials/site.php:271
688
  msgid "Change License"
689
  msgstr ""
690
 
691
- #: includes/class-freemius.php:23500, templates/account/partials/site.php:169
692
  msgid "Opt Out"
693
  msgstr ""
694
 
695
- #: includes/class-freemius.php:23502, includes/class-freemius.php:23508, templates/account/partials/site.php:49, templates/account/partials/site.php:169
696
  msgid "Opt In"
697
  msgstr ""
698
 
699
- #: includes/class-freemius.php:23738
700
  msgid " The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s"
701
  msgstr ""
702
 
703
- #: includes/class-freemius.php:23746
704
  msgid "Activate %s features"
705
  msgstr ""
706
 
707
- #: includes/class-freemius.php:23759
708
  msgid "Please follow these steps to complete the upgrade"
709
  msgstr ""
710
 
711
- #: includes/class-freemius.php:23763
712
  msgid "Download the latest %s version"
713
  msgstr ""
714
 
715
- #: includes/class-freemius.php:23767
716
  msgid "Upload and activate the downloaded version"
717
  msgstr ""
718
 
719
- #: includes/class-freemius.php:23769
720
  msgid "How to upload and activate?"
721
  msgstr ""
722
 
723
- #: includes/class-freemius.php:23903
724
  msgid "%sClick here%s to choose the sites where you'd like to activate the license on."
725
  msgstr ""
726
 
727
- #: includes/class-freemius.php:24072
728
  msgid "Auto installation only works for opted-in users."
729
  msgstr ""
730
 
731
- #: includes/class-freemius.php:24082, includes/class-freemius.php:24115, includes/class-fs-plugin-updater.php:1261, includes/class-fs-plugin-updater.php:1275
732
  msgid "Invalid module ID."
733
  msgstr ""
734
 
735
- #: includes/class-freemius.php:24091, includes/class-fs-plugin-updater.php:1297
736
  msgid "Premium version already active."
737
  msgstr ""
738
 
739
- #: includes/class-freemius.php:24098
740
  msgid "You do not have a valid license to access the premium version."
741
  msgstr ""
742
 
743
- #: includes/class-freemius.php:24105
744
  msgid "Plugin is a \"Serviceware\" which means it does not have a premium code version."
745
  msgstr ""
746
 
747
- #: includes/class-freemius.php:24123, includes/class-fs-plugin-updater.php:1296
748
  msgid "Premium add-on version already installed."
749
  msgstr ""
750
 
751
- #: includes/class-freemius.php:24473
752
  msgid "View paid features"
753
  msgstr ""
754
 
755
- #: includes/class-freemius.php:24795
756
  msgid "Thank you so much for using %s and its add-ons!"
757
  msgstr ""
758
 
759
- #: includes/class-freemius.php:24796
760
  msgid "Thank you so much for using %s!"
761
  msgstr ""
762
 
763
- #: includes/class-freemius.php:24802
764
  msgid "You've already opted-in to our usage-tracking, which helps us keep improving the %s."
765
  msgstr ""
766
 
767
- #: includes/class-freemius.php:24806
768
  msgid "Thank you so much for using our products!"
769
  msgstr ""
770
 
771
- #: includes/class-freemius.php:24807
772
  msgid "You've already opted-in to our usage-tracking, which helps us keep improving them."
773
  msgstr ""
774
 
775
- #: includes/class-freemius.php:24826
776
  msgid "%s and its add-ons"
777
  msgstr ""
778
 
779
- #: includes/class-freemius.php:24835
780
  msgid "Products"
781
  msgstr ""
782
 
783
- #: includes/class-freemius.php:24842, templates/connect.php:275
784
  msgid "Yes"
785
  msgstr ""
786
 
787
- #: includes/class-freemius.php:24843, templates/connect.php:276
788
  msgid "send me security & feature updates, educational content and offers."
789
  msgstr ""
790
 
791
- #: includes/class-freemius.php:24844, templates/connect.php:281
792
  msgid "No"
793
  msgstr ""
794
 
795
- #: includes/class-freemius.php:24846, templates/connect.php:283
796
  msgid "do %sNOT%s send me security & feature updates, educational content and offers."
797
  msgstr ""
798
 
799
- #: includes/class-freemius.php:24856
800
  msgid "Due to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)"
801
  msgstr ""
802
 
803
- #: includes/class-freemius.php:24858, templates/connect.php:290
804
  msgid "Please let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:"
805
  msgstr ""
806
 
807
- #: includes/class-freemius.php:25140
808
  msgid "License key is empty."
809
  msgstr ""
810
 
@@ -816,620 +828,620 @@ msgstr ""
816
  msgid "Buy license"
817
  msgstr ""
818
 
819
- #: includes/class-fs-plugin-updater.php:327, includes/class-fs-plugin-updater.php:360
820
  msgid "There is a %s of %s available."
821
  msgstr ""
822
 
823
- #: includes/class-fs-plugin-updater.php:329, includes/class-fs-plugin-updater.php:365
824
  msgid "new Beta version"
825
  msgstr ""
826
 
827
- #: includes/class-fs-plugin-updater.php:330, includes/class-fs-plugin-updater.php:366
828
  msgid "new version"
829
  msgstr ""
830
 
831
- #: includes/class-fs-plugin-updater.php:389
832
  msgid "Important Upgrade Notice:"
833
  msgstr ""
834
 
835
- #: includes/class-fs-plugin-updater.php:1326
836
  msgid "Installing plugin: %s"
837
  msgstr ""
838
 
839
- #: includes/class-fs-plugin-updater.php:1367
840
  msgid "Unable to connect to the filesystem. Please confirm your credentials."
841
  msgstr ""
842
 
843
- #: includes/class-fs-plugin-updater.php:1549
844
  msgid "The remote plugin package does not contain a folder with the desired slug and renaming did not work."
845
  msgstr ""
846
 
847
- #: includes/fs-plugin-info-dialog.php:541
848
  msgid "Purchase More"
849
  msgstr ""
850
 
851
- #: includes/fs-plugin-info-dialog.php:542, templates/account/partials/addon.php:385
852
  msgctxt "verb"
853
  msgid "Purchase"
854
  msgstr ""
855
 
856
- #: includes/fs-plugin-info-dialog.php:546
857
  msgid "Start my free %s"
858
  msgstr ""
859
 
860
- #: includes/fs-plugin-info-dialog.php:744
861
  msgid "Install Free Version Update Now"
862
  msgstr ""
863
 
864
- #: includes/fs-plugin-info-dialog.php:745, templates/account.php:641
865
  msgid "Install Update Now"
866
  msgstr ""
867
 
868
- #: includes/fs-plugin-info-dialog.php:754
869
  msgid "Install Free Version Now"
870
  msgstr ""
871
 
872
- #: includes/fs-plugin-info-dialog.php:755, templates/add-ons.php:323, templates/auto-installation.php:111, templates/account/partials/addon.php:365, templates/account/partials/addon.php:418
873
  msgid "Install Now"
874
  msgstr ""
875
 
876
- #: includes/fs-plugin-info-dialog.php:771
877
  msgctxt "as download latest version"
878
  msgid "Download Latest Free Version"
879
  msgstr ""
880
 
881
- #: includes/fs-plugin-info-dialog.php:772, templates/account.php:101, templates/add-ons.php:37, templates/account/partials/addon.php:25
882
  msgctxt "as download latest version"
883
  msgid "Download Latest"
884
  msgstr ""
885
 
886
- #: includes/fs-plugin-info-dialog.php:787, templates/add-ons.php:329, templates/account/partials/addon.php:356, templates/account/partials/addon.php:412
887
  msgid "Activate this add-on"
888
  msgstr ""
889
 
890
- #: includes/fs-plugin-info-dialog.php:789, templates/connect.php:458
891
  msgid "Activate Free Version"
892
  msgstr ""
893
 
894
- #: includes/fs-plugin-info-dialog.php:790, templates/account.php:125, templates/add-ons.php:330, templates/account/partials/addon.php:48
895
  msgid "Activate"
896
  msgstr ""
897
 
898
- #: includes/fs-plugin-info-dialog.php:1002
899
  msgctxt "Plugin installer section title"
900
  msgid "Description"
901
  msgstr ""
902
 
903
- #: includes/fs-plugin-info-dialog.php:1003
904
  msgctxt "Plugin installer section title"
905
  msgid "Installation"
906
  msgstr ""
907
 
908
- #: includes/fs-plugin-info-dialog.php:1004
909
  msgctxt "Plugin installer section title"
910
  msgid "FAQ"
911
  msgstr ""
912
 
913
- #: includes/fs-plugin-info-dialog.php:1005, templates/plugin-info/description.php:55
914
  msgid "Screenshots"
915
  msgstr ""
916
 
917
- #: includes/fs-plugin-info-dialog.php:1006
918
  msgctxt "Plugin installer section title"
919
  msgid "Changelog"
920
  msgstr ""
921
 
922
- #: includes/fs-plugin-info-dialog.php:1007
923
  msgctxt "Plugin installer section title"
924
  msgid "Reviews"
925
  msgstr ""
926
 
927
- #: includes/fs-plugin-info-dialog.php:1008
928
  msgctxt "Plugin installer section title"
929
  msgid "Other Notes"
930
  msgstr ""
931
 
932
- #: includes/fs-plugin-info-dialog.php:1023
933
  msgctxt "Plugin installer section title"
934
  msgid "Features & Pricing"
935
  msgstr ""
936
 
937
- #: includes/fs-plugin-info-dialog.php:1033
938
  msgid "Plugin Install"
939
  msgstr ""
940
 
941
- #: includes/fs-plugin-info-dialog.php:1105
942
  msgctxt "e.g. Professional Plan"
943
  msgid "%s Plan"
944
  msgstr ""
945
 
946
- #: includes/fs-plugin-info-dialog.php:1131
947
  msgctxt "e.g. the best product"
948
  msgid "Best"
949
  msgstr ""
950
 
951
- #: includes/fs-plugin-info-dialog.php:1137, includes/fs-plugin-info-dialog.php:1157
952
  msgctxt "as every month"
953
  msgid "Monthly"
954
  msgstr ""
955
 
956
- #: includes/fs-plugin-info-dialog.php:1140
957
  msgctxt "as once a year"
958
  msgid "Annual"
959
  msgstr ""
960
 
961
- #: includes/fs-plugin-info-dialog.php:1143
962
  msgid "Lifetime"
963
  msgstr ""
964
 
965
- #: includes/fs-plugin-info-dialog.php:1157, includes/fs-plugin-info-dialog.php:1159, includes/fs-plugin-info-dialog.php:1161
966
  msgctxt "e.g. billed monthly"
967
  msgid "Billed %s"
968
  msgstr ""
969
 
970
- #: includes/fs-plugin-info-dialog.php:1159
971
  msgctxt "as once a year"
972
  msgid "Annually"
973
  msgstr ""
974
 
975
- #: includes/fs-plugin-info-dialog.php:1161
976
  msgctxt "as once a year"
977
  msgid "Once"
978
  msgstr ""
979
 
980
- #: includes/fs-plugin-info-dialog.php:1167
981
  msgid "Single Site License"
982
  msgstr ""
983
 
984
- #: includes/fs-plugin-info-dialog.php:1169
985
  msgid "Unlimited Licenses"
986
  msgstr ""
987
 
988
- #: includes/fs-plugin-info-dialog.php:1171
989
  msgid "Up to %s Sites"
990
  msgstr ""
991
 
992
- #: includes/fs-plugin-info-dialog.php:1181, templates/plugin-info/features.php:82
993
  msgctxt "as monthly period"
994
  msgid "mo"
995
  msgstr ""
996
 
997
- #: includes/fs-plugin-info-dialog.php:1188, templates/plugin-info/features.php:80
998
  msgctxt "as annual period"
999
  msgid "year"
1000
  msgstr ""
1001
 
1002
- #: includes/fs-plugin-info-dialog.php:1242
1003
  msgctxt "noun"
1004
  msgid "Price"
1005
  msgstr ""
1006
 
1007
- #: includes/fs-plugin-info-dialog.php:1290
1008
  msgid "Save %s"
1009
  msgstr ""
1010
 
1011
- #: includes/fs-plugin-info-dialog.php:1300
1012
  msgid "No commitment for %s - cancel anytime"
1013
  msgstr ""
1014
 
1015
- #: includes/fs-plugin-info-dialog.php:1303
1016
  msgid "After your free %s, pay as little as %s"
1017
  msgstr ""
1018
 
1019
- #: includes/fs-plugin-info-dialog.php:1314
1020
  msgid "Details"
1021
  msgstr ""
1022
 
1023
- #: includes/fs-plugin-info-dialog.php:1318, templates/account.php:112, templates/debug.php:201, templates/debug.php:238, templates/debug.php:455, templates/account/partials/addon.php:36
1024
  msgctxt "product version"
1025
  msgid "Version"
1026
  msgstr ""
1027
 
1028
- #: includes/fs-plugin-info-dialog.php:1325
1029
  msgctxt "as the plugin author"
1030
  msgid "Author"
1031
  msgstr ""
1032
 
1033
- #: includes/fs-plugin-info-dialog.php:1332
1034
  msgid "Last Updated"
1035
  msgstr ""
1036
 
1037
- #: includes/fs-plugin-info-dialog.php:1337, templates/account.php:527
1038
  msgctxt "x-ago"
1039
  msgid "%s ago"
1040
  msgstr ""
1041
 
1042
- #: includes/fs-plugin-info-dialog.php:1346
1043
  msgid "Requires WordPress Version"
1044
  msgstr ""
1045
 
1046
- #: includes/fs-plugin-info-dialog.php:1347
1047
  msgid "%s or higher"
1048
  msgstr ""
1049
 
1050
- #: includes/fs-plugin-info-dialog.php:1354
1051
  msgid "Compatible up to"
1052
  msgstr ""
1053
 
1054
- #: includes/fs-plugin-info-dialog.php:1362
 
 
 
 
1055
  msgid "Downloaded"
1056
  msgstr ""
1057
 
1058
- #: includes/fs-plugin-info-dialog.php:1366
1059
  msgid "%s time"
1060
  msgstr ""
1061
 
1062
- #: includes/fs-plugin-info-dialog.php:1368
1063
  msgid "%s times"
1064
  msgstr ""
1065
 
1066
- #: includes/fs-plugin-info-dialog.php:1379
1067
  msgid "WordPress.org Plugin Page"
1068
  msgstr ""
1069
 
1070
- #: includes/fs-plugin-info-dialog.php:1388
1071
  msgid "Plugin Homepage"
1072
  msgstr ""
1073
 
1074
- #: includes/fs-plugin-info-dialog.php:1397, includes/fs-plugin-info-dialog.php:1481
1075
  msgid "Donate to this plugin"
1076
  msgstr ""
1077
 
1078
- #: includes/fs-plugin-info-dialog.php:1404
1079
  msgid "Average Rating"
1080
  msgstr ""
1081
 
1082
- #: includes/fs-plugin-info-dialog.php:1411
1083
  msgid "based on %s"
1084
  msgstr ""
1085
 
1086
- #: includes/fs-plugin-info-dialog.php:1415
1087
  msgid "%s rating"
1088
  msgstr ""
1089
 
1090
- #: includes/fs-plugin-info-dialog.php:1417
1091
  msgid "%s ratings"
1092
  msgstr ""
1093
 
1094
- #: includes/fs-plugin-info-dialog.php:1432
1095
  msgid "%s star"
1096
  msgstr ""
1097
 
1098
- #: includes/fs-plugin-info-dialog.php:1434
1099
  msgid "%s stars"
1100
  msgstr ""
1101
 
1102
- #: includes/fs-plugin-info-dialog.php:1446
1103
  msgid "Click to see reviews that provided a rating of %s"
1104
  msgstr ""
1105
 
1106
- #: includes/fs-plugin-info-dialog.php:1459
1107
  msgid "Contributors"
1108
  msgstr ""
1109
 
1110
- #: includes/fs-plugin-info-dialog.php:1489, includes/fs-plugin-info-dialog.php:1491
 
 
 
 
 
 
 
 
1111
  msgid "Warning"
1112
  msgstr ""
1113
 
1114
- #: includes/fs-plugin-info-dialog.php:1489
1115
  msgid "This plugin has not been tested with your current version of WordPress."
1116
  msgstr ""
1117
 
1118
- #: includes/fs-plugin-info-dialog.php:1491
1119
  msgid "This plugin has not been marked as compatible with your version of WordPress."
1120
  msgstr ""
1121
 
1122
- #: includes/fs-plugin-info-dialog.php:1510
1123
  msgid "Paid add-on must be deployed to Freemius."
1124
  msgstr ""
1125
 
1126
- #: includes/fs-plugin-info-dialog.php:1511
1127
  msgid "Add-on must be deployed to WordPress.org or Freemius."
1128
  msgstr ""
1129
 
1130
- #: includes/fs-plugin-info-dialog.php:1532
1131
  msgid "Newer Version (%s) Installed"
1132
  msgstr ""
1133
 
1134
- #: includes/fs-plugin-info-dialog.php:1533
1135
  msgid "Newer Free Version (%s) Installed"
1136
  msgstr ""
1137
 
1138
- #: includes/fs-plugin-info-dialog.php:1540
1139
  msgid "Latest Version Installed"
1140
  msgstr ""
1141
 
1142
- #: includes/fs-plugin-info-dialog.php:1541
1143
  msgid "Latest Free Version Installed"
1144
  msgstr ""
1145
 
1146
- #: templates/account.php:102, templates/forms/subscription-cancellation.php:96, templates/account/partials/addon.php:26, templates/account/partials/site.php:311
1147
  msgid "Downgrading your plan"
1148
  msgstr ""
1149
 
1150
- #: templates/account.php:103, templates/forms/subscription-cancellation.php:97, templates/account/partials/addon.php:27, templates/account/partials/site.php:312
1151
  msgid "Cancelling the subscription"
1152
  msgstr ""
1153
 
1154
  #. translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription'
1155
- #: templates/account.php:105, templates/forms/subscription-cancellation.php:99, templates/account/partials/site.php:314
1156
  msgid "%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s."
1157
  msgstr ""
1158
 
1159
- #: templates/account.php:106, templates/forms/subscription-cancellation.php:100, templates/account/partials/addon.php:30, templates/account/partials/site.php:315
1160
  msgid "Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price."
1161
  msgstr ""
1162
 
1163
- #: templates/account.php:107, templates/forms/subscription-cancellation.php:106, templates/account/partials/addon.php:31
1164
  msgid "Cancelling the trial will immediately block access to all premium features. Are you sure?"
1165
  msgstr ""
1166
 
1167
- #: templates/account.php:108, templates/forms/subscription-cancellation.php:101, templates/account/partials/addon.php:32, templates/account/partials/site.php:316
1168
  msgid "You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support."
1169
  msgstr ""
1170
 
1171
- #: templates/account.php:109, templates/forms/subscription-cancellation.php:102, templates/account/partials/addon.php:33, templates/account/partials/site.php:317
1172
  msgid "Once your license expires you can still use the Free version but you will NOT have access to the %s features."
1173
  msgstr ""
1174
 
1175
  #. translators: %s: Plan title (e.g. "Professional")
1176
- #: templates/account.php:111, templates/account/partials/activate-license-button.php:31, templates/account/partials/addon.php:35
1177
  msgid "Activate %s Plan"
1178
  msgstr ""
1179
 
1180
  #. translators: %s: Time period (e.g. Auto renews in "2 months")
1181
- #: templates/account.php:114, templates/account/partials/addon.php:38, templates/account/partials/site.php:291
1182
  msgid "Auto renews in %s"
1183
  msgstr ""
1184
 
1185
  #. translators: %s: Time period (e.g. Expires in "2 months")
1186
- #: templates/account.php:116, templates/account/partials/addon.php:40, templates/account/partials/site.php:293
1187
  msgid "Expires in %s"
1188
  msgstr ""
1189
 
1190
- #: templates/account.php:117
1191
  msgctxt "as synchronize license"
1192
  msgid "Sync License"
1193
  msgstr ""
1194
 
1195
- #: templates/account.php:118, templates/account/partials/addon.php:41
1196
  msgid "Cancel Trial"
1197
  msgstr ""
1198
 
1199
- #: templates/account.php:119, templates/account/partials/addon.php:42
1200
  msgid "Change Plan"
1201
  msgstr ""
1202
 
1203
- #: templates/account.php:120, templates/account/partials/addon.php:43
1204
  msgctxt "verb"
1205
  msgid "Upgrade"
1206
  msgstr ""
1207
 
1208
- #: templates/account.php:122, templates/account/partials/addon.php:45, templates/account/partials/site.php:318
1209
  msgctxt "verb"
1210
  msgid "Downgrade"
1211
  msgstr ""
1212
 
1213
- #: templates/account.php:124, templates/add-ons.php:246, templates/plugin-info/features.php:72, templates/account/partials/addon.php:47, templates/account/partials/site.php:33
1214
  msgid "Free"
1215
  msgstr ""
1216
 
1217
- #: templates/account.php:126, templates/debug.php:371, includes/customizer/class-fs-customizer-upsell-control.php:110, templates/account/partials/addon.php:49
1218
  msgctxt "as product pricing plan"
1219
  msgid "Plan"
1220
  msgstr ""
1221
 
1222
- #: templates/account.php:127
1223
  msgid "Bundle Plan"
1224
  msgstr ""
1225
 
1226
- #: templates/account.php:250
1227
  msgid "Free Trial"
1228
  msgstr ""
1229
 
1230
- #: templates/account.php:261
1231
  msgid "Account Details"
1232
  msgstr ""
1233
 
1234
- #: templates/account.php:268, templates/forms/data-debug-mode.php:33
1235
  msgid "Start Debug"
1236
  msgstr ""
1237
 
1238
- #: templates/account.php:270
1239
  msgid "Stop Debug"
1240
  msgstr ""
1241
 
1242
- #: templates/account.php:277
1243
  msgid "Billing & Invoices"
1244
  msgstr ""
1245
 
1246
- #: templates/account.php:288
1247
- msgid "Deleting the account will automatically deactivate your %s plan license so you can use it on other sites. If you want to terminate the recurring payments as well, click the \"Cancel\" button, and first \"Downgrade\" your account. Are you sure you would like to continue with the deletion?"
1248
- msgstr ""
1249
-
1250
- #: templates/account.php:290
1251
- msgid "Deletion is not temporary. Only delete if you no longer want to use this %s anymore. Are you sure you would like to continue with the deletion?"
1252
- msgstr ""
1253
-
1254
- #: templates/account.php:293
1255
- msgid "Delete Account"
1256
- msgstr ""
1257
-
1258
- #: templates/account.php:305, templates/account/partials/addon.php:231, templates/account/partials/deactivate-license-button.php:35
1259
  msgid "Deactivate License"
1260
  msgstr ""
1261
 
1262
- #: templates/account.php:328, templates/forms/subscription-cancellation.php:125
1263
  msgid "Are you sure you want to proceed?"
1264
  msgstr ""
1265
 
1266
- #: templates/account.php:328, templates/account/partials/addon.php:255
1267
  msgid "Cancel Subscription"
1268
  msgstr ""
1269
 
1270
- #: templates/account.php:357, templates/account/partials/addon.php:340
1271
  msgctxt "as synchronize"
1272
  msgid "Sync"
1273
  msgstr ""
1274
 
1275
- #: templates/account.php:372, templates/debug.php:505
1276
  msgid "Name"
1277
  msgstr ""
1278
 
1279
- #: templates/account.php:378, templates/debug.php:506
1280
  msgid "Email"
1281
  msgstr ""
1282
 
1283
- #: templates/account.php:385, templates/debug.php:369, templates/debug.php:555
1284
  msgid "User ID"
1285
  msgstr ""
1286
 
1287
- #: templates/account.php:403, templates/account.php:721, templates/account.php:754, templates/debug.php:236, templates/debug.php:363, templates/debug.php:452, templates/debug.php:504, templates/debug.php:553, templates/debug.php:632, templates/account/payments.php:35, templates/debug/logger.php:21
1288
  msgid "ID"
1289
  msgstr ""
1290
 
1291
- #: templates/account.php:410
1292
  msgid "Site ID"
1293
  msgstr ""
1294
 
1295
- #: templates/account.php:413
1296
  msgid "No ID"
1297
  msgstr ""
1298
 
1299
- #: templates/account.php:418, templates/debug.php:243, templates/debug.php:372, templates/debug.php:456, templates/debug.php:508, templates/account/partials/site.php:227
1300
  msgid "Public Key"
1301
  msgstr ""
1302
 
1303
- #: templates/account.php:424, templates/debug.php:373, templates/debug.php:457, templates/debug.php:509, templates/account/partials/site.php:239
1304
  msgid "Secret Key"
1305
  msgstr ""
1306
 
1307
- #: templates/account.php:427
1308
  msgctxt "as secret encryption key missing"
1309
  msgid "No Secret"
1310
  msgstr ""
1311
 
1312
- #: templates/account.php:454, templates/account/partials/site.php:120, templates/account/partials/site.php:122
1313
  msgid "Trial"
1314
  msgstr ""
1315
 
1316
- #: templates/account.php:481, templates/debug.php:561, templates/account/partials/site.php:260
1317
  msgid "License Key"
1318
  msgstr ""
1319
 
1320
- #: templates/account.php:512
1321
  msgid "Join the Beta program"
1322
  msgstr ""
1323
 
1324
- #: templates/account.php:518
1325
  msgid "not verified"
1326
  msgstr ""
1327
 
1328
- #: templates/account.php:527, templates/account/partials/addon.php:190
1329
  msgid "Expired"
1330
  msgstr ""
1331
 
1332
- #: templates/account.php:587
1333
  msgid "Premium version"
1334
  msgstr ""
1335
 
1336
- #: templates/account.php:589
1337
  msgid "Free version"
1338
  msgstr ""
1339
 
1340
- #: templates/account.php:601
1341
  msgid "Verify Email"
1342
  msgstr ""
1343
 
1344
- #: templates/account.php:615
1345
  msgid "Download %s Version"
1346
  msgstr ""
1347
 
1348
- #: templates/account.php:631
1349
  msgid "Download Paid Version"
1350
  msgstr ""
1351
 
1352
- #: templates/account.php:649, templates/account.php:892, templates/account/partials/site.php:248, templates/account/partials/site.php:270
1353
  msgctxt "verb"
1354
  msgid "Show"
1355
  msgstr ""
1356
 
1357
- #: templates/account.php:664
1358
  msgid "What is your %s?"
1359
  msgstr ""
1360
 
1361
- #: templates/account.php:672, templates/account/billing.php:21
1362
  msgctxt "verb"
1363
  msgid "Edit"
1364
  msgstr ""
1365
 
1366
- #: templates/account.php:676, templates/forms/user-change.php:27
1367
  msgid "Change User"
1368
  msgstr ""
1369
 
1370
- #: templates/account.php:700
1371
  msgid "Sites"
1372
  msgstr ""
1373
 
1374
- #: templates/account.php:713
1375
  msgid "Search by address"
1376
  msgstr ""
1377
 
1378
- #: templates/account.php:722, templates/debug.php:366
1379
  msgid "Address"
1380
  msgstr ""
1381
 
1382
- #: templates/account.php:723
1383
  msgid "License"
1384
  msgstr ""
1385
 
1386
- #: templates/account.php:724
1387
  msgid "Plan"
1388
  msgstr ""
1389
 
1390
- #: templates/account.php:757
1391
  msgctxt "as software license"
1392
  msgid "License"
1393
  msgstr ""
1394
 
1395
- #: templates/account.php:886
1396
  msgctxt "verb"
1397
  msgid "Hide"
1398
  msgstr ""
1399
 
1400
- #: templates/account.php:908, templates/forms/data-debug-mode.php:31
1401
  msgid "Processing"
1402
  msgstr ""
1403
 
1404
- #: templates/account.php:911
1405
  msgid "Get updates for bleeding edge Beta versions of %s."
1406
  msgstr ""
1407
 
1408
- #: templates/account.php:969
1409
  msgid "Cancelling %s"
1410
  msgstr ""
1411
 
1412
- #: templates/account.php:969, templates/account.php:986, templates/forms/subscription-cancellation.php:27, templates/forms/deactivation/form.php:133
1413
  msgid "trial"
1414
  msgstr ""
1415
 
1416
- #: templates/account.php:984, templates/forms/deactivation/form.php:150
1417
  msgid "Cancelling %s..."
1418
  msgstr ""
1419
 
1420
- #: templates/account.php:987, templates/forms/subscription-cancellation.php:28, templates/forms/deactivation/form.php:134
1421
  msgid "subscription"
1422
  msgstr ""
1423
 
1424
- #: templates/account.php:1001
1425
  msgid "Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?"
1426
  msgstr ""
1427
 
1428
- #: templates/account.php:1075
1429
  msgid "Disabling white-label mode"
1430
  msgstr ""
1431
 
1432
- #: templates/account.php:1076
1433
  msgid "Enabling white-label mode"
1434
  msgstr ""
1435
 
@@ -1455,7 +1467,7 @@ msgctxt "installed add-on"
1455
  msgid "Installed"
1456
  msgstr ""
1457
 
1458
- #: templates/admin-notice.php:13, templates/forms/license-activation.php:222, templates/forms/resend-key.php:77
1459
  msgctxt "as close a window"
1460
  msgid "Dismiss"
1461
  msgstr ""
@@ -1489,198 +1501,147 @@ msgid "PCI compliant"
1489
  msgstr ""
1490
 
1491
  #. translators: %s: name (e.g. Hey John,)
1492
- #: templates/connect.php:112
1493
  msgctxt "greeting"
1494
  msgid "Hey %s,"
1495
  msgstr ""
1496
 
1497
- #: templates/connect.php:162
 
 
 
 
 
 
 
 
1498
  msgid "Allow & Continue"
1499
  msgstr ""
1500
 
1501
- #: templates/connect.php:166
1502
  msgid "Re-send activation email"
1503
  msgstr ""
1504
 
1505
- #: templates/connect.php:170
1506
  msgid "Thanks %s!"
1507
  msgstr ""
1508
 
1509
- #: templates/connect.php:180, templates/forms/license-activation.php:46
1510
- msgid "Agree & Activate License"
1511
  msgstr ""
1512
 
1513
- #: templates/connect.php:184
1514
- msgid "Welcome to %s! To get started, please enter your license key:"
1515
  msgstr ""
1516
 
1517
- #: templates/connect.php:191
1518
- msgid "Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s."
 
 
 
 
 
1519
  msgstr ""
1520
 
1521
- #: templates/connect.php:192
1522
- msgid "Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking with %4$s."
1523
  msgstr ""
1524
 
1525
- #: templates/connect.php:198
1526
- msgid "Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s. If you skip this, that's okay! %1$s will still work just fine."
1527
  msgstr ""
1528
 
1529
- #: templates/connect.php:199
1530
- msgid "Never miss an important update - opt in to our security & feature updates notifications, and non-sensitive diagnostic tracking with %4$s. If you skip this, that's okay! %1$s will still work just fine."
1531
  msgstr ""
1532
 
1533
- #: templates/connect.php:233
1534
  msgid "We're excited to introduce the Freemius network-level integration."
1535
  msgstr ""
1536
 
1537
- #: templates/connect.php:236
1538
  msgid "During the update process we detected %d site(s) that are still pending license activation."
1539
  msgstr ""
1540
 
1541
- #: templates/connect.php:238
1542
  msgid "If you'd like to use the %s on those sites, please enter your license key below and click the activation button."
1543
  msgstr ""
1544
 
1545
- #: templates/connect.php:240
1546
  msgid "%s's paid features"
1547
  msgstr ""
1548
 
1549
- #: templates/connect.php:245
1550
  msgid "Alternatively, you can skip it for now and activate the license later, in your %s's network-level Account page."
1551
  msgstr ""
1552
 
1553
- #: templates/connect.php:247
1554
  msgid "During the update process we detected %s site(s) in the network that are still pending your attention."
1555
  msgstr ""
1556
 
1557
- #: templates/connect.php:256, templates/forms/data-debug-mode.php:35, templates/forms/license-activation.php:49
1558
  msgid "License key"
1559
  msgstr ""
1560
 
1561
- #: templates/connect.php:259, templates/forms/license-activation.php:22
1562
  msgid "Can't find your license key?"
1563
  msgstr ""
1564
 
1565
- #: templates/connect.php:318, templates/connect.php:700, templates/forms/deactivation/retry-skip.php:20
1566
  msgctxt "verb"
1567
  msgid "Skip"
1568
  msgstr ""
1569
 
1570
- #: templates/connect.php:321
1571
  msgid "Delegate to Site Admins"
1572
  msgstr ""
1573
 
1574
- #: templates/connect.php:321
1575
  msgid "If you click it, this decision will be delegated to the sites administrators."
1576
  msgstr ""
1577
 
1578
- #: templates/connect.php:346
1579
  msgid "License issues?"
1580
  msgstr ""
1581
 
1582
- #: templates/connect.php:362
1583
- msgid "Your Profile Overview"
1584
- msgstr ""
1585
-
1586
- #: templates/connect.php:363
1587
- msgid "Name and email address"
1588
- msgstr ""
1589
-
1590
- #: templates/connect.php:370
1591
- msgid "So you can manage and control your license remotely from the User Dashboard."
1592
- msgstr ""
1593
-
1594
- #: templates/connect.php:371
1595
- msgid "Your Site Overview"
1596
- msgstr ""
1597
-
1598
- #: templates/connect.php:372
1599
- msgid "Site URL, WP version, PHP info"
1600
- msgstr ""
1601
-
1602
- #: templates/connect.php:379
1603
- msgid "Admin Notices"
1604
- msgstr ""
1605
-
1606
- #: templates/connect.php:380, templates/connect.php:398
1607
- msgid "Updates, announcements, marketing, no spam"
1608
- msgstr ""
1609
-
1610
- #: templates/connect.php:387
1611
- msgid "So you can reuse the license when the %s is no longer active."
1612
- msgstr ""
1613
-
1614
- #: templates/connect.php:388
1615
- msgid "Current %s Status"
1616
- msgstr ""
1617
-
1618
- #: templates/connect.php:389
1619
- msgid "Active, deactivated, or uninstalled"
1620
- msgstr ""
1621
-
1622
- #: templates/connect.php:397
1623
- msgid "Newsletter"
1624
- msgstr ""
1625
-
1626
- #: templates/connect.php:405
1627
- msgid "Plugins & Themes"
1628
- msgstr ""
1629
-
1630
- #: templates/connect.php:405
1631
- msgid "optional"
1632
- msgstr ""
1633
-
1634
- #: templates/connect.php:406
1635
- msgid "To help us troubleshoot any potential issues that may arise from other plugin or theme conflicts."
1636
- msgstr ""
1637
-
1638
- #: templates/connect.php:407
1639
- msgid "Title, slug, version, and is active"
1640
- msgstr ""
1641
-
1642
- #: templates/connect.php:424
1643
- msgid "The %1$s will periodically send %2$s to %3$s for security & feature updates delivery, and license management."
1644
- msgstr ""
1645
-
1646
- #: templates/connect.php:426
1647
- msgid "diagnostic data"
1648
- msgstr ""
1649
-
1650
- #: templates/connect.php:427
1651
- msgid "Freemius is our licensing and software updates engine"
1652
  msgstr ""
1653
 
1654
  #: templates/connect.php:430
1655
- msgid "What permissions are being granted?"
1656
  msgstr ""
1657
 
1658
- #: templates/connect.php:457
1659
  msgid "Don't have a license key?"
1660
  msgstr ""
1661
 
1662
- #: templates/connect.php:460
1663
  msgid "Have a license key?"
1664
  msgstr ""
1665
 
1666
- #: templates/connect.php:468
 
 
 
 
1667
  msgid "Privacy Policy"
1668
  msgstr ""
1669
 
1670
- #: templates/connect.php:470
1671
  msgid "License Agreement"
1672
  msgstr ""
1673
 
1674
- #: templates/connect.php:470
1675
  msgid "Terms of Service"
1676
  msgstr ""
1677
 
1678
- #: templates/connect.php:866
1679
  msgctxt "as in the process of sending an email"
1680
  msgid "Sending email"
1681
  msgstr ""
1682
 
1683
- #: templates/connect.php:867
1684
  msgctxt "as activating plugin"
1685
  msgid "Activating"
1686
  msgstr ""
@@ -1699,235 +1660,239 @@ msgctxt "as turned on"
1699
  msgid "On"
1700
  msgstr ""
1701
 
1702
- #: templates/debug.php:20
1703
  msgid "SDK"
1704
  msgstr ""
1705
 
1706
- #: templates/debug.php:24
1707
  msgctxt "as code debugging"
1708
  msgid "Debugging"
1709
  msgstr ""
1710
 
1711
- #: templates/debug.php:52, templates/debug.php:248, templates/debug.php:374, templates/debug.php:510
1712
  msgid "Actions"
1713
  msgstr ""
1714
 
1715
- #: templates/debug.php:62
1716
  msgid "Are you sure you want to delete all Freemius data?"
1717
  msgstr ""
1718
 
1719
- #: templates/debug.php:62
1720
  msgid "Delete All Accounts"
1721
  msgstr ""
1722
 
1723
- #: templates/debug.php:69
1724
  msgid "Clear API Cache"
1725
  msgstr ""
1726
 
1727
- #: templates/debug.php:77
1728
  msgid "Clear Updates Transients"
1729
  msgstr ""
1730
 
1731
- #: templates/debug.php:84
 
 
 
 
1732
  msgid "Sync Data From Server"
1733
  msgstr ""
1734
 
1735
- #: templates/debug.php:93
1736
  msgid "Migrate Options to Network"
1737
  msgstr ""
1738
 
1739
- #: templates/debug.php:98
1740
  msgid "Load DB Option"
1741
  msgstr ""
1742
 
1743
- #: templates/debug.php:101
1744
  msgid "Set DB Option"
1745
  msgstr ""
1746
 
1747
- #: templates/debug.php:180
1748
  msgid "Key"
1749
  msgstr ""
1750
 
1751
- #: templates/debug.php:181
1752
  msgid "Value"
1753
  msgstr ""
1754
 
1755
- #: templates/debug.php:197
1756
  msgctxt "as software development kit versions"
1757
  msgid "SDK Versions"
1758
  msgstr ""
1759
 
1760
- #: templates/debug.php:202
1761
  msgid "SDK Path"
1762
  msgstr ""
1763
 
1764
- #: templates/debug.php:203, templates/debug.php:242
1765
  msgid "Module Path"
1766
  msgstr ""
1767
 
1768
- #: templates/debug.php:204
1769
  msgid "Is Active"
1770
  msgstr ""
1771
 
1772
- #: templates/debug.php:232, templates/debug/plugins-themes-sync.php:35
1773
  msgid "Plugins"
1774
  msgstr ""
1775
 
1776
- #: templates/debug.php:232, templates/debug/plugins-themes-sync.php:56
1777
  msgid "Themes"
1778
  msgstr ""
1779
 
1780
- #: templates/debug.php:237, templates/debug.php:368, templates/debug.php:454, templates/debug/scheduled-crons.php:80
1781
  msgid "Slug"
1782
  msgstr ""
1783
 
1784
- #: templates/debug.php:239, templates/debug.php:453
1785
  msgid "Title"
1786
  msgstr ""
1787
 
1788
- #: templates/debug.php:240
1789
  msgctxt "as application program interface"
1790
  msgid "API"
1791
  msgstr ""
1792
 
1793
- #: templates/debug.php:241
1794
  msgid "Freemius State"
1795
  msgstr ""
1796
 
1797
- #: templates/debug.php:245
1798
  msgid "Network Blog"
1799
  msgstr ""
1800
 
1801
- #: templates/debug.php:246
1802
  msgid "Network User"
1803
  msgstr ""
1804
 
1805
- #: templates/debug.php:283
1806
  msgctxt "as connection was successful"
1807
  msgid "Connected"
1808
  msgstr ""
1809
 
1810
- #: templates/debug.php:284
1811
  msgctxt "as connection blocked"
1812
  msgid "Blocked"
1813
  msgstr ""
1814
 
1815
- #: templates/debug.php:320
1816
  msgid "Simulate Trial Promotion"
1817
  msgstr ""
1818
 
1819
- #: templates/debug.php:332
1820
  msgid "Simulate Network Upgrade"
1821
  msgstr ""
1822
 
1823
- #: templates/debug.php:357
1824
  msgid "%s Installs"
1825
  msgstr ""
1826
 
1827
- #: templates/debug.php:359
1828
  msgctxt "like websites"
1829
  msgid "Sites"
1830
  msgstr ""
1831
 
1832
- #: templates/debug.php:365, templates/account/partials/site.php:156
1833
  msgid "Blog ID"
1834
  msgstr ""
1835
 
1836
- #: templates/debug.php:370
1837
  msgid "License ID"
1838
  msgstr ""
1839
 
1840
- #: templates/debug.php:434, templates/debug.php:533, templates/account/partials/addon.php:435
1841
  msgctxt "verb"
1842
  msgid "Delete"
1843
  msgstr ""
1844
 
1845
- #: templates/debug.php:448
1846
  msgid "Add Ons of module %s"
1847
  msgstr ""
1848
 
1849
- #: templates/debug.php:500
1850
  msgid "Users"
1851
  msgstr ""
1852
 
1853
- #: templates/debug.php:507
1854
  msgid "Verified"
1855
  msgstr ""
1856
 
1857
- #: templates/debug.php:549
1858
  msgid "%s Licenses"
1859
  msgstr ""
1860
 
1861
- #: templates/debug.php:554
1862
  msgid "Plugin ID"
1863
  msgstr ""
1864
 
1865
- #: templates/debug.php:556
1866
  msgid "Plan ID"
1867
  msgstr ""
1868
 
1869
- #: templates/debug.php:557
1870
  msgid "Quota"
1871
  msgstr ""
1872
 
1873
- #: templates/debug.php:558
1874
  msgid "Activated"
1875
  msgstr ""
1876
 
1877
- #: templates/debug.php:559
1878
  msgid "Blocking"
1879
  msgstr ""
1880
 
1881
- #: templates/debug.php:560, templates/debug.php:631, templates/debug/logger.php:22
1882
  msgid "Type"
1883
  msgstr ""
1884
 
1885
- #: templates/debug.php:562
1886
  msgctxt "as expiration date"
1887
  msgid "Expiration"
1888
  msgstr ""
1889
 
1890
- #: templates/debug.php:590
1891
  msgid "Debug Log"
1892
  msgstr ""
1893
 
1894
- #: templates/debug.php:594
1895
  msgid "All Types"
1896
  msgstr ""
1897
 
1898
- #: templates/debug.php:601
1899
  msgid "All Requests"
1900
  msgstr ""
1901
 
1902
- #: templates/debug.php:606, templates/debug.php:635, templates/debug/logger.php:25
1903
  msgid "File"
1904
  msgstr ""
1905
 
1906
- #: templates/debug.php:607, templates/debug.php:633, templates/debug/logger.php:23
1907
  msgid "Function"
1908
  msgstr ""
1909
 
1910
- #: templates/debug.php:608
1911
  msgid "Process ID"
1912
  msgstr ""
1913
 
1914
- #: templates/debug.php:609
1915
  msgid "Logger"
1916
  msgstr ""
1917
 
1918
- #: templates/debug.php:610, templates/debug.php:634, templates/debug/logger.php:24
1919
  msgid "Message"
1920
  msgstr ""
1921
 
1922
- #: templates/debug.php:612
1923
  msgid "Filter"
1924
  msgstr ""
1925
 
1926
- #: templates/debug.php:620
1927
  msgid "Download"
1928
  msgstr ""
1929
 
1930
- #: templates/debug.php:636, templates/debug/logger.php:26
1931
  msgid "Timestamp"
1932
  msgstr ""
1933
 
@@ -1952,6 +1917,211 @@ msgstr ""
1952
  msgid "Requests"
1953
  msgstr ""
1954
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1955
  #: templates/account/billing.php:22
1956
  msgctxt "verb"
1957
  msgid "Update"
@@ -2017,6 +2187,16 @@ msgstr ""
2017
  msgid "Invoice"
2018
  msgstr ""
2019
 
 
 
 
 
 
 
 
 
 
 
2020
  #: templates/debug/api-calls.php:56
2021
  msgid "API"
2022
  msgstr ""
@@ -2105,143 +2285,143 @@ msgstr ""
2105
  msgid "Next"
2106
  msgstr ""
2107
 
2108
- #: templates/forms/affiliation.php:82
2109
  msgid "Non-expiring"
2110
  msgstr ""
2111
 
2112
- #: templates/forms/affiliation.php:85
2113
  msgid "Apply to become an affiliate"
2114
  msgstr ""
2115
 
2116
- #: templates/forms/affiliation.php:107
2117
  msgid "Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s."
2118
  msgstr ""
2119
 
2120
- #: templates/forms/affiliation.php:122
2121
  msgid "Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information."
2122
  msgstr ""
2123
 
2124
- #: templates/forms/affiliation.php:125
2125
  msgid "Your affiliation account was temporarily suspended."
2126
  msgstr ""
2127
 
2128
- #: templates/forms/affiliation.php:128
2129
  msgid "Thank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days."
2130
  msgstr ""
2131
 
2132
- #: templates/forms/affiliation.php:131
2133
  msgid "Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support."
2134
  msgstr ""
2135
 
2136
- #: templates/forms/affiliation.php:144
2137
  msgid "Like the %s? Become our ambassador and earn cash ;-)"
2138
  msgstr ""
2139
 
2140
- #: templates/forms/affiliation.php:145
2141
  msgid "Refer new customers to our %s and earn %s commission on each successful sale you refer!"
2142
  msgstr ""
2143
 
2144
- #: templates/forms/affiliation.php:148
2145
  msgid "Program Summary"
2146
  msgstr ""
2147
 
2148
- #: templates/forms/affiliation.php:150
2149
  msgid "%s commission when a customer purchases a new license."
2150
  msgstr ""
2151
 
2152
- #: templates/forms/affiliation.php:152
2153
  msgid "Get commission for automated subscription renewals."
2154
  msgstr ""
2155
 
2156
- #: templates/forms/affiliation.php:155
2157
  msgid "%s tracking cookie after the first visit to maximize earnings potential."
2158
  msgstr ""
2159
 
2160
- #: templates/forms/affiliation.php:158
2161
  msgid "Unlimited commissions."
2162
  msgstr ""
2163
 
2164
- #: templates/forms/affiliation.php:160
2165
  msgid "%s minimum payout amount."
2166
  msgstr ""
2167
 
2168
- #: templates/forms/affiliation.php:161
2169
  msgid "Payouts are in USD and processed monthly via PayPal."
2170
  msgstr ""
2171
 
2172
- #: templates/forms/affiliation.php:162
2173
  msgid "As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days."
2174
  msgstr ""
2175
 
2176
- #: templates/forms/affiliation.php:165
2177
  msgid "Affiliate"
2178
  msgstr ""
2179
 
2180
- #: templates/forms/affiliation.php:168, templates/forms/resend-key.php:23
2181
  msgid "Email address"
2182
  msgstr ""
2183
 
2184
- #: templates/forms/affiliation.php:172
2185
  msgid "Full name"
2186
  msgstr ""
2187
 
2188
- #: templates/forms/affiliation.php:176
2189
  msgid "PayPal account email address"
2190
  msgstr ""
2191
 
2192
- #: templates/forms/affiliation.php:180
2193
  msgid "Where are you going to promote the %s?"
2194
  msgstr ""
2195
 
2196
- #: templates/forms/affiliation.php:182
2197
  msgid "Enter the domain of your website or other websites from where you plan to promote the %s."
2198
  msgstr ""
2199
 
2200
- #: templates/forms/affiliation.php:184
2201
  msgid "Add another domain"
2202
  msgstr ""
2203
 
2204
- #: templates/forms/affiliation.php:188
2205
  msgid "Extra Domains"
2206
  msgstr ""
2207
 
2208
- #: templates/forms/affiliation.php:189
2209
  msgid "Extra domains where you will be marketing the product from."
2210
  msgstr ""
2211
 
2212
- #: templates/forms/affiliation.php:199
2213
  msgid "Promotion methods"
2214
  msgstr ""
2215
 
2216
- #: templates/forms/affiliation.php:202
2217
  msgid "Social media (Facebook, Twitter, etc.)"
2218
  msgstr ""
2219
 
2220
- #: templates/forms/affiliation.php:206
2221
  msgid "Mobile apps"
2222
  msgstr ""
2223
 
2224
- #: templates/forms/affiliation.php:210
2225
  msgid "Website, email, and social media statistics (optional)"
2226
  msgstr ""
2227
 
2228
- #: templates/forms/affiliation.php:213
2229
  msgid "Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential)."
2230
  msgstr ""
2231
 
2232
- #: templates/forms/affiliation.php:217
2233
  msgid "How will you promote us?"
2234
  msgstr ""
2235
 
2236
- #: templates/forms/affiliation.php:220
2237
  msgid "Please provide details on how you intend to promote %s (please be as specific as possible)."
2238
  msgstr ""
2239
 
2240
- #: templates/forms/affiliation.php:232, templates/forms/resend-key.php:22
2241
  msgid "Cancel"
2242
  msgstr ""
2243
 
2244
- #: templates/forms/affiliation.php:234
2245
  msgid "Become an affiliate"
2246
  msgstr ""
2247
 
@@ -2261,66 +2441,93 @@ msgstr ""
2261
  msgid "User key"
2262
  msgstr ""
2263
 
2264
- #: templates/forms/license-activation.php:23
2265
- msgid "Please enter the license key that you received in the email right after the purchase:"
2266
  msgstr ""
2267
 
2268
- #: templates/forms/license-activation.php:28
2269
- msgid "Update License"
 
2270
  msgstr ""
2271
 
2272
- #: templates/forms/license-activation.php:41
2273
- msgid "The %1$s will be periodically sending data to %2$s to check for security and feature updates, and verify the validity of your license."
2274
  msgstr ""
2275
 
2276
- #: templates/forms/license-activation.php:183
2277
- msgid "Associate with the license owner's account."
2278
  msgstr ""
2279
 
2280
- #: templates/forms/optout.php:30
2281
- msgctxt "verb"
2282
- msgid "Opt Out"
2283
  msgstr ""
2284
 
2285
- #: templates/forms/optout.php:31
2286
- msgctxt "verb"
2287
- msgid "Opt In"
 
 
 
2288
  msgstr ""
2289
 
2290
- #: templates/forms/optout.php:34
2291
- msgid "Connectivity to the licensing engine was successfully re-established. Automatic security & feature updates are now available through the WP Admin Dashboard."
2292
  msgstr ""
2293
 
2294
- #: templates/forms/optout.php:36
2295
- msgid "Warning: Opting out will block automatic updates"
2296
  msgstr ""
2297
 
2298
- #: templates/forms/optout.php:37
2299
- msgid "Ongoing connectivity with the licensing engine is essential for receiving automatic security & feature updates of the paid product. To receive these updates, data like your license key, %1$s version, and WordPress version, is periodically sent to the server to check for updates. By opting out, you understand that your site won't receive automatic updates for %2$s from within the WP Admin Dashboard. This can put your site at risk, and we highly recommend to keep this connection active. If you do choose to opt-out, you'll need to check for %1$s updates and install them manually."
2300
  msgstr ""
2301
 
2302
- #: templates/forms/optout.php:39
2303
- msgid "I'd like to keep automatic updates"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2304
  msgstr ""
2305
 
2306
  #: templates/forms/optout.php:44
2307
- msgid "Usage tracking is done in the name of making %s better. Making a better user experience, prioritizing new features, and more good things. We'd really appreciate if you'll reconsider letting us continue with the tracking."
2308
  msgstr ""
2309
 
2310
- #: templates/forms/optout.php:45
2311
- msgid "On second thought - I want to continue helping"
2312
  msgstr ""
2313
 
2314
- #: templates/forms/optout.php:49
2315
- msgid "By clicking \"Opt Out\", we will no longer be sending any data from %s to %s."
2316
  msgstr ""
2317
 
2318
- #: templates/forms/optout.php:74
2319
- msgid "Plugins & themes tracking"
2320
  msgstr ""
2321
 
2322
- #: templates/forms/optout.php:261
2323
- msgid "Saved"
 
 
 
 
2324
  msgstr ""
2325
 
2326
  #: templates/forms/premium-versions-upgrade-handler.php:40
@@ -2344,7 +2551,11 @@ msgstr ""
2344
  msgid "Send License Key"
2345
  msgstr ""
2346
 
2347
- #: templates/forms/resend-key.php:57
 
 
 
 
2348
  msgid "Enter the email address you've used for the upgrade below and we will resend you the license key."
2349
  msgstr ""
2350
 
@@ -2356,10 +2567,6 @@ msgstr ""
2356
  msgid "In case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?"
2357
  msgstr ""
2358
 
2359
- #: templates/forms/subscription-cancellation.php:52
2360
- msgid "license"
2361
- msgstr ""
2362
-
2363
  #: templates/forms/subscription-cancellation.php:57
2364
  msgid "Cancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site."
2365
  msgstr ""
@@ -2380,7 +2587,7 @@ msgstr ""
2380
  msgid "Proceed"
2381
  msgstr ""
2382
 
2383
- #: templates/forms/subscription-cancellation.php:191, templates/forms/deactivation/form.php:171
2384
  msgid "Cancel %s & Proceed"
2385
  msgstr ""
2386
 
@@ -2404,9 +2611,8 @@ msgstr ""
2404
  msgid "Enter email address"
2405
  msgstr ""
2406
 
2407
- #: templates/forms/user-change.php:81
2408
- msgctxt "close window"
2409
- msgid "Dismiss"
2410
  msgstr ""
2411
 
2412
  #: templates/js/style-premium-theme.php:39
@@ -2417,31 +2623,31 @@ msgstr ""
2417
  msgid "Beta"
2418
  msgstr ""
2419
 
2420
- #: templates/partials/network-activation.php:27
2421
  msgid "Activate license on all sites in the network."
2422
  msgstr ""
2423
 
2424
- #: templates/partials/network-activation.php:28
2425
  msgid "Apply on all sites in the network."
2426
  msgstr ""
2427
 
2428
- #: templates/partials/network-activation.php:31
2429
  msgid "Activate license on all pending sites."
2430
  msgstr ""
2431
 
2432
- #: templates/partials/network-activation.php:32
2433
  msgid "Apply on all pending sites."
2434
  msgstr ""
2435
 
2436
- #: templates/partials/network-activation.php:40, templates/partials/network-activation.php:74
2437
  msgid "allow"
2438
  msgstr ""
2439
 
2440
- #: templates/partials/network-activation.php:43, templates/partials/network-activation.php:77
2441
  msgid "delegate"
2442
  msgstr ""
2443
 
2444
- #: templates/partials/network-activation.php:47, templates/partials/network-activation.php:81
2445
  msgid "skip"
2446
  msgstr ""
2447
 
@@ -2467,31 +2673,47 @@ msgid "Last license"
2467
  msgstr ""
2468
 
2469
  #. translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription'
2470
- #: templates/account/partials/addon.php:29
2471
  msgid "%1$s will immediately stop all future recurring payments and your %s plan license will expire in %s."
2472
  msgstr ""
2473
 
2474
- #: templates/account/partials/addon.php:185
2475
  msgid "Cancelled"
2476
  msgstr ""
2477
 
2478
- #: templates/account/partials/addon.php:195
2479
  msgid "No expiration"
2480
  msgstr ""
2481
 
2482
- #: templates/account/partials/site.php:189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2483
  msgid "Owner Name"
2484
  msgstr ""
2485
 
2486
- #: templates/account/partials/site.php:201
2487
  msgid "Owner Email"
2488
  msgstr ""
2489
 
2490
- #: templates/account/partials/site.php:213
2491
  msgid "Owner ID"
2492
  msgstr ""
2493
 
2494
- #: templates/account/partials/site.php:286
2495
  msgid "Subscription"
2496
  msgstr ""
2497
 
@@ -2503,47 +2725,63 @@ msgstr ""
2503
  msgid "Contact Support"
2504
  msgstr ""
2505
 
2506
- #: templates/forms/deactivation/form.php:64
2507
  msgid "Anonymous feedback"
2508
  msgstr ""
2509
 
2510
- #: templates/forms/deactivation/form.php:70
 
 
 
 
 
 
 
 
 
 
 
 
2511
  msgid "Deactivate"
2512
  msgstr ""
2513
 
2514
- #: templates/forms/deactivation/form.php:72
2515
  msgid "Activate %s"
2516
  msgstr ""
2517
 
2518
- #: templates/forms/deactivation/form.php:87
 
 
 
 
2519
  msgid "Quick Feedback"
2520
  msgstr ""
2521
 
2522
- #: templates/forms/deactivation/form.php:91
2523
  msgid "If you have a moment, please let us know why you are %s"
2524
  msgstr ""
2525
 
2526
- #: templates/forms/deactivation/form.php:91
2527
  msgid "deactivating"
2528
  msgstr ""
2529
 
2530
- #: templates/forms/deactivation/form.php:91
2531
  msgid "switching"
2532
  msgstr ""
2533
 
2534
- #: templates/forms/deactivation/form.php:369
2535
- msgid "Submit & %s"
2536
  msgstr ""
2537
 
2538
- #: templates/forms/deactivation/form.php:390
2539
- msgid "Kindly tell us the reason so we can improve."
2540
  msgstr ""
2541
 
2542
- #: templates/forms/deactivation/form.php:515
2543
  msgid "Yes - %s"
2544
  msgstr ""
2545
 
2546
- #: templates/forms/deactivation/form.php:522
2547
  msgid "Skip & %s"
2548
  msgstr ""
2549
 
1
+ # Copyright (C) 2022 freemius
2
  # This file is distributed under the same license as the freemius package.
3
  msgid ""
4
  msgstr ""
16
  "X-Poedit-SourceCharset: UTF-8\n"
17
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18
 
19
+ #: includes/class-freemius.php:1744, templates/account.php:947
20
  msgid "An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned."
21
  msgstr ""
22
 
23
+ #: includes/class-freemius.php:1751
24
  msgid "Would you like to proceed with the update?"
25
  msgstr ""
26
 
27
+ #: includes/class-freemius.php:1976
28
  msgid "Freemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error."
29
  msgstr ""
30
 
31
+ #: includes/class-freemius.php:1978, includes/fs-plugin-info-dialog.php:1517
32
  msgid "Error"
33
  msgstr ""
34
 
35
+ #: includes/class-freemius.php:2424
36
  msgid "I found a better %s"
37
  msgstr ""
38
 
39
+ #: includes/class-freemius.php:2426
40
  msgid "What's the %s's name?"
41
  msgstr ""
42
 
43
+ #: includes/class-freemius.php:2432
44
+ msgid "It's a temporary %s - I'm troubleshooting an issue"
45
  msgstr ""
46
 
47
+ #: includes/class-freemius.php:2434
48
  msgid "Deactivation"
49
  msgstr ""
50
 
51
+ #: includes/class-freemius.php:2435
52
  msgid "Theme Switch"
53
  msgstr ""
54
 
55
+ #: includes/class-freemius.php:2444, templates/forms/resend-key.php:24, templates/forms/user-change.php:29
56
  msgid "Other"
57
  msgstr ""
58
 
59
+ #: includes/class-freemius.php:2452
60
  msgid "I no longer need the %s"
61
  msgstr ""
62
 
63
+ #: includes/class-freemius.php:2459
64
  msgid "I only needed the %s for a short period"
65
  msgstr ""
66
 
67
+ #: includes/class-freemius.php:2465
68
  msgid "The %s broke my site"
69
  msgstr ""
70
 
71
+ #: includes/class-freemius.php:2472
72
  msgid "The %s suddenly stopped working"
73
  msgstr ""
74
 
75
+ #: includes/class-freemius.php:2482
76
  msgid "I can't pay for it anymore"
77
  msgstr ""
78
 
79
+ #: includes/class-freemius.php:2484
80
  msgid "What price would you feel comfortable paying?"
81
  msgstr ""
82
 
83
+ #: includes/class-freemius.php:2490
84
  msgid "I don't like to share my information with you"
85
  msgstr ""
86
 
87
+ #: includes/class-freemius.php:2511
88
  msgid "The %s didn't work"
89
  msgstr ""
90
 
91
+ #: includes/class-freemius.php:2521
92
  msgid "I couldn't understand how to make it work"
93
  msgstr ""
94
 
95
+ #: includes/class-freemius.php:2529
96
  msgid "The %s is great, but I need specific feature that you don't support"
97
  msgstr ""
98
 
99
+ #: includes/class-freemius.php:2531
100
  msgid "What feature?"
101
  msgstr ""
102
 
103
+ #: includes/class-freemius.php:2535
104
  msgid "The %s is not working"
105
  msgstr ""
106
 
107
+ #: includes/class-freemius.php:2537
108
  msgid "Kindly share what didn't work so we can fix it for future users..."
109
  msgstr ""
110
 
111
+ #: includes/class-freemius.php:2541
112
  msgid "It's not what I was looking for"
113
  msgstr ""
114
 
115
+ #: includes/class-freemius.php:2543
116
  msgid "What you've been looking for?"
117
  msgstr ""
118
 
119
+ #: includes/class-freemius.php:2547
120
  msgid "The %s didn't work as expected"
121
  msgstr ""
122
 
123
+ #: includes/class-freemius.php:2549
124
  msgid "What did you expect?"
125
  msgstr ""
126
 
127
+ #: includes/class-freemius.php:3637, templates/debug.php:24
128
  msgid "Freemius Debug"
129
  msgstr ""
130
 
131
+ #: includes/class-freemius.php:4444
132
  msgid "I don't know what is cURL or how to install it, help me!"
133
  msgstr ""
134
 
135
+ #: includes/class-freemius.php:4446
136
  msgid "We'll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update."
137
  msgstr ""
138
 
139
+ #: includes/class-freemius.php:4453
140
  msgid "Great, please install cURL and enable it in your php.ini file. In addition, search for the 'disable_functions' directive in your php.ini file and remove any disabled methods starting with 'curl_'. To make sure it was successfully activated, use 'phpinfo()'. Once activated, deactivate the %s and reactivate it back again."
141
  msgstr ""
142
 
143
+ #: includes/class-freemius.php:4558
144
  msgid "Yes - do your thing"
145
  msgstr ""
146
 
147
+ #: includes/class-freemius.php:4563
148
  msgid "No - just deactivate"
149
  msgstr ""
150
 
151
+ #: includes/class-freemius.php:4608, includes/class-freemius.php:5139, includes/class-freemius.php:6334, includes/class-freemius.php:13998, includes/class-freemius.php:14747, includes/class-freemius.php:18513, includes/class-freemius.php:18618, includes/class-freemius.php:18795, includes/class-freemius.php:21078, includes/class-freemius.php:21456, includes/class-freemius.php:21470, includes/class-freemius.php:22158, includes/class-freemius.php:23174, includes/class-freemius.php:23304, includes/class-freemius.php:23434, templates/add-ons.php:57
152
  msgctxt "exclamation"
153
  msgid "Oops"
154
  msgstr ""
155
 
156
+ #: includes/class-freemius.php:4683
157
  msgid "Thank for giving us the chance to fix it! A message was just sent to our technical staff. We will get back to you as soon as we have an update to %s. Appreciate your patience."
158
  msgstr ""
159
 
160
+ #: includes/class-freemius.php:5108
161
+ msgid "You have purchased a %s license."
162
+ msgstr ""
163
+
164
+ #: includes/class-freemius.php:5112
165
+ msgid " The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box."
166
+ msgstr ""
167
+
168
+ #: includes/class-freemius.php:5122, includes/class-freemius.php:6031, includes/class-freemius.php:17889, includes/class-freemius.php:17900, includes/class-freemius.php:21347, includes/class-freemius.php:21738, includes/class-freemius.php:21807, includes/class-freemius.php:21979
169
+ msgctxt "interjection expressing joy or exuberance"
170
+ msgid "Yee-haw"
171
+ msgstr ""
172
+
173
+ #: includes/class-freemius.php:5136
174
  msgctxt "addonX cannot run without pluginY"
175
  msgid "%s cannot run without %s."
176
  msgstr ""
177
 
178
+ #: includes/class-freemius.php:5137
179
  msgctxt "addonX cannot run..."
180
  msgid "%s cannot run without the plugin."
181
  msgstr ""
182
 
183
+ #: includes/class-freemius.php:5418
184
+ msgid "There was an unexpected API error while processing your request. Please try again in a few minutes and if it still doesn't work, contact the %s's author with the following:"
185
  msgstr ""
186
 
187
+ #: includes/class-freemius.php:6000
188
  msgid "Premium %s version was successfully activated."
189
  msgstr ""
190
 
191
+ #: includes/class-freemius.php:6012, includes/class-freemius.php:7992
192
  msgctxt "Used to express elation, enthusiasm, or triumph (especially in electronic communication)."
193
  msgid "W00t"
194
  msgstr ""
195
 
196
+ #: includes/class-freemius.php:6027
197
  msgid "You have a %s license."
198
  msgstr ""
199
 
200
+ #: includes/class-freemius.php:6317
 
 
 
 
 
201
  msgid "%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license."
202
  msgstr ""
203
 
204
+ #: includes/class-freemius.php:6321
205
  msgid "%s is a premium only add-on. You have to purchase a license first before activating the plugin."
206
  msgstr ""
207
 
208
+ #: includes/class-freemius.php:6330, templates/add-ons.php:186, templates/account/partials/addon.php:386
209
  msgid "More information about %s"
210
  msgstr ""
211
 
212
+ #: includes/class-freemius.php:6331
213
  msgid "Purchase License"
214
  msgstr ""
215
 
216
+ #: includes/class-freemius.php:7318, templates/connect.php:216
217
+ msgid "You should receive a confirmation email for %s to your mailbox at %s. Please make sure you click the button in that email to %s."
218
  msgstr ""
219
 
220
+ #: includes/class-freemius.php:7322
221
  msgid "start the trial"
222
  msgstr ""
223
 
224
+ #: includes/class-freemius.php:7323, templates/connect.php:220
225
+ msgid "complete the opt-in"
226
  msgstr ""
227
 
228
+ #: includes/class-freemius.php:7456
229
  msgid "You are just one step away - %s"
230
  msgstr ""
231
 
232
+ #: includes/class-freemius.php:7459
233
  msgctxt "%s - plugin name. As complete \"PluginX\" activation now"
234
  msgid "Complete \"%s\" Activation Now"
235
  msgstr ""
236
 
237
+ #: includes/class-freemius.php:7541
238
  msgid "We made a few tweaks to the %s, %s"
239
  msgstr ""
240
 
241
+ #: includes/class-freemius.php:7545
242
  msgid "Opt in to make \"%s\" better!"
243
  msgstr ""
244
 
245
+ #: includes/class-freemius.php:7991
246
  msgid "The upgrade of %s was successfully completed."
247
  msgstr ""
248
 
249
+ #: includes/class-freemius.php:10709, includes/class-fs-plugin-updater.php:1090, includes/class-fs-plugin-updater.php:1305, includes/class-fs-plugin-updater.php:1312, templates/auto-installation.php:32
250
  msgid "Add-On"
251
  msgstr ""
252
 
253
+ #: includes/class-freemius.php:10711, templates/account.php:411, templates/account.php:419, templates/debug.php:395, templates/debug.php:615
254
  msgid "Plugin"
255
  msgstr ""
256
 
257
+ #: includes/class-freemius.php:10712, templates/account.php:412, templates/account.php:420, templates/debug.php:395, templates/debug.php:615, templates/forms/deactivation/form.php:107
258
  msgid "Theme"
259
  msgstr ""
260
 
261
+ #: includes/class-freemius.php:13817
262
  msgid "An unknown error has occurred while trying to toggle the license's white-label mode."
263
  msgstr ""
264
 
265
+ #: includes/class-freemius.php:13831
266
  msgid "Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s."
267
  msgstr ""
268
 
269
+ #: includes/class-freemius.php:13836, templates/account/partials/disconnect-button.php:84
270
  msgid "User Dashboard"
271
  msgstr ""
272
 
273
+ #: includes/class-freemius.php:13837
274
  msgid "revert it now"
275
  msgstr ""
276
 
277
+ #: includes/class-freemius.php:13895
278
  msgid "An unknown error has occurred while trying to set the user's beta mode."
279
  msgstr ""
280
 
281
+ #: includes/class-freemius.php:13969
282
  msgid "Invalid new user ID or email address."
283
  msgstr ""
284
 
285
+ #: includes/class-freemius.php:13999
286
  msgid "Sorry, we could not complete the email update. Another user with the same email is already registered."
287
  msgstr ""
288
 
289
+ #: includes/class-freemius.php:14000
290
  msgid "If you would like to give up the ownership of the %s's account to %s click the Change Ownership button."
291
  msgstr ""
292
 
293
+ #: includes/class-freemius.php:14007
294
  msgid "Change Ownership"
295
  msgstr ""
296
 
297
+ #: includes/class-freemius.php:14614
298
  msgid "Invalid site details collection."
299
  msgstr ""
300
 
301
+ #: includes/class-freemius.php:14734
302
  msgid "We couldn't find your email address in the system, are you sure it's the right address?"
303
  msgstr ""
304
 
305
+ #: includes/class-freemius.php:14736
306
  msgid "We can't see any active licenses associated with that email address, are you sure it's the right address?"
307
  msgstr ""
308
 
309
+ #: includes/class-freemius.php:15034
310
+ msgid "Account is pending activation. Please check your email and click the link to activate your account and then submit the affiliate form again."
311
  msgstr ""
312
 
313
+ #: includes/class-freemius.php:15148, templates/forms/premium-versions-upgrade-handler.php:47
314
  msgid "Buy a license now"
315
  msgstr ""
316
 
317
+ #: includes/class-freemius.php:15160, templates/forms/premium-versions-upgrade-handler.php:46
318
  msgid "Renew your license now"
319
  msgstr ""
320
 
321
+ #: includes/class-freemius.php:15164
322
  msgid "%s to access version %s security & feature updates, and support."
323
  msgstr ""
324
 
325
+ #: includes/class-freemius.php:17871
326
+ msgid "%s opt-in was successfully completed."
327
  msgstr ""
328
 
329
+ #: includes/class-freemius.php:17885
330
  msgid "Your account was successfully activated with the %s plan."
331
  msgstr ""
332
 
333
+ #: includes/class-freemius.php:17896, includes/class-freemius.php:21803
334
  msgid "Your trial has been successfully started."
335
  msgstr ""
336
 
337
+ #: includes/class-freemius.php:18511, includes/class-freemius.php:18616, includes/class-freemius.php:18793
338
  msgid "Couldn't activate %s."
339
  msgstr ""
340
 
341
+ #: includes/class-freemius.php:18512, includes/class-freemius.php:18617, includes/class-freemius.php:18794
342
  msgid "Please contact us with the following message:"
343
  msgstr ""
344
 
345
+ #: includes/class-freemius.php:18613, templates/forms/data-debug-mode.php:162
346
  msgid "An unknown error has occurred."
347
  msgstr ""
348
 
349
+ #: includes/class-freemius.php:19153, includes/class-freemius.php:24542
350
  msgid "Upgrade"
351
  msgstr ""
352
 
353
+ #: includes/class-freemius.php:19159
354
  msgid "Start Trial"
355
  msgstr ""
356
 
357
+ #: includes/class-freemius.php:19161
358
  msgid "Pricing"
359
  msgstr ""
360
 
361
+ #: includes/class-freemius.php:19241, includes/class-freemius.php:19243
362
  msgid "Affiliation"
363
  msgstr ""
364
 
365
+ #: includes/class-freemius.php:19271, includes/class-freemius.php:19273, templates/account.php:264, templates/debug.php:362
366
  msgid "Account"
367
  msgstr ""
368
 
369
+ #: includes/class-freemius.php:19287, includes/class-freemius.php:19289, includes/customizer/class-fs-customizer-support-section.php:60
370
  msgid "Contact Us"
371
  msgstr ""
372
 
373
+ #: includes/class-freemius.php:19300, includes/class-freemius.php:19302, includes/class-freemius.php:24556, templates/account.php:134, templates/account/partials/addon.php:49
374
  msgid "Add-Ons"
375
  msgstr ""
376
 
377
+ #: includes/class-freemius.php:19336
378
  msgctxt "ASCII arrow left icon"
379
  msgid "&#x2190;"
380
  msgstr ""
381
 
382
+ #: includes/class-freemius.php:19336
383
  msgctxt "ASCII arrow right icon"
384
  msgid "&#x27a4;"
385
  msgstr ""
386
 
387
+ #: includes/class-freemius.php:19338, templates/pricing.php:110
388
  msgctxt "noun"
389
  msgid "Pricing"
390
  msgstr ""
391
 
392
+ #: includes/class-freemius.php:19551, includes/customizer/class-fs-customizer-support-section.php:67
393
  msgid "Support Forum"
394
  msgstr ""
395
 
396
+ #: includes/class-freemius.php:20572
397
  msgid "Your email has been successfully verified - you are AWESOME!"
398
  msgstr ""
399
 
400
+ #: includes/class-freemius.php:20573
401
  msgctxt "a positive response"
402
  msgid "Right on"
403
  msgstr ""
404
 
405
+ #: includes/class-freemius.php:21079
406
  msgid "seems like the key you entered doesn't match our records."
407
  msgstr ""
408
 
409
+ #: includes/class-freemius.php:21103
410
  msgid "Debug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the \"Stop Debug\" link."
411
  msgstr ""
412
 
413
+ #: includes/class-freemius.php:21338
414
  msgid "Your %s Add-on plan was successfully upgraded."
415
  msgstr ""
416
 
417
+ #: includes/class-freemius.php:21340
418
  msgid "%s Add-on was successfully purchased."
419
  msgstr ""
420
 
421
+ #: includes/class-freemius.php:21343
422
  msgid "Download the latest version"
423
  msgstr ""
424
 
425
+ #: includes/class-freemius.php:21449
426
  msgid "Your server is blocking the access to Freemius' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s"
427
  msgstr ""
428
 
429
+ #: includes/class-freemius.php:21455, includes/class-freemius.php:21469, includes/class-freemius.php:21938, includes/class-freemius.php:22027
430
  msgid "Error received from the server:"
431
  msgstr ""
432
 
433
+ #: includes/class-freemius.php:21469
434
  msgid "It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again."
435
  msgstr ""
436
 
437
+ #: includes/class-freemius.php:21700, includes/class-freemius.php:21943, includes/class-freemius.php:21998, includes/class-freemius.php:22105
438
  msgctxt "something somebody says when they are thinking about what you have just said."
439
  msgid "Hmm"
440
  msgstr ""
441
 
442
+ #: includes/class-freemius.php:21713
443
  msgid "It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry."
444
  msgstr ""
445
 
446
+ #: includes/class-freemius.php:21714, templates/account.php:136, templates/add-ons.php:250, templates/account/partials/addon.php:51
447
  msgctxt "trial period"
448
  msgid "Trial"
449
  msgstr ""
450
 
451
+ #: includes/class-freemius.php:21719
452
  msgid "I have upgraded my account but when I try to Sync the License, the plan remains %s."
453
  msgstr ""
454
 
455
+ #: includes/class-freemius.php:21723, includes/class-freemius.php:21782
456
  msgid "Please contact us here"
457
  msgstr ""
458
 
459
+ #: includes/class-freemius.php:21734
460
  msgid "Your plan was successfully activated."
461
  msgstr ""
462
 
463
+ #: includes/class-freemius.php:21735
464
  msgid "Your plan was successfully upgraded."
465
  msgstr ""
466
 
467
+ #: includes/class-freemius.php:21752
468
  msgid "Your plan was successfully changed to %s."
469
  msgstr ""
470
 
471
+ #: includes/class-freemius.php:21768
472
  msgid "Your license has expired. You can still continue using the free %s forever."
473
  msgstr ""
474
 
475
+ #: includes/class-freemius.php:21770
476
  msgid "Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
477
  msgstr ""
478
 
479
+ #: includes/class-freemius.php:21778
480
  msgid "Your license has been cancelled. If you think it's a mistake, please contact support."
481
  msgstr ""
482
 
483
+ #: includes/class-freemius.php:21791
484
  msgid "Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support."
485
  msgstr ""
486
 
487
+ #: includes/class-freemius.php:21817
488
  msgid "Your free trial has expired. You can still continue using all our free features."
489
  msgstr ""
490
 
491
+ #: includes/class-freemius.php:21819
492
  msgid "Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
493
  msgstr ""
494
 
495
+ #: includes/class-freemius.php:21934
496
  msgid "It looks like the license could not be activated."
497
  msgstr ""
498
 
499
+ #: includes/class-freemius.php:21976
500
  msgid "Your license was successfully activated."
501
  msgstr ""
502
 
503
+ #: includes/class-freemius.php:22002
504
  msgid "It looks like your site currently doesn't have an active license."
505
  msgstr ""
506
 
507
+ #: includes/class-freemius.php:22026
508
  msgid "It looks like the license deactivation failed."
509
  msgstr ""
510
 
511
+ #: includes/class-freemius.php:22055
512
  msgid "Your %s license was successfully deactivated."
513
  msgstr ""
514
 
515
+ #: includes/class-freemius.php:22056
516
  msgid "Your license was successfully deactivated, you are back to the %s plan."
517
  msgstr ""
518
 
519
+ #: includes/class-freemius.php:22059
520
  msgid "O.K"
521
  msgstr ""
522
 
523
+ #: includes/class-freemius.php:22112
524
  msgid "Seems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes."
525
  msgstr ""
526
 
527
+ #: includes/class-freemius.php:22121
528
  msgid "Your subscription was successfully cancelled. Your %s plan license will expire in %s."
529
  msgstr ""
530
 
531
+ #: includes/class-freemius.php:22163
532
  msgid "You are already running the %s in a trial mode."
533
  msgstr ""
534
 
535
+ #: includes/class-freemius.php:22174
536
  msgid "You already utilized a trial before."
537
  msgstr ""
538
 
539
+ #: includes/class-freemius.php:22188
540
  msgid "Plan %s do not exist, therefore, can't start a trial."
541
  msgstr ""
542
 
543
+ #: includes/class-freemius.php:22199
544
  msgid "Plan %s does not support a trial period."
545
  msgstr ""
546
 
547
+ #: includes/class-freemius.php:22210
548
  msgid "None of the %s's plans supports a trial period."
549
  msgstr ""
550
 
551
+ #: includes/class-freemius.php:22259
552
  msgid "It looks like you are not in trial mode anymore so there's nothing to cancel :)"
553
  msgstr ""
554
 
555
+ #: includes/class-freemius.php:22295
556
  msgid "Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes."
557
  msgstr ""
558
 
559
+ #: includes/class-freemius.php:22314
560
  msgid "Your %s free trial was successfully cancelled."
561
  msgstr ""
562
 
563
+ #: includes/class-freemius.php:22641
564
  msgid "Version %s was released."
565
  msgstr ""
566
 
567
+ #: includes/class-freemius.php:22641
568
  msgid "Please download %s."
569
  msgstr ""
570
 
571
+ #: includes/class-freemius.php:22648
572
  msgid "the latest %s version here"
573
  msgstr ""
574
 
575
+ #: includes/class-freemius.php:22653
576
  msgid "New"
577
  msgstr ""
578
 
579
+ #: includes/class-freemius.php:22658
580
  msgid "Seems like you got the latest release."
581
  msgstr ""
582
 
583
+ #: includes/class-freemius.php:22659
584
  msgid "You are all good!"
585
  msgstr ""
586
 
587
+ #: includes/class-freemius.php:23062
588
  msgid "Verification mail was just sent to %s. If you can't find it after 5 min, please check your spam box."
589
  msgstr ""
590
 
591
+ #: includes/class-freemius.php:23202
592
  msgid "Site successfully opted in."
593
  msgstr ""
594
 
595
+ #: includes/class-freemius.php:23203, includes/class-freemius.php:24252
596
  msgid "Awesome"
597
  msgstr ""
598
 
599
+ #: includes/class-freemius.php:23219
600
+ msgid "Sharing diagnostic data with %s helps to provide functionality that's more relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the plugin should be translated and tailored to."
601
  msgstr ""
602
 
603
+ #: includes/class-freemius.php:23220
604
  msgid "Thank you!"
605
  msgstr ""
606
 
607
+ #: includes/class-freemius.php:23229
608
+ msgid "Diagnostic data will no longer be sent from %s to %s."
609
  msgstr ""
610
 
611
+ #: includes/class-freemius.php:23384
612
+ msgid "A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours."
613
  msgstr ""
614
 
615
+ #: includes/class-freemius.php:23386
616
+ msgid "A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder."
617
+ msgstr ""
618
+
619
+ #: includes/class-freemius.php:23393
620
  msgid "Thanks for confirming the ownership change. An email was just sent to %s for final approval."
621
  msgstr ""
622
 
623
+ #: includes/class-freemius.php:23398
624
  msgid "%s is the new owner of the account."
625
  msgstr ""
626
 
627
+ #: includes/class-freemius.php:23400
628
  msgctxt "as congratulations"
629
  msgid "Congrats"
630
  msgstr ""
631
 
632
+ #: includes/class-freemius.php:23417
 
 
 
 
633
  msgid "Please provide your full name."
634
  msgstr ""
635
 
636
+ #: includes/class-freemius.php:23422
637
  msgid "Your name was successfully updated."
638
  msgstr ""
639
 
640
+ #: includes/class-freemius.php:23483
641
  msgid "You have successfully updated your %s."
642
  msgstr ""
643
 
644
+ #: includes/class-freemius.php:23542
645
  msgid "Is this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin."
646
  msgstr ""
647
 
648
+ #: includes/class-freemius.php:23545
649
  msgid "Click here"
650
  msgstr ""
651
 
652
+ #: includes/class-freemius.php:23582, includes/class-freemius.php:23579
653
+ msgid "Bundle"
654
+ msgstr ""
655
+
656
+ #: includes/class-freemius.php:23662
657
  msgid "Just letting you know that the add-ons information of %s is being pulled from an external server."
658
  msgstr ""
659
 
660
+ #: includes/class-freemius.php:23663
661
  msgctxt "advance notice of something that will need attention."
662
  msgid "Heads up"
663
  msgstr ""
664
 
665
+ #: includes/class-freemius.php:24292
666
  msgctxt "exclamation"
667
  msgid "Hey"
668
  msgstr ""
669
 
670
+ #: includes/class-freemius.php:24292
671
  msgid "How do you like %s so far? Test all our %s premium features with a %d-day free trial."
672
  msgstr ""
673
 
674
+ #: includes/class-freemius.php:24300
675
  msgid "No commitment for %s days - cancel anytime!"
676
  msgstr ""
677
 
678
+ #: includes/class-freemius.php:24301
679
  msgid "No credit card required"
680
  msgstr ""
681
 
682
+ #: includes/class-freemius.php:24308, templates/forms/trial-start.php:53
683
  msgctxt "call to action"
684
  msgid "Start free trial"
685
  msgstr ""
686
 
687
+ #: includes/class-freemius.php:24385
688
  msgid "Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!"
689
  msgstr ""
690
 
691
+ #: includes/class-freemius.php:24394
692
  msgid "Learn more"
693
  msgstr ""
694
 
695
+ #: includes/class-freemius.php:24580, templates/account.php:573, templates/account.php:725, templates/connect.php:223, templates/connect.php:449, includes/managers/class-fs-clone-manager.php:1295, templates/forms/license-activation.php:27, templates/account/partials/addon.php:326
696
  msgid "Activate License"
697
  msgstr ""
698
 
699
+ #: includes/class-freemius.php:24581, templates/account.php:667, templates/account.php:724, templates/account/partials/addon.php:327, templates/account/partials/site.php:273
700
  msgid "Change License"
701
  msgstr ""
702
 
703
+ #: includes/class-freemius.php:24688, templates/account/partials/site.php:170
704
  msgid "Opt Out"
705
  msgstr ""
706
 
707
+ #: includes/class-freemius.php:24690, includes/class-freemius.php:24696, templates/account/partials/site.php:49, templates/account/partials/site.php:170
708
  msgid "Opt In"
709
  msgstr ""
710
 
711
+ #: includes/class-freemius.php:24931
712
  msgid " The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s"
713
  msgstr ""
714
 
715
+ #: includes/class-freemius.php:24941
716
  msgid "Activate %s features"
717
  msgstr ""
718
 
719
+ #: includes/class-freemius.php:24954
720
  msgid "Please follow these steps to complete the upgrade"
721
  msgstr ""
722
 
723
+ #: includes/class-freemius.php:24958
724
  msgid "Download the latest %s version"
725
  msgstr ""
726
 
727
+ #: includes/class-freemius.php:24962
728
  msgid "Upload and activate the downloaded version"
729
  msgstr ""
730
 
731
+ #: includes/class-freemius.php:24964
732
  msgid "How to upload and activate?"
733
  msgstr ""
734
 
735
+ #: includes/class-freemius.php:25098
736
  msgid "%sClick here%s to choose the sites where you'd like to activate the license on."
737
  msgstr ""
738
 
739
+ #: includes/class-freemius.php:25267
740
  msgid "Auto installation only works for opted-in users."
741
  msgstr ""
742
 
743
+ #: includes/class-freemius.php:25277, includes/class-freemius.php:25310, includes/class-fs-plugin-updater.php:1284, includes/class-fs-plugin-updater.php:1298
744
  msgid "Invalid module ID."
745
  msgstr ""
746
 
747
+ #: includes/class-freemius.php:25286, includes/class-fs-plugin-updater.php:1320
748
  msgid "Premium version already active."
749
  msgstr ""
750
 
751
+ #: includes/class-freemius.php:25293
752
  msgid "You do not have a valid license to access the premium version."
753
  msgstr ""
754
 
755
+ #: includes/class-freemius.php:25300
756
  msgid "Plugin is a \"Serviceware\" which means it does not have a premium code version."
757
  msgstr ""
758
 
759
+ #: includes/class-freemius.php:25318, includes/class-fs-plugin-updater.php:1319
760
  msgid "Premium add-on version already installed."
761
  msgstr ""
762
 
763
+ #: includes/class-freemius.php:25672
764
  msgid "View paid features"
765
  msgstr ""
766
 
767
+ #: includes/class-freemius.php:25994
768
  msgid "Thank you so much for using %s and its add-ons!"
769
  msgstr ""
770
 
771
+ #: includes/class-freemius.php:25995
772
  msgid "Thank you so much for using %s!"
773
  msgstr ""
774
 
775
+ #: includes/class-freemius.php:26001
776
  msgid "You've already opted-in to our usage-tracking, which helps us keep improving the %s."
777
  msgstr ""
778
 
779
+ #: includes/class-freemius.php:26005
780
  msgid "Thank you so much for using our products!"
781
  msgstr ""
782
 
783
+ #: includes/class-freemius.php:26006
784
  msgid "You've already opted-in to our usage-tracking, which helps us keep improving them."
785
  msgstr ""
786
 
787
+ #: includes/class-freemius.php:26025
788
  msgid "%s and its add-ons"
789
  msgstr ""
790
 
791
+ #: includes/class-freemius.php:26034
792
  msgid "Products"
793
  msgstr ""
794
 
795
+ #: includes/class-freemius.php:26041, templates/connect.php:324
796
  msgid "Yes"
797
  msgstr ""
798
 
799
+ #: includes/class-freemius.php:26042, templates/connect.php:325
800
  msgid "send me security & feature updates, educational content and offers."
801
  msgstr ""
802
 
803
+ #: includes/class-freemius.php:26043, templates/connect.php:330
804
  msgid "No"
805
  msgstr ""
806
 
807
+ #: includes/class-freemius.php:26045, templates/connect.php:332
808
  msgid "do %sNOT%s send me security & feature updates, educational content and offers."
809
  msgstr ""
810
 
811
+ #: includes/class-freemius.php:26055
812
  msgid "Due to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)"
813
  msgstr ""
814
 
815
+ #: includes/class-freemius.php:26057, templates/connect.php:339
816
  msgid "Please let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:"
817
  msgstr ""
818
 
819
+ #: includes/class-freemius.php:26339
820
  msgid "License key is empty."
821
  msgstr ""
822
 
828
  msgid "Buy license"
829
  msgstr ""
830
 
831
+ #: includes/class-fs-plugin-updater.php:331, includes/class-fs-plugin-updater.php:364
832
  msgid "There is a %s of %s available."
833
  msgstr ""
834
 
835
+ #: includes/class-fs-plugin-updater.php:333, includes/class-fs-plugin-updater.php:369
836
  msgid "new Beta version"
837
  msgstr ""
838
 
839
+ #: includes/class-fs-plugin-updater.php:334, includes/class-fs-plugin-updater.php:370
840
  msgid "new version"
841
  msgstr ""
842
 
843
+ #: includes/class-fs-plugin-updater.php:393
844
  msgid "Important Upgrade Notice:"
845
  msgstr ""
846
 
847
+ #: includes/class-fs-plugin-updater.php:1349
848
  msgid "Installing plugin: %s"
849
  msgstr ""
850
 
851
+ #: includes/class-fs-plugin-updater.php:1390
852
  msgid "Unable to connect to the filesystem. Please confirm your credentials."
853
  msgstr ""
854
 
855
+ #: includes/class-fs-plugin-updater.php:1572
856
  msgid "The remote plugin package does not contain a folder with the desired slug and renaming did not work."
857
  msgstr ""
858
 
859
+ #: includes/fs-plugin-info-dialog.php:542
860
  msgid "Purchase More"
861
  msgstr ""
862
 
863
+ #: includes/fs-plugin-info-dialog.php:543, templates/account/partials/addon.php:390
864
  msgctxt "verb"
865
  msgid "Purchase"
866
  msgstr ""
867
 
868
+ #: includes/fs-plugin-info-dialog.php:547
869
  msgid "Start my free %s"
870
  msgstr ""
871
 
872
+ #: includes/fs-plugin-info-dialog.php:745
873
  msgid "Install Free Version Update Now"
874
  msgstr ""
875
 
876
+ #: includes/fs-plugin-info-dialog.php:746, templates/account.php:656
877
  msgid "Install Update Now"
878
  msgstr ""
879
 
880
+ #: includes/fs-plugin-info-dialog.php:755
881
  msgid "Install Free Version Now"
882
  msgstr ""
883
 
884
+ #: includes/fs-plugin-info-dialog.php:756, templates/add-ons.php:323, templates/auto-installation.php:111, templates/account/partials/addon.php:370, templates/account/partials/addon.php:423
885
  msgid "Install Now"
886
  msgstr ""
887
 
888
+ #: includes/fs-plugin-info-dialog.php:772
889
  msgctxt "as download latest version"
890
  msgid "Download Latest Free Version"
891
  msgstr ""
892
 
893
+ #: includes/fs-plugin-info-dialog.php:773, templates/account.php:114, templates/add-ons.php:37, templates/account/partials/addon.php:30
894
  msgctxt "as download latest version"
895
  msgid "Download Latest"
896
  msgstr ""
897
 
898
+ #: includes/fs-plugin-info-dialog.php:788, templates/add-ons.php:329, templates/account/partials/addon.php:361, templates/account/partials/addon.php:417
899
  msgid "Activate this add-on"
900
  msgstr ""
901
 
902
+ #: includes/fs-plugin-info-dialog.php:790, templates/connect.php:446
903
  msgid "Activate Free Version"
904
  msgstr ""
905
 
906
+ #: includes/fs-plugin-info-dialog.php:791, templates/account.php:138, templates/add-ons.php:330, templates/account/partials/addon.php:53
907
  msgid "Activate"
908
  msgstr ""
909
 
910
+ #: includes/fs-plugin-info-dialog.php:1003
911
  msgctxt "Plugin installer section title"
912
  msgid "Description"
913
  msgstr ""
914
 
915
+ #: includes/fs-plugin-info-dialog.php:1004
916
  msgctxt "Plugin installer section title"
917
  msgid "Installation"
918
  msgstr ""
919
 
920
+ #: includes/fs-plugin-info-dialog.php:1005
921
  msgctxt "Plugin installer section title"
922
  msgid "FAQ"
923
  msgstr ""
924
 
925
+ #: includes/fs-plugin-info-dialog.php:1006, templates/plugin-info/description.php:55
926
  msgid "Screenshots"
927
  msgstr ""
928
 
929
+ #: includes/fs-plugin-info-dialog.php:1007
930
  msgctxt "Plugin installer section title"
931
  msgid "Changelog"
932
  msgstr ""
933
 
934
+ #: includes/fs-plugin-info-dialog.php:1008
935
  msgctxt "Plugin installer section title"
936
  msgid "Reviews"
937
  msgstr ""
938
 
939
+ #: includes/fs-plugin-info-dialog.php:1009
940
  msgctxt "Plugin installer section title"
941
  msgid "Other Notes"
942
  msgstr ""
943
 
944
+ #: includes/fs-plugin-info-dialog.php:1024
945
  msgctxt "Plugin installer section title"
946
  msgid "Features & Pricing"
947
  msgstr ""
948
 
949
+ #: includes/fs-plugin-info-dialog.php:1034
950
  msgid "Plugin Install"
951
  msgstr ""
952
 
953
+ #: includes/fs-plugin-info-dialog.php:1106
954
  msgctxt "e.g. Professional Plan"
955
  msgid "%s Plan"
956
  msgstr ""
957
 
958
+ #: includes/fs-plugin-info-dialog.php:1132
959
  msgctxt "e.g. the best product"
960
  msgid "Best"
961
  msgstr ""
962
 
963
+ #: includes/fs-plugin-info-dialog.php:1138, includes/fs-plugin-info-dialog.php:1158
964
  msgctxt "as every month"
965
  msgid "Monthly"
966
  msgstr ""
967
 
968
+ #: includes/fs-plugin-info-dialog.php:1141
969
  msgctxt "as once a year"
970
  msgid "Annual"
971
  msgstr ""
972
 
973
+ #: includes/fs-plugin-info-dialog.php:1144
974
  msgid "Lifetime"
975
  msgstr ""
976
 
977
+ #: includes/fs-plugin-info-dialog.php:1158, includes/fs-plugin-info-dialog.php:1160, includes/fs-plugin-info-dialog.php:1162
978
  msgctxt "e.g. billed monthly"
979
  msgid "Billed %s"
980
  msgstr ""
981
 
982
+ #: includes/fs-plugin-info-dialog.php:1160
983
  msgctxt "as once a year"
984
  msgid "Annually"
985
  msgstr ""
986
 
987
+ #: includes/fs-plugin-info-dialog.php:1162
988
  msgctxt "as once a year"
989
  msgid "Once"
990
  msgstr ""
991
 
992
+ #: includes/fs-plugin-info-dialog.php:1168
993
  msgid "Single Site License"
994
  msgstr ""
995
 
996
+ #: includes/fs-plugin-info-dialog.php:1170
997
  msgid "Unlimited Licenses"
998
  msgstr ""
999
 
1000
+ #: includes/fs-plugin-info-dialog.php:1172
1001
  msgid "Up to %s Sites"
1002
  msgstr ""
1003
 
1004
+ #: includes/fs-plugin-info-dialog.php:1182, templates/plugin-info/features.php:82
1005
  msgctxt "as monthly period"
1006
  msgid "mo"
1007
  msgstr ""
1008
 
1009
+ #: includes/fs-plugin-info-dialog.php:1189, templates/plugin-info/features.php:80
1010
  msgctxt "as annual period"
1011
  msgid "year"
1012
  msgstr ""
1013
 
1014
+ #: includes/fs-plugin-info-dialog.php:1243
1015
  msgctxt "noun"
1016
  msgid "Price"
1017
  msgstr ""
1018
 
1019
+ #: includes/fs-plugin-info-dialog.php:1291
1020
  msgid "Save %s"
1021
  msgstr ""
1022
 
1023
+ #: includes/fs-plugin-info-dialog.php:1301
1024
  msgid "No commitment for %s - cancel anytime"
1025
  msgstr ""
1026
 
1027
+ #: includes/fs-plugin-info-dialog.php:1304
1028
  msgid "After your free %s, pay as little as %s"
1029
  msgstr ""
1030
 
1031
+ #: includes/fs-plugin-info-dialog.php:1315
1032
  msgid "Details"
1033
  msgstr ""
1034
 
1035
+ #: includes/fs-plugin-info-dialog.php:1319, templates/account.php:125, templates/debug.php:232, templates/debug.php:269, templates/debug.php:514, templates/account/partials/addon.php:41
1036
  msgctxt "product version"
1037
  msgid "Version"
1038
  msgstr ""
1039
 
1040
+ #: includes/fs-plugin-info-dialog.php:1326
1041
  msgctxt "as the plugin author"
1042
  msgid "Author"
1043
  msgstr ""
1044
 
1045
+ #: includes/fs-plugin-info-dialog.php:1333
1046
  msgid "Last Updated"
1047
  msgstr ""
1048
 
1049
+ #: includes/fs-plugin-info-dialog.php:1338, templates/account.php:544
1050
  msgctxt "x-ago"
1051
  msgid "%s ago"
1052
  msgstr ""
1053
 
1054
+ #: includes/fs-plugin-info-dialog.php:1347
1055
  msgid "Requires WordPress Version"
1056
  msgstr ""
1057
 
1058
+ #: includes/fs-plugin-info-dialog.php:1350, includes/fs-plugin-info-dialog.php:1370
1059
  msgid "%s or higher"
1060
  msgstr ""
1061
 
1062
+ #: includes/fs-plugin-info-dialog.php:1358
1063
  msgid "Compatible up to"
1064
  msgstr ""
1065
 
1066
+ #: includes/fs-plugin-info-dialog.php:1366
1067
+ msgid "Requires PHP Version"
1068
+ msgstr ""
1069
+
1070
+ #: includes/fs-plugin-info-dialog.php:1379
1071
  msgid "Downloaded"
1072
  msgstr ""
1073
 
1074
+ #: includes/fs-plugin-info-dialog.php:1383
1075
  msgid "%s time"
1076
  msgstr ""
1077
 
1078
+ #: includes/fs-plugin-info-dialog.php:1385
1079
  msgid "%s times"
1080
  msgstr ""
1081
 
1082
+ #: includes/fs-plugin-info-dialog.php:1396
1083
  msgid "WordPress.org Plugin Page"
1084
  msgstr ""
1085
 
1086
+ #: includes/fs-plugin-info-dialog.php:1405
1087
  msgid "Plugin Homepage"
1088
  msgstr ""
1089
 
1090
+ #: includes/fs-plugin-info-dialog.php:1414, includes/fs-plugin-info-dialog.php:1498
1091
  msgid "Donate to this plugin"
1092
  msgstr ""
1093
 
1094
+ #: includes/fs-plugin-info-dialog.php:1421
1095
  msgid "Average Rating"
1096
  msgstr ""
1097
 
1098
+ #: includes/fs-plugin-info-dialog.php:1428
1099
  msgid "based on %s"
1100
  msgstr ""
1101
 
1102
+ #: includes/fs-plugin-info-dialog.php:1432
1103
  msgid "%s rating"
1104
  msgstr ""
1105
 
1106
+ #: includes/fs-plugin-info-dialog.php:1434
1107
  msgid "%s ratings"
1108
  msgstr ""
1109
 
1110
+ #: includes/fs-plugin-info-dialog.php:1449
1111
  msgid "%s star"
1112
  msgstr ""
1113
 
1114
+ #: includes/fs-plugin-info-dialog.php:1451
1115
  msgid "%s stars"
1116
  msgstr ""
1117
 
1118
+ #: includes/fs-plugin-info-dialog.php:1463
1119
  msgid "Click to see reviews that provided a rating of %s"
1120
  msgstr ""
1121
 
1122
+ #: includes/fs-plugin-info-dialog.php:1476
1123
  msgid "Contributors"
1124
  msgstr ""
1125
 
1126
+ #: includes/fs-plugin-info-dialog.php:1517
1127
+ msgid "This plugin requires a newer version of PHP."
1128
+ msgstr ""
1129
+
1130
+ #: includes/fs-plugin-info-dialog.php:1526
1131
+ msgid "<a href=\"%s\" target=\"_blank\">Click here to learn more about updating PHP</a>."
1132
+ msgstr ""
1133
+
1134
+ #: includes/fs-plugin-info-dialog.php:1540, includes/fs-plugin-info-dialog.php:1542
1135
  msgid "Warning"
1136
  msgstr ""
1137
 
1138
+ #: includes/fs-plugin-info-dialog.php:1540
1139
  msgid "This plugin has not been tested with your current version of WordPress."
1140
  msgstr ""
1141
 
1142
+ #: includes/fs-plugin-info-dialog.php:1542
1143
  msgid "This plugin has not been marked as compatible with your version of WordPress."
1144
  msgstr ""
1145
 
1146
+ #: includes/fs-plugin-info-dialog.php:1561
1147
  msgid "Paid add-on must be deployed to Freemius."
1148
  msgstr ""
1149
 
1150
+ #: includes/fs-plugin-info-dialog.php:1562
1151
  msgid "Add-on must be deployed to WordPress.org or Freemius."
1152
  msgstr ""
1153
 
1154
+ #: includes/fs-plugin-info-dialog.php:1583
1155
  msgid "Newer Version (%s) Installed"
1156
  msgstr ""
1157
 
1158
+ #: includes/fs-plugin-info-dialog.php:1584
1159
  msgid "Newer Free Version (%s) Installed"
1160
  msgstr ""
1161
 
1162
+ #: includes/fs-plugin-info-dialog.php:1591
1163
  msgid "Latest Version Installed"
1164
  msgstr ""
1165
 
1166
+ #: includes/fs-plugin-info-dialog.php:1592
1167
  msgid "Latest Free Version Installed"
1168
  msgstr ""
1169
 
1170
+ #: templates/account.php:115, templates/forms/subscription-cancellation.php:96, templates/account/partials/addon.php:31, templates/account/partials/site.php:313
1171
  msgid "Downgrading your plan"
1172
  msgstr ""
1173
 
1174
+ #: templates/account.php:116, templates/forms/subscription-cancellation.php:97, templates/account/partials/addon.php:32, templates/account/partials/site.php:314
1175
  msgid "Cancelling the subscription"
1176
  msgstr ""
1177
 
1178
  #. translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription'
1179
+ #: templates/account.php:118, templates/forms/subscription-cancellation.php:99, templates/account/partials/site.php:316
1180
  msgid "%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s."
1181
  msgstr ""
1182
 
1183
+ #: templates/account.php:119, templates/forms/subscription-cancellation.php:100, templates/account/partials/addon.php:35, templates/account/partials/site.php:317
1184
  msgid "Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price."
1185
  msgstr ""
1186
 
1187
+ #: templates/account.php:120, templates/forms/subscription-cancellation.php:106, templates/account/partials/addon.php:36
1188
  msgid "Cancelling the trial will immediately block access to all premium features. Are you sure?"
1189
  msgstr ""
1190
 
1191
+ #: templates/account.php:121, templates/forms/subscription-cancellation.php:101, templates/account/partials/addon.php:37, templates/account/partials/site.php:318
1192
  msgid "You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support."
1193
  msgstr ""
1194
 
1195
+ #: templates/account.php:122, templates/forms/subscription-cancellation.php:102, templates/account/partials/addon.php:38, templates/account/partials/site.php:319
1196
  msgid "Once your license expires you can still use the Free version but you will NOT have access to the %s features."
1197
  msgstr ""
1198
 
1199
  #. translators: %s: Plan title (e.g. "Professional")
1200
+ #: templates/account.php:124, templates/account/partials/activate-license-button.php:31, templates/account/partials/addon.php:40
1201
  msgid "Activate %s Plan"
1202
  msgstr ""
1203
 
1204
  #. translators: %s: Time period (e.g. Auto renews in "2 months")
1205
+ #: templates/account.php:127, templates/account/partials/addon.php:43, templates/account/partials/site.php:293
1206
  msgid "Auto renews in %s"
1207
  msgstr ""
1208
 
1209
  #. translators: %s: Time period (e.g. Expires in "2 months")
1210
+ #: templates/account.php:129, templates/account/partials/addon.php:45, templates/account/partials/site.php:295
1211
  msgid "Expires in %s"
1212
  msgstr ""
1213
 
1214
+ #: templates/account.php:130
1215
  msgctxt "as synchronize license"
1216
  msgid "Sync License"
1217
  msgstr ""
1218
 
1219
+ #: templates/account.php:131, templates/account/partials/addon.php:46
1220
  msgid "Cancel Trial"
1221
  msgstr ""
1222
 
1223
+ #: templates/account.php:132, templates/account/partials/addon.php:47
1224
  msgid "Change Plan"
1225
  msgstr ""
1226
 
1227
+ #: templates/account.php:133, templates/account/partials/addon.php:48
1228
  msgctxt "verb"
1229
  msgid "Upgrade"
1230
  msgstr ""
1231
 
1232
+ #: templates/account.php:135, templates/account/partials/addon.php:50, templates/account/partials/site.php:320
1233
  msgctxt "verb"
1234
  msgid "Downgrade"
1235
  msgstr ""
1236
 
1237
+ #: templates/account.php:137, templates/add-ons.php:246, templates/plugin-info/features.php:72, templates/account/partials/addon.php:52, templates/account/partials/site.php:33
1238
  msgid "Free"
1239
  msgstr ""
1240
 
1241
+ #: templates/account.php:139, templates/debug.php:408, includes/customizer/class-fs-customizer-upsell-control.php:110, templates/account/partials/addon.php:54
1242
  msgctxt "as product pricing plan"
1243
  msgid "Plan"
1244
  msgstr ""
1245
 
1246
+ #: templates/account.php:140
1247
  msgid "Bundle Plan"
1248
  msgstr ""
1249
 
1250
+ #: templates/account.php:272
1251
  msgid "Free Trial"
1252
  msgstr ""
1253
 
1254
+ #: templates/account.php:283
1255
  msgid "Account Details"
1256
  msgstr ""
1257
 
1258
+ #: templates/account.php:290, templates/forms/data-debug-mode.php:33
1259
  msgid "Start Debug"
1260
  msgstr ""
1261
 
1262
+ #: templates/account.php:292
1263
  msgid "Stop Debug"
1264
  msgstr ""
1265
 
1266
+ #: templates/account.php:299
1267
  msgid "Billing & Invoices"
1268
  msgstr ""
1269
 
1270
+ #: templates/account.php:322, templates/account/partials/addon.php:236, templates/account/partials/deactivate-license-button.php:35
 
 
 
 
 
 
 
 
 
 
 
 
1271
  msgid "Deactivate License"
1272
  msgstr ""
1273
 
1274
+ #: templates/account.php:345, templates/forms/subscription-cancellation.php:125
1275
  msgid "Are you sure you want to proceed?"
1276
  msgstr ""
1277
 
1278
+ #: templates/account.php:345, templates/account/partials/addon.php:260
1279
  msgid "Cancel Subscription"
1280
  msgstr ""
1281
 
1282
+ #: templates/account.php:374, templates/account/partials/addon.php:345
1283
  msgctxt "as synchronize"
1284
  msgid "Sync"
1285
  msgstr ""
1286
 
1287
+ #: templates/account.php:389, templates/debug.php:571
1288
  msgid "Name"
1289
  msgstr ""
1290
 
1291
+ #: templates/account.php:395, templates/debug.php:572
1292
  msgid "Email"
1293
  msgstr ""
1294
 
1295
+ #: templates/account.php:402, templates/debug.php:406, templates/debug.php:621
1296
  msgid "User ID"
1297
  msgstr ""
1298
 
1299
+ #: templates/account.php:420, templates/account.php:738, templates/account.php:789, templates/debug.php:267, templates/debug.php:400, templates/debug.php:511, templates/debug.php:570, templates/debug.php:619, templates/debug.php:698, templates/account/payments.php:35, templates/debug/logger.php:21
1300
  msgid "ID"
1301
  msgstr ""
1302
 
1303
+ #: templates/account.php:427
1304
  msgid "Site ID"
1305
  msgstr ""
1306
 
1307
+ #: templates/account.php:430
1308
  msgid "No ID"
1309
  msgstr ""
1310
 
1311
+ #: templates/account.php:435, templates/debug.php:274, templates/debug.php:409, templates/debug.php:515, templates/debug.php:574, templates/account/partials/site.php:228
1312
  msgid "Public Key"
1313
  msgstr ""
1314
 
1315
+ #: templates/account.php:441, templates/debug.php:410, templates/debug.php:516, templates/debug.php:575, templates/account/partials/site.php:241
1316
  msgid "Secret Key"
1317
  msgstr ""
1318
 
1319
+ #: templates/account.php:444
1320
  msgctxt "as secret encryption key missing"
1321
  msgid "No Secret"
1322
  msgstr ""
1323
 
1324
+ #: templates/account.php:471, templates/account/partials/site.php:120, templates/account/partials/site.php:122
1325
  msgid "Trial"
1326
  msgstr ""
1327
 
1328
+ #: templates/account.php:498, templates/debug.php:627, templates/account/partials/site.php:262
1329
  msgid "License Key"
1330
  msgstr ""
1331
 
1332
+ #: templates/account.php:529
1333
  msgid "Join the Beta program"
1334
  msgstr ""
1335
 
1336
+ #: templates/account.php:535
1337
  msgid "not verified"
1338
  msgstr ""
1339
 
1340
+ #: templates/account.php:544, templates/account/partials/addon.php:195
1341
  msgid "Expired"
1342
  msgstr ""
1343
 
1344
+ #: templates/account.php:602
1345
  msgid "Premium version"
1346
  msgstr ""
1347
 
1348
+ #: templates/account.php:604
1349
  msgid "Free version"
1350
  msgstr ""
1351
 
1352
+ #: templates/account.php:616
1353
  msgid "Verify Email"
1354
  msgstr ""
1355
 
1356
+ #: templates/account.php:630
1357
  msgid "Download %s Version"
1358
  msgstr ""
1359
 
1360
+ #: templates/account.php:646
1361
  msgid "Download Paid Version"
1362
  msgstr ""
1363
 
1364
+ #: templates/account.php:664, templates/account.php:927, templates/account/partials/site.php:250, templates/account/partials/site.php:272
1365
  msgctxt "verb"
1366
  msgid "Show"
1367
  msgstr ""
1368
 
1369
+ #: templates/account.php:680
1370
  msgid "What is your %s?"
1371
  msgstr ""
1372
 
1373
+ #: templates/account.php:688, templates/account/billing.php:21
1374
  msgctxt "verb"
1375
  msgid "Edit"
1376
  msgstr ""
1377
 
1378
+ #: templates/account.php:693, templates/forms/user-change.php:27
1379
  msgid "Change User"
1380
  msgstr ""
1381
 
1382
+ #: templates/account.php:717
1383
  msgid "Sites"
1384
  msgstr ""
1385
 
1386
+ #: templates/account.php:730
1387
  msgid "Search by address"
1388
  msgstr ""
1389
 
1390
+ #: templates/account.php:739, templates/debug.php:403
1391
  msgid "Address"
1392
  msgstr ""
1393
 
1394
+ #: templates/account.php:740
1395
  msgid "License"
1396
  msgstr ""
1397
 
1398
+ #: templates/account.php:741
1399
  msgid "Plan"
1400
  msgstr ""
1401
 
1402
+ #: templates/account.php:792
1403
  msgctxt "as software license"
1404
  msgid "License"
1405
  msgstr ""
1406
 
1407
+ #: templates/account.php:921
1408
  msgctxt "verb"
1409
  msgid "Hide"
1410
  msgstr ""
1411
 
1412
+ #: templates/account.php:943, templates/forms/data-debug-mode.php:31, templates/forms/deactivation/form.php:358, templates/forms/deactivation/form.php:389
1413
  msgid "Processing"
1414
  msgstr ""
1415
 
1416
+ #: templates/account.php:946
1417
  msgid "Get updates for bleeding edge Beta versions of %s."
1418
  msgstr ""
1419
 
1420
+ #: templates/account.php:1004
1421
  msgid "Cancelling %s"
1422
  msgstr ""
1423
 
1424
+ #: templates/account.php:1004, templates/account.php:1021, templates/forms/subscription-cancellation.php:27, templates/forms/deactivation/form.php:178
1425
  msgid "trial"
1426
  msgstr ""
1427
 
1428
+ #: templates/account.php:1019, templates/forms/deactivation/form.php:195
1429
  msgid "Cancelling %s..."
1430
  msgstr ""
1431
 
1432
+ #: templates/account.php:1022, templates/forms/subscription-cancellation.php:28, templates/forms/deactivation/form.php:179
1433
  msgid "subscription"
1434
  msgstr ""
1435
 
1436
+ #: templates/account.php:1036
1437
  msgid "Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?"
1438
  msgstr ""
1439
 
1440
+ #: templates/account.php:1110
1441
  msgid "Disabling white-label mode"
1442
  msgstr ""
1443
 
1444
+ #: templates/account.php:1111
1445
  msgid "Enabling white-label mode"
1446
  msgstr ""
1447
 
1467
  msgid "Installed"
1468
  msgstr ""
1469
 
1470
+ #: templates/admin-notice.php:13, templates/forms/license-activation.php:243, templates/forms/resend-key.php:80
1471
  msgctxt "as close a window"
1472
  msgid "Dismiss"
1473
  msgstr ""
1501
  msgstr ""
1502
 
1503
  #. translators: %s: name (e.g. Hey John,)
1504
+ #: templates/connect.php:121
1505
  msgctxt "greeting"
1506
  msgid "Hey %s,"
1507
  msgstr ""
1508
 
1509
+ #: templates/connect.php:189
1510
+ msgid "Never miss an important update"
1511
+ msgstr ""
1512
+
1513
+ #: templates/connect.php:197
1514
+ msgid "Thank you for updating to %1$s v%2$s!"
1515
+ msgstr ""
1516
+
1517
+ #: templates/connect.php:207
1518
  msgid "Allow & Continue"
1519
  msgstr ""
1520
 
1521
+ #: templates/connect.php:211
1522
  msgid "Re-send activation email"
1523
  msgstr ""
1524
 
1525
+ #: templates/connect.php:215
1526
  msgid "Thanks %s!"
1527
  msgstr ""
1528
 
1529
+ #: templates/connect.php:227
1530
+ msgid "Welcome to %s! To get started, please enter your license key:"
1531
  msgstr ""
1532
 
1533
+ #: templates/connect.php:237
1534
+ msgid "Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to."
1535
  msgstr ""
1536
 
1537
+ #: templates/connect.php:239
1538
+ msgid "Opt in to get email notifications for security & feature updates, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to."
1539
+ msgstr ""
1540
+
1541
+ #. translators: %s: module type (plugin, theme, or add-on)
1542
+ #: templates/connect.php:245
1543
+ msgid "We have introduced this opt-in so you never miss an important update and help us make the %s more compatible with your site and better at doing what you need it to."
1544
  msgstr ""
1545
 
1546
+ #: templates/connect.php:248
1547
+ msgid "Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info."
1548
  msgstr ""
1549
 
1550
+ #: templates/connect.php:249
1551
+ msgid "Opt in to get email notifications for security & feature updates, and to share some basic WordPress environment info."
1552
  msgstr ""
1553
 
1554
+ #: templates/connect.php:252
1555
+ msgid "If you skip this, that's okay! %1$s will still work just fine."
1556
  msgstr ""
1557
 
1558
+ #: templates/connect.php:282
1559
  msgid "We're excited to introduce the Freemius network-level integration."
1560
  msgstr ""
1561
 
1562
+ #: templates/connect.php:285
1563
  msgid "During the update process we detected %d site(s) that are still pending license activation."
1564
  msgstr ""
1565
 
1566
+ #: templates/connect.php:287
1567
  msgid "If you'd like to use the %s on those sites, please enter your license key below and click the activation button."
1568
  msgstr ""
1569
 
1570
+ #: templates/connect.php:289
1571
  msgid "%s's paid features"
1572
  msgstr ""
1573
 
1574
+ #: templates/connect.php:294
1575
  msgid "Alternatively, you can skip it for now and activate the license later, in your %s's network-level Account page."
1576
  msgstr ""
1577
 
1578
+ #: templates/connect.php:296
1579
  msgid "During the update process we detected %s site(s) in the network that are still pending your attention."
1580
  msgstr ""
1581
 
1582
+ #: templates/connect.php:305, templates/forms/data-debug-mode.php:35, templates/forms/license-activation.php:42
1583
  msgid "License key"
1584
  msgstr ""
1585
 
1586
+ #: templates/connect.php:308, templates/forms/license-activation.php:22
1587
  msgid "Can't find your license key?"
1588
  msgstr ""
1589
 
1590
+ #: templates/connect.php:371, templates/connect.php:695, templates/forms/deactivation/retry-skip.php:20
1591
  msgctxt "verb"
1592
  msgid "Skip"
1593
  msgstr ""
1594
 
1595
+ #: templates/connect.php:374
1596
  msgid "Delegate to Site Admins"
1597
  msgstr ""
1598
 
1599
+ #: templates/connect.php:374
1600
  msgid "If you click it, this decision will be delegated to the sites administrators."
1601
  msgstr ""
1602
 
1603
+ #: templates/connect.php:401
1604
  msgid "License issues?"
1605
  msgstr ""
1606
 
1607
+ #: templates/connect.php:425
1608
+ msgid "For delivery of security & feature updates, and license management, %s needs to"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1609
  msgstr ""
1610
 
1611
  #: templates/connect.php:430
1612
+ msgid "This will allow %s to"
1613
  msgstr ""
1614
 
1615
+ #: templates/connect.php:445
1616
  msgid "Don't have a license key?"
1617
  msgstr ""
1618
 
1619
+ #: templates/connect.php:448
1620
  msgid "Have a license key?"
1621
  msgstr ""
1622
 
1623
+ #: templates/connect.php:456
1624
+ msgid "Freemius is our licensing and software updates engine"
1625
+ msgstr ""
1626
+
1627
+ #: templates/connect.php:459
1628
  msgid "Privacy Policy"
1629
  msgstr ""
1630
 
1631
+ #: templates/connect.php:461
1632
  msgid "License Agreement"
1633
  msgstr ""
1634
 
1635
+ #: templates/connect.php:461
1636
  msgid "Terms of Service"
1637
  msgstr ""
1638
 
1639
+ #: templates/connect.php:881
1640
  msgctxt "as in the process of sending an email"
1641
  msgid "Sending email"
1642
  msgstr ""
1643
 
1644
+ #: templates/connect.php:882
1645
  msgctxt "as activating plugin"
1646
  msgid "Activating"
1647
  msgstr ""
1660
  msgid "On"
1661
  msgstr ""
1662
 
1663
+ #: templates/debug.php:24
1664
  msgid "SDK"
1665
  msgstr ""
1666
 
1667
+ #: templates/debug.php:28
1668
  msgctxt "as code debugging"
1669
  msgid "Debugging"
1670
  msgstr ""
1671
 
1672
+ #: templates/debug.php:58, templates/debug.php:279, templates/debug.php:411, templates/debug.php:576
1673
  msgid "Actions"
1674
  msgstr ""
1675
 
1676
+ #: templates/debug.php:68
1677
  msgid "Are you sure you want to delete all Freemius data?"
1678
  msgstr ""
1679
 
1680
+ #: templates/debug.php:68
1681
  msgid "Delete All Accounts"
1682
  msgstr ""
1683
 
1684
+ #: templates/debug.php:75
1685
  msgid "Clear API Cache"
1686
  msgstr ""
1687
 
1688
+ #: templates/debug.php:83
1689
  msgid "Clear Updates Transients"
1690
  msgstr ""
1691
 
1692
+ #: templates/debug.php:92
1693
+ msgid "Reset Deactivation Snoozing"
1694
+ msgstr ""
1695
+
1696
+ #: templates/debug.php:100
1697
  msgid "Sync Data From Server"
1698
  msgstr ""
1699
 
1700
+ #: templates/debug.php:109
1701
  msgid "Migrate Options to Network"
1702
  msgstr ""
1703
 
1704
+ #: templates/debug.php:114
1705
  msgid "Load DB Option"
1706
  msgstr ""
1707
 
1708
+ #: templates/debug.php:117
1709
  msgid "Set DB Option"
1710
  msgstr ""
1711
 
1712
+ #: templates/debug.php:211
1713
  msgid "Key"
1714
  msgstr ""
1715
 
1716
+ #: templates/debug.php:212
1717
  msgid "Value"
1718
  msgstr ""
1719
 
1720
+ #: templates/debug.php:228
1721
  msgctxt "as software development kit versions"
1722
  msgid "SDK Versions"
1723
  msgstr ""
1724
 
1725
+ #: templates/debug.php:233
1726
  msgid "SDK Path"
1727
  msgstr ""
1728
 
1729
+ #: templates/debug.php:234, templates/debug.php:273
1730
  msgid "Module Path"
1731
  msgstr ""
1732
 
1733
+ #: templates/debug.php:235
1734
  msgid "Is Active"
1735
  msgstr ""
1736
 
1737
+ #: templates/debug.php:263, templates/debug/plugins-themes-sync.php:35
1738
  msgid "Plugins"
1739
  msgstr ""
1740
 
1741
+ #: templates/debug.php:263, templates/debug/plugins-themes-sync.php:56
1742
  msgid "Themes"
1743
  msgstr ""
1744
 
1745
+ #: templates/debug.php:268, templates/debug.php:405, templates/debug.php:513, templates/debug/scheduled-crons.php:80
1746
  msgid "Slug"
1747
  msgstr ""
1748
 
1749
+ #: templates/debug.php:270, templates/debug.php:512
1750
  msgid "Title"
1751
  msgstr ""
1752
 
1753
+ #: templates/debug.php:271
1754
  msgctxt "as application program interface"
1755
  msgid "API"
1756
  msgstr ""
1757
 
1758
+ #: templates/debug.php:272
1759
  msgid "Freemius State"
1760
  msgstr ""
1761
 
1762
+ #: templates/debug.php:276
1763
  msgid "Network Blog"
1764
  msgstr ""
1765
 
1766
+ #: templates/debug.php:277
1767
  msgid "Network User"
1768
  msgstr ""
1769
 
1770
+ #: templates/debug.php:321
1771
  msgctxt "as connection was successful"
1772
  msgid "Connected"
1773
  msgstr ""
1774
 
1775
+ #: templates/debug.php:322
1776
  msgctxt "as connection blocked"
1777
  msgid "Blocked"
1778
  msgstr ""
1779
 
1780
+ #: templates/debug.php:358
1781
  msgid "Simulate Trial Promotion"
1782
  msgstr ""
1783
 
1784
+ #: templates/debug.php:370
1785
  msgid "Simulate Network Upgrade"
1786
  msgstr ""
1787
 
1788
+ #: templates/debug.php:394
1789
  msgid "%s Installs"
1790
  msgstr ""
1791
 
1792
+ #: templates/debug.php:396
1793
  msgctxt "like websites"
1794
  msgid "Sites"
1795
  msgstr ""
1796
 
1797
+ #: templates/debug.php:402, templates/account/partials/site.php:156
1798
  msgid "Blog ID"
1799
  msgstr ""
1800
 
1801
+ #: templates/debug.php:407
1802
  msgid "License ID"
1803
  msgstr ""
1804
 
1805
+ #: templates/debug.php:493, templates/debug.php:599, templates/account/partials/addon.php:440
1806
  msgctxt "verb"
1807
  msgid "Delete"
1808
  msgstr ""
1809
 
1810
+ #: templates/debug.php:507
1811
  msgid "Add Ons of module %s"
1812
  msgstr ""
1813
 
1814
+ #: templates/debug.php:566
1815
  msgid "Users"
1816
  msgstr ""
1817
 
1818
+ #: templates/debug.php:573
1819
  msgid "Verified"
1820
  msgstr ""
1821
 
1822
+ #: templates/debug.php:615
1823
  msgid "%s Licenses"
1824
  msgstr ""
1825
 
1826
+ #: templates/debug.php:620
1827
  msgid "Plugin ID"
1828
  msgstr ""
1829
 
1830
+ #: templates/debug.php:622
1831
  msgid "Plan ID"
1832
  msgstr ""
1833
 
1834
+ #: templates/debug.php:623
1835
  msgid "Quota"
1836
  msgstr ""
1837
 
1838
+ #: templates/debug.php:624
1839
  msgid "Activated"
1840
  msgstr ""
1841
 
1842
+ #: templates/debug.php:625
1843
  msgid "Blocking"
1844
  msgstr ""
1845
 
1846
+ #: templates/debug.php:626, templates/debug.php:697, templates/debug/logger.php:22
1847
  msgid "Type"
1848
  msgstr ""
1849
 
1850
+ #: templates/debug.php:628
1851
  msgctxt "as expiration date"
1852
  msgid "Expiration"
1853
  msgstr ""
1854
 
1855
+ #: templates/debug.php:656
1856
  msgid "Debug Log"
1857
  msgstr ""
1858
 
1859
+ #: templates/debug.php:660
1860
  msgid "All Types"
1861
  msgstr ""
1862
 
1863
+ #: templates/debug.php:667
1864
  msgid "All Requests"
1865
  msgstr ""
1866
 
1867
+ #: templates/debug.php:672, templates/debug.php:701, templates/debug/logger.php:25
1868
  msgid "File"
1869
  msgstr ""
1870
 
1871
+ #: templates/debug.php:673, templates/debug.php:699, templates/debug/logger.php:23
1872
  msgid "Function"
1873
  msgstr ""
1874
 
1875
+ #: templates/debug.php:674
1876
  msgid "Process ID"
1877
  msgstr ""
1878
 
1879
+ #: templates/debug.php:675
1880
  msgid "Logger"
1881
  msgstr ""
1882
 
1883
+ #: templates/debug.php:676, templates/debug.php:700, templates/debug/logger.php:24
1884
  msgid "Message"
1885
  msgstr ""
1886
 
1887
+ #: templates/debug.php:678
1888
  msgid "Filter"
1889
  msgstr ""
1890
 
1891
+ #: templates/debug.php:686
1892
  msgid "Download"
1893
  msgstr ""
1894
 
1895
+ #: templates/debug.php:702, templates/debug/logger.php:26
1896
  msgid "Timestamp"
1897
  msgstr ""
1898
 
1917
  msgid "Requests"
1918
  msgstr ""
1919
 
1920
+ #: includes/managers/class-fs-clone-manager.php:839
1921
+ msgid "Invalid clone resolution action."
1922
+ msgstr ""
1923
+
1924
+ #: includes/managers/class-fs-clone-manager.php:1024
1925
+ msgid "products"
1926
+ msgstr ""
1927
+
1928
+ #: includes/managers/class-fs-clone-manager.php:1205
1929
+ msgid "%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s."
1930
+ msgstr ""
1931
+
1932
+ #: includes/managers/class-fs-clone-manager.php:1211
1933
+ msgid "The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$s"
1934
+ msgstr ""
1935
+
1936
+ #: includes/managers/class-fs-clone-manager.php:1212
1937
+ msgid "The products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$s"
1938
+ msgstr ""
1939
+
1940
+ #: includes/managers/class-fs-clone-manager.php:1238
1941
+ msgid "the above-mentioned sites"
1942
+ msgstr ""
1943
+
1944
+ #: includes/managers/class-fs-clone-manager.php:1251
1945
+ msgid "Is %2$s a duplicate of %4$s?"
1946
+ msgstr ""
1947
+
1948
+ #: includes/managers/class-fs-clone-manager.php:1252
1949
+ msgid "Yes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development."
1950
+ msgstr ""
1951
+
1952
+ #: includes/managers/class-fs-clone-manager.php:1257
1953
+ msgid "Long-Term Duplicate"
1954
+ msgstr ""
1955
+
1956
+ #: includes/managers/class-fs-clone-manager.php:1262
1957
+ msgid "Duplicate Website"
1958
+ msgstr ""
1959
+
1960
+ #: includes/managers/class-fs-clone-manager.php:1268
1961
+ msgid "Is %2$s the new home of %4$s?"
1962
+ msgstr ""
1963
+
1964
+ #: includes/managers/class-fs-clone-manager.php:1270
1965
+ msgid "Yes, %%2$s is replacing %%4$s. I would like to migrate my %s from %%4$s to %%2$s."
1966
+ msgstr ""
1967
+
1968
+ #: includes/managers/class-fs-clone-manager.php:1271, templates/forms/subscription-cancellation.php:52
1969
+ msgid "license"
1970
+ msgstr ""
1971
+
1972
+ #: includes/managers/class-fs-clone-manager.php:1271
1973
+ msgid "data"
1974
+ msgstr ""
1975
+
1976
+ #: includes/managers/class-fs-clone-manager.php:1277
1977
+ msgid "Migrate License"
1978
+ msgstr ""
1979
+
1980
+ #: includes/managers/class-fs-clone-manager.php:1278
1981
+ msgid "Migrate"
1982
+ msgstr ""
1983
+
1984
+ #: includes/managers/class-fs-clone-manager.php:1284
1985
+ msgid "Is %2$s a new website?"
1986
+ msgstr ""
1987
+
1988
+ #: includes/managers/class-fs-clone-manager.php:1285
1989
+ msgid "Yes, %2$s is a new and different website that is separate from %4$s."
1990
+ msgstr ""
1991
+
1992
+ #: includes/managers/class-fs-clone-manager.php:1287
1993
+ msgid "It requires license activation."
1994
+ msgstr ""
1995
+
1996
+ #: includes/managers/class-fs-clone-manager.php:1294
1997
+ msgid "New Website"
1998
+ msgstr ""
1999
+
2000
+ #: includes/managers/class-fs-clone-manager.php:1319
2001
+ msgctxt "Clone resolution admin notice products list label"
2002
+ msgid "Products"
2003
+ msgstr ""
2004
+
2005
+ #: includes/managers/class-fs-clone-manager.php:1408
2006
+ msgid "You marked this website, %s, as a temporary duplicate of %s."
2007
+ msgstr ""
2008
+
2009
+ #: includes/managers/class-fs-clone-manager.php:1409
2010
+ msgid "You marked this website, %s, as a temporary duplicate of these sites"
2011
+ msgstr ""
2012
+
2013
+ #: includes/managers/class-fs-clone-manager.php:1423
2014
+ msgid "%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first)."
2015
+ msgstr ""
2016
+
2017
+ #: includes/managers/class-fs-clone-manager.php:1426
2018
+ msgctxt "\"The <product_label>\", e.g.: \"The plugin\""
2019
+ msgid "The %s's"
2020
+ msgstr ""
2021
+
2022
+ #: includes/managers/class-fs-clone-manager.php:1429
2023
+ msgid "The following products'"
2024
+ msgstr ""
2025
+
2026
+ #: includes/managers/class-fs-clone-manager.php:1437
2027
+ msgid "If this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s."
2028
+ msgstr ""
2029
+
2030
+ #: includes/managers/class-fs-clone-manager.php:1439
2031
+ msgid "activate a license here"
2032
+ msgstr ""
2033
+
2034
+ #: includes/managers/class-fs-permission-manager.php:182
2035
+ msgid "View Basic Website Info"
2036
+ msgstr ""
2037
+
2038
+ #: includes/managers/class-fs-permission-manager.php:183
2039
+ msgid "Homepage URL & title, WP & PHP versions, and site language"
2040
+ msgstr ""
2041
+
2042
+ #: includes/managers/class-fs-permission-manager.php:186
2043
+ msgid "To provide additional functionality that's relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to."
2044
+ msgstr ""
2045
+
2046
+ #: includes/managers/class-fs-permission-manager.php:198
2047
+ msgid "View Basic %s Info"
2048
+ msgstr ""
2049
+
2050
+ #: includes/managers/class-fs-permission-manager.php:201
2051
+ msgid "Current %s & SDK versions, and if active or uninstalled"
2052
+ msgstr ""
2053
+
2054
+ #: includes/managers/class-fs-permission-manager.php:252
2055
+ msgid "View License Essentials"
2056
+ msgstr ""
2057
+
2058
+ #: includes/managers/class-fs-permission-manager.php:253
2059
+ msgstr ""
2060
+
2061
+ #: includes/managers/class-fs-permission-manager.php:263
2062
+ msgid "To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize."
2063
+ msgstr ""
2064
+
2065
+ #: includes/managers/class-fs-permission-manager.php:275
2066
+ msgid "View %s State"
2067
+ msgstr ""
2068
+
2069
+ #: includes/managers/class-fs-permission-manager.php:278
2070
+ msgid "Is active, deactivated, or uninstalled"
2071
+ msgstr ""
2072
+
2073
+ #: includes/managers/class-fs-permission-manager.php:281
2074
+ msgid "So you can reuse the license when the %s is no longer active."
2075
+ msgstr ""
2076
+
2077
+ #: includes/managers/class-fs-permission-manager.php:317
2078
+ msgid "View Diagnostic Info"
2079
+ msgstr ""
2080
+
2081
+ #: includes/managers/class-fs-permission-manager.php:317, includes/managers/class-fs-permission-manager.php:354
2082
+ msgid "optional"
2083
+ msgstr ""
2084
+
2085
+ #: includes/managers/class-fs-permission-manager.php:318
2086
+ msgid "WordPress & PHP versions, site language & title"
2087
+ msgstr ""
2088
+
2089
+ #: includes/managers/class-fs-permission-manager.php:321
2090
+ msgid "To avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to."
2091
+ msgstr ""
2092
+
2093
+ #: includes/managers/class-fs-permission-manager.php:354
2094
+ msgid "View Plugins & Themes List"
2095
+ msgstr ""
2096
+
2097
+ #: includes/managers/class-fs-permission-manager.php:355
2098
+ msgid "Names, slugs, versions, and if active or not"
2099
+ msgstr ""
2100
+
2101
+ #: includes/managers/class-fs-permission-manager.php:356
2102
+ msgid "To ensure compatibility and avoid conflicts with your installed plugins and themes."
2103
+ msgstr ""
2104
+
2105
+ #: includes/managers/class-fs-permission-manager.php:373
2106
+ msgid "View Basic Profile Info"
2107
+ msgstr ""
2108
+
2109
+ #: includes/managers/class-fs-permission-manager.php:374
2110
+ msgid "Your WordPress user's: first & last name, and email address"
2111
+ msgstr ""
2112
+
2113
+ #: includes/managers/class-fs-permission-manager.php:375
2114
+ msgid "Never miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features."
2115
+ msgstr ""
2116
+
2117
+ #: includes/managers/class-fs-permission-manager.php:396
2118
+ msgid "Newsletter"
2119
+ msgstr ""
2120
+
2121
+ #: includes/managers/class-fs-permission-manager.php:397
2122
+ msgid "Updates, announcements, marketing, no spam"
2123
+ msgstr ""
2124
+
2125
  #: templates/account/billing.php:22
2126
  msgctxt "verb"
2127
  msgid "Update"
2187
  msgid "Invoice"
2188
  msgstr ""
2189
 
2190
+ #: templates/connect/permissions-group.php:31, templates/forms/optout.php:26, templates/js/permissions.php:78
2191
+ msgctxt "verb"
2192
+ msgid "Opt Out"
2193
+ msgstr ""
2194
+
2195
+ #: templates/connect/permissions-group.php:32, templates/js/permissions.php:77
2196
+ msgctxt "verb"
2197
+ msgid "Opt In"
2198
+ msgstr ""
2199
+
2200
  #: templates/debug/api-calls.php:56
2201
  msgid "API"
2202
  msgstr ""
2285
  msgid "Next"
2286
  msgstr ""
2287
 
2288
+ #: templates/forms/affiliation.php:83
2289
  msgid "Non-expiring"
2290
  msgstr ""
2291
 
2292
+ #: templates/forms/affiliation.php:86
2293
  msgid "Apply to become an affiliate"
2294
  msgstr ""
2295
 
2296
+ #: templates/forms/affiliation.php:108
2297
  msgid "Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s."
2298
  msgstr ""
2299
 
2300
+ #: templates/forms/affiliation.php:123
2301
  msgid "Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information."
2302
  msgstr ""
2303
 
2304
+ #: templates/forms/affiliation.php:126
2305
  msgid "Your affiliation account was temporarily suspended."
2306
  msgstr ""
2307
 
2308
+ #: templates/forms/affiliation.php:129
2309
  msgid "Thank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days."
2310
  msgstr ""
2311
 
2312
+ #: templates/forms/affiliation.php:132
2313
  msgid "Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support."
2314
  msgstr ""
2315
 
2316
+ #: templates/forms/affiliation.php:145
2317
  msgid "Like the %s? Become our ambassador and earn cash ;-)"
2318
  msgstr ""
2319
 
2320
+ #: templates/forms/affiliation.php:146
2321
  msgid "Refer new customers to our %s and earn %s commission on each successful sale you refer!"
2322
  msgstr ""
2323
 
2324
+ #: templates/forms/affiliation.php:149
2325
  msgid "Program Summary"
2326
  msgstr ""
2327
 
2328
+ #: templates/forms/affiliation.php:151
2329
  msgid "%s commission when a customer purchases a new license."
2330
  msgstr ""
2331
 
2332
+ #: templates/forms/affiliation.php:153
2333
  msgid "Get commission for automated subscription renewals."
2334
  msgstr ""
2335
 
2336
+ #: templates/forms/affiliation.php:156
2337
  msgid "%s tracking cookie after the first visit to maximize earnings potential."
2338
  msgstr ""
2339
 
2340
+ #: templates/forms/affiliation.php:159
2341
  msgid "Unlimited commissions."
2342
  msgstr ""
2343
 
2344
+ #: templates/forms/affiliation.php:161
2345
  msgid "%s minimum payout amount."
2346
  msgstr ""
2347
 
2348
+ #: templates/forms/affiliation.php:162
2349
  msgid "Payouts are in USD and processed monthly via PayPal."
2350
  msgstr ""
2351
 
2352
+ #: templates/forms/affiliation.php:163
2353
  msgid "As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days."
2354
  msgstr ""
2355
 
2356
+ #: templates/forms/affiliation.php:166
2357
  msgid "Affiliate"
2358
  msgstr ""
2359
 
2360
+ #: templates/forms/affiliation.php:169, templates/forms/resend-key.php:23
2361
  msgid "Email address"
2362
  msgstr ""
2363
 
2364
+ #: templates/forms/affiliation.php:173
2365
  msgid "Full name"
2366
  msgstr ""
2367
 
2368
+ #: templates/forms/affiliation.php:177
2369
  msgid "PayPal account email address"
2370
  msgstr ""
2371
 
2372
+ #: templates/forms/affiliation.php:181
2373
  msgid "Where are you going to promote the %s?"
2374
  msgstr ""
2375
 
2376
+ #: templates/forms/affiliation.php:183
2377
  msgid "Enter the domain of your website or other websites from where you plan to promote the %s."
2378
  msgstr ""
2379
 
2380
+ #: templates/forms/affiliation.php:185
2381
  msgid "Add another domain"
2382
  msgstr ""
2383
 
2384
+ #: templates/forms/affiliation.php:189
2385
  msgid "Extra Domains"
2386
  msgstr ""
2387
 
2388
+ #: templates/forms/affiliation.php:190
2389
  msgid "Extra domains where you will be marketing the product from."
2390
  msgstr ""
2391
 
2392
+ #: templates/forms/affiliation.php:200
2393
  msgid "Promotion methods"
2394
  msgstr ""
2395
 
2396
+ #: templates/forms/affiliation.php:203
2397
  msgid "Social media (Facebook, Twitter, etc.)"
2398
  msgstr ""
2399
 
2400
+ #: templates/forms/affiliation.php:207
2401
  msgid "Mobile apps"
2402
  msgstr ""
2403
 
2404
+ #: templates/forms/affiliation.php:211
2405
  msgid "Website, email, and social media statistics (optional)"
2406
  msgstr ""
2407
 
2408
+ #: templates/forms/affiliation.php:214
2409
  msgid "Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential)."
2410
  msgstr ""
2411
 
2412
+ #: templates/forms/affiliation.php:218
2413
  msgid "How will you promote us?"
2414
  msgstr ""
2415
 
2416
+ #: templates/forms/affiliation.php:221
2417
  msgid "Please provide details on how you intend to promote %s (please be as specific as possible)."
2418
  msgstr ""
2419
 
2420
+ #: templates/forms/affiliation.php:233, templates/forms/resend-key.php:22, templates/account/partials/disconnect-button.php:92
2421
  msgid "Cancel"
2422
  msgstr ""
2423
 
2424
+ #: templates/forms/affiliation.php:235
2425
  msgid "Become an affiliate"
2426
  msgstr ""
2427
 
2441
  msgid "User key"
2442
  msgstr ""
2443
 
2444
+ #: templates/forms/email-address-update.php:32
2445
+ msgid "Email address update"
2446
  msgstr ""
2447
 
2448
+ #: templates/forms/email-address-update.php:33, templates/forms/user-change.php:81
2449
+ msgctxt "close window"
2450
+ msgid "Dismiss"
2451
  msgstr ""
2452
 
2453
+ #: templates/forms/email-address-update.php:38
2454
+ msgid "Enter the new email address"
2455
  msgstr ""
2456
 
2457
+ #: templates/forms/email-address-update.php:42
2458
+ msgid "Are both %s and %s your email addresses?"
2459
  msgstr ""
2460
 
2461
+ #: templates/forms/email-address-update.php:50
2462
+ msgid "Yes - both addresses are mine"
 
2463
  msgstr ""
2464
 
2465
+ #: templates/forms/email-address-update.php:57
2466
+ msgid "%s is my client's email address"
2467
+ msgstr ""
2468
+
2469
+ #: templates/forms/email-address-update.php:66
2470
+ msgid "%s is my email address"
2471
  msgstr ""
2472
 
2473
+ #: templates/forms/email-address-update.php:75
2474
+ msgid "Would you like to merge %s into %s?"
2475
  msgstr ""
2476
 
2477
+ #: templates/forms/email-address-update.php:84
2478
+ msgid "Yes - move all my data and assets from %s to %s"
2479
  msgstr ""
2480
 
2481
+ #: templates/forms/email-address-update.php:94
2482
+ msgid "No - only move this site's data to %s"
2483
  msgstr ""
2484
 
2485
+ #: templates/forms/email-address-update.php:292, templates/forms/email-address-update.php:298
2486
+ msgid "Update"
2487
+ msgstr ""
2488
+
2489
+ #: templates/forms/license-activation.php:23
2490
+ msgid "Please enter the license key that you received in the email right after the purchase:"
2491
+ msgstr ""
2492
+
2493
+ #: templates/forms/license-activation.php:28
2494
+ msgid "Update License"
2495
+ msgstr ""
2496
+
2497
+ #: templates/forms/license-activation.php:34
2498
+ msgid "The %1$s will be periodically sending essential license data to %2$s to check for security and feature updates, and verify the validity of your license."
2499
+ msgstr ""
2500
+
2501
+ #: templates/forms/license-activation.php:39
2502
+ msgid "Agree & Activate License"
2503
+ msgstr ""
2504
+
2505
+ #: templates/forms/license-activation.php:204
2506
+ msgid "Associate with the license owner's account."
2507
  msgstr ""
2508
 
2509
  #: templates/forms/optout.php:44
2510
+ msgid "Communication"
2511
  msgstr ""
2512
 
2513
+ #: templates/forms/optout.php:56
2514
+ msgid "Stay Connected"
2515
  msgstr ""
2516
 
2517
+ #: templates/forms/optout.php:61
2518
+ msgid "Diagnostic Info"
2519
  msgstr ""
2520
 
2521
+ #: templates/forms/optout.php:77
2522
+ msgid "Keep Sharing"
2523
  msgstr ""
2524
 
2525
+ #: templates/forms/optout.php:82
2526
+ msgid "Extensions"
2527
+ msgstr ""
2528
+
2529
+ #: templates/forms/optout.php:104
2530
+ msgid "Keep automatic updates"
2531
  msgstr ""
2532
 
2533
  #: templates/forms/premium-versions-upgrade-handler.php:40
2551
  msgid "Send License Key"
2552
  msgstr ""
2553
 
2554
+ #: templates/forms/resend-key.php:58
2555
+ msgid "Enter the email address you've used during the purchase and we will resend you the license key."
2556
+ msgstr ""
2557
+
2558
+ #: templates/forms/resend-key.php:59
2559
  msgid "Enter the email address you've used for the upgrade below and we will resend you the license key."
2560
  msgstr ""
2561
 
2567
  msgid "In case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?"
2568
  msgstr ""
2569
 
 
 
 
 
2570
  #: templates/forms/subscription-cancellation.php:57
2571
  msgid "Cancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site."
2572
  msgstr ""
2587
  msgid "Proceed"
2588
  msgstr ""
2589
 
2590
+ #: templates/forms/subscription-cancellation.php:191, templates/forms/deactivation/form.php:216
2591
  msgid "Cancel %s & Proceed"
2592
  msgstr ""
2593
 
2611
  msgid "Enter email address"
2612
  msgstr ""
2613
 
2614
+ #: templates/js/permissions.php:337, templates/js/permissions.php:485
2615
+ msgid "Saved"
 
2616
  msgstr ""
2617
 
2618
  #: templates/js/style-premium-theme.php:39
2623
  msgid "Beta"
2624
  msgstr ""
2625
 
2626
+ #: templates/partials/network-activation.php:32
2627
  msgid "Activate license on all sites in the network."
2628
  msgstr ""
2629
 
2630
+ #: templates/partials/network-activation.php:33
2631
  msgid "Apply on all sites in the network."
2632
  msgstr ""
2633
 
2634
+ #: templates/partials/network-activation.php:36
2635
  msgid "Activate license on all pending sites."
2636
  msgstr ""
2637
 
2638
+ #: templates/partials/network-activation.php:37
2639
  msgid "Apply on all pending sites."
2640
  msgstr ""
2641
 
2642
+ #: templates/partials/network-activation.php:45, templates/partials/network-activation.php:79
2643
  msgid "allow"
2644
  msgstr ""
2645
 
2646
+ #: templates/partials/network-activation.php:48, templates/partials/network-activation.php:82
2647
  msgid "delegate"
2648
  msgstr ""
2649
 
2650
+ #: templates/partials/network-activation.php:52, templates/partials/network-activation.php:86
2651
  msgid "skip"
2652
  msgstr ""
2653
 
2673
  msgstr ""
2674
 
2675
  #. translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription'
2676
+ #: templates/account/partials/addon.php:34
2677
  msgid "%1$s will immediately stop all future recurring payments and your %s plan license will expire in %s."
2678
  msgstr ""
2679
 
2680
+ #: templates/account/partials/addon.php:190
2681
  msgid "Cancelled"
2682
  msgstr ""
2683
 
2684
+ #: templates/account/partials/addon.php:200
2685
  msgid "No expiration"
2686
  msgstr ""
2687
 
2688
+ #: templates/account/partials/disconnect-button.php:74
2689
+ msgid "By disconnecting the website, previously shared diagnostic data about %1$s will be deleted and no longer visible to %2$s."
2690
+ msgstr ""
2691
+
2692
+ #: templates/account/partials/disconnect-button.php:78
2693
+ msgid "Disconnecting the website will permanently remove %s from your User Dashboard's account."
2694
+ msgstr ""
2695
+
2696
+ #: templates/account/partials/disconnect-button.php:84
2697
+ msgid "If you wish to cancel your %1$s plan's subscription instead, please navigate to the %2$s and cancel it there."
2698
+ msgstr ""
2699
+
2700
+ #: templates/account/partials/disconnect-button.php:88
2701
+ msgid "Are you sure you would like to proceed with the disconnection?"
2702
+ msgstr ""
2703
+
2704
+ #: templates/account/partials/site.php:190
2705
  msgid "Owner Name"
2706
  msgstr ""
2707
 
2708
+ #: templates/account/partials/site.php:202
2709
  msgid "Owner Email"
2710
  msgstr ""
2711
 
2712
+ #: templates/account/partials/site.php:214
2713
  msgid "Owner ID"
2714
  msgstr ""
2715
 
2716
+ #: templates/account/partials/site.php:288
2717
  msgid "Subscription"
2718
  msgstr ""
2719
 
2725
  msgid "Contact Support"
2726
  msgstr ""
2727
 
2728
+ #: templates/forms/deactivation/form.php:65
2729
  msgid "Anonymous feedback"
2730
  msgstr ""
2731
 
2732
+ #: templates/forms/deactivation/form.php:71
2733
+ msgid "hour"
2734
+ msgstr ""
2735
+
2736
+ #: templates/forms/deactivation/form.php:76
2737
+ msgid "hours"
2738
+ msgstr ""
2739
+
2740
+ #: templates/forms/deactivation/form.php:81, templates/forms/deactivation/form.php:86
2741
+ msgid "days"
2742
+ msgstr ""
2743
+
2744
+ #: templates/forms/deactivation/form.php:106
2745
  msgid "Deactivate"
2746
  msgstr ""
2747
 
2748
+ #: templates/forms/deactivation/form.php:108
2749
  msgid "Activate %s"
2750
  msgstr ""
2751
 
2752
+ #: templates/forms/deactivation/form.php:111
2753
+ msgid "Submit & %s"
2754
+ msgstr ""
2755
+
2756
+ #: templates/forms/deactivation/form.php:130
2757
  msgid "Quick Feedback"
2758
  msgstr ""
2759
 
2760
+ #: templates/forms/deactivation/form.php:134
2761
  msgid "If you have a moment, please let us know why you are %s"
2762
  msgstr ""
2763
 
2764
+ #: templates/forms/deactivation/form.php:134
2765
  msgid "deactivating"
2766
  msgstr ""
2767
 
2768
+ #: templates/forms/deactivation/form.php:134
2769
  msgid "switching"
2770
  msgstr ""
2771
 
2772
+ #: templates/forms/deactivation/form.php:448
2773
+ msgid "Kindly tell us the reason so we can improve."
2774
  msgstr ""
2775
 
2776
+ #: templates/forms/deactivation/form.php:478
2777
+ msgid "Snooze & %s"
2778
  msgstr ""
2779
 
2780
+ #: templates/forms/deactivation/form.php:638
2781
  msgid "Yes - %s"
2782
  msgstr ""
2783
 
2784
+ #: templates/forms/deactivation/form.php:645
2785
  msgid "Skip & %s"
2786
  msgstr ""
2787
 
freemius/require.php CHANGED
@@ -21,6 +21,8 @@
21
  // require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-abstract-manager.php';
22
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-option-manager.php';
23
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-gdpr-manager.php';
 
 
24
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-cache-manager.php';
25
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-notice-manager.php';
26
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-menu-manager.php';
21
  // require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-abstract-manager.php';
22
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-option-manager.php';
23
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-gdpr-manager.php';
24
+ require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-clone-manager.php';
25
+ require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-permission-manager.php';
26
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-cache-manager.php';
27
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-notice-manager.php';
28
  require_once WP_FS__DIR_INCLUDES . '/managers/class-fs-admin-menu-manager.php';
freemius/start.php CHANGED
@@ -15,7 +15,7 @@
15
  *
16
  * @var string
17
  */
18
- $this_sdk_version = '2.4.3';
19
 
20
  #region SDK Selection Logic --------------------------------------------------------------------
21
 
@@ -68,7 +68,11 @@
68
 
69
  if ( ! isset( $fs_active_plugins ) ) {
70
  // Load all Freemius powered active plugins.
71
- $fs_active_plugins = get_option( 'fs_active_plugins', new stdClass() );
 
 
 
 
72
 
73
  if ( ! isset( $fs_active_plugins->plugins ) ) {
74
  $fs_active_plugins->plugins = array();
15
  *
16
  * @var string
17
  */
18
+ $this_sdk_version = '2.5.3';
19
 
20
  #region SDK Selection Logic --------------------------------------------------------------------
21
 
68
 
69
  if ( ! isset( $fs_active_plugins ) ) {
70
  // Load all Freemius powered active plugins.
71
+ $fs_active_plugins = get_option( 'fs_active_plugins' );
72
+
73
+ if ( ! is_object( $fs_active_plugins ) ) {
74
+ $fs_active_plugins = new stdClass();
75
+ }
76
 
77
  if ( ! isset( $fs_active_plugins->plugins ) ) {
78
  $fs_active_plugins->plugins = array();
freemius/templates/account.php CHANGED
@@ -46,6 +46,7 @@
46
  $site = $fs->get_site();
47
  $name = $user->get_name();
48
  $license = $fs->_get_license();
 
49
  $is_data_debug_mode = $fs->is_data_debug_mode();
50
  $is_whitelabeled = $fs->is_whitelabeled();
51
  $subscription = ( is_object( $license ) ?
@@ -58,10 +59,19 @@
58
  $show_upgrade = ( ! $is_whitelabeled && $has_paid_plan && ! $is_paying && ! $is_paid_trial );
59
  $trial_plan = $fs->get_trial_plan();
60
 
 
 
 
 
 
61
  if ( $has_paid_plan ) {
62
  $fs->_add_license_activation_dialog_box();
63
  }
64
 
 
 
 
 
65
  $ids_of_installs_activated_with_foreign_licenses = $fs->should_handle_user_change() ?
66
  $fs->get_installs_ids_with_foreign_licenses() :
67
  array();
@@ -86,12 +96,15 @@
86
  ) );
87
  }
88
 
89
- $payments = $fs->_fetch_payments();
 
 
90
 
91
- $show_billing = ( ! $is_whitelabeled && is_array( $payments ) && 0 < count( $payments ) );
 
92
 
93
 
94
- $has_tabs = $fs->_add_tabs_before_content();
95
 
96
  if ( $has_tabs ) {
97
  $query_params['tabs'] = 'true';
@@ -140,6 +153,7 @@
140
  $install = $fs->get_install_by_blog_id( $site_info['blog_id'] );
141
  $view_params = array(
142
  'freemius' => $fs,
 
143
  'license' => $license,
144
  'site' => $site_info,
145
  'install' => $install,
@@ -234,6 +248,14 @@
234
  $addons_to_show = array_unique( array_merge( $installed_addons_ids, $account_addons ) );
235
 
236
  $is_active_bundle_subscription = ( is_object( $bundle_subscription ) && $bundle_subscription->is_active() );
 
 
 
 
 
 
 
 
237
  ?>
238
  <div class="wrap fs-section">
239
  <?php if ( ! $has_tabs && ! $fs->apply_filters( 'hide_account_tabs', false ) ) : ?>
@@ -274,24 +296,19 @@
274
  <li>&nbsp;&bull;&nbsp;</li>
275
  <?php endif ?>
276
  <?php if ( $show_billing ) : ?>
277
- <li><a href="#fs_billing"><i class="dashicons dashicons-portfolio"></i> <?php fs_esc_html_echo_inline( 'Billing & Invoices', 'billing-invoices', $slug ) ?></li>
278
  <li>&nbsp;&bull;&nbsp;</li>
279
  <?php endif ?>
280
  <?php if ( ! $is_whitelabeled ) : ?>
281
  <?php if ( ! $is_paying ) : ?>
282
  <li>
283
- <form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
284
- <input type="hidden" name="fs_action" value="delete_account">
285
- <?php wp_nonce_field( 'delete_account' ) ?>
286
- <a class="fs-delete-account" href="#" onclick="if (confirm('<?php
287
- if ( $is_active_subscription ) {
288
- echo esc_attr( sprintf( fs_text_inline( 'Deleting the account will automatically deactivate your %s plan license so you can use it on other sites. If you want to terminate the recurring payments as well, click the "Cancel" button, and first "Downgrade" your account. Are you sure you would like to continue with the deletion?', 'delete-account-x-confirm', $slug ), $plan->title ) );
289
- } else {
290
- echo esc_attr( sprintf( fs_text_inline( 'Deletion is not temporary. Only delete if you no longer want to use this %s anymore. Are you sure you would like to continue with the deletion?', 'delete-account-confirm', $slug ), $fs->get_module_label( true ) ) );
291
- }
292
- ?>')) this.parentNode.submit(); return false;"><i
293
- class="dashicons dashicons-no"></i> <?php fs_esc_html_echo_inline( 'Delete Account', 'delete-account', $slug ) ?></a>
294
- </form>
295
  </li>
296
  <li>&nbsp;&bull;&nbsp;</li>
297
  <?php endif ?>
@@ -330,7 +347,7 @@
330
  </li>
331
  <li>&nbsp;&bull;&nbsp;</li>
332
  <?php endif ?>
333
- <?php if ( ! $fs->is_single_plan() ) : ?>
334
  <li>
335
  <a href="<?php echo $fs->get_upgrade_url() ?>"><i
336
  class="dashicons dashicons-grid-view"></i> <?php echo esc_html( $change_plan_text ) ?></a>
@@ -366,7 +383,7 @@
366
 
367
  $profile = array();
368
 
369
- if ( ! $is_whitelabeled ) {
370
  $profile[] = array(
371
  'id' => 'user_name',
372
  'title' => fs_text_inline( 'Name', 'name', $slug ),
@@ -435,7 +452,7 @@
435
  'value' => $fs->get_plugin_version()
436
  );
437
 
438
- if ( ! fs_is_network_admin() && $is_premium && ! $is_whitelabeled ) {
439
  $profile[] = array(
440
  'id' => 'beta_program',
441
  'title' => '',
@@ -537,15 +554,13 @@
537
  <?php endif ?>
538
  <?php if ( ! $is_whitelabeled ) : ?>
539
  <div class="button-group">
540
- <?php $available_license = $fs->is_free_plan() && ! fs_is_network_admin() ? $fs->_get_available_premium_license( $site->is_localhost() ) : false ?>
541
  <?php if ( is_object( $available_license ) ) : ?>
542
- <?php $premium_plan = $fs->_get_plan_by_id( $available_license->plan_id ) ?>
543
  <?php
544
  $view_params = array(
545
  'freemius' => $fs,
546
  'slug' => $slug,
547
  'license' => $available_license,
548
- 'plan' => $premium_plan,
549
  'is_localhost' => $site->is_localhost(),
550
  'install_id' => $site->id,
551
  'class' => 'button-primary',
@@ -562,7 +577,7 @@
562
  <input type="hidden" name="fs_action"
563
  value="<?php echo $fs->get_unique_affix() ?>_sync_license">
564
  <?php wp_nonce_field( $fs->get_unique_affix() . '_sync_license' ) ?>
565
- <?php if ( $show_upgrade || ! $fs->is_single_plan() ) : ?>
566
  <a href="<?php echo $fs->get_upgrade_url() ?>"
567
  class="button<?php
568
  echo $show_upgrade ?
@@ -645,7 +660,7 @@
645
  <?php endif ?>
646
  <?php
647
  elseif ( in_array( $p['id'], array( 'license_key', 'site_secret_key' ) ) ) : ?>
648
- <?php if ( ! $is_whitelabeled ) : ?>
649
  <button class="button button-small fs-toggle-visibility"><?php fs_esc_html_echo_x_inline( 'Show', 'verb', 'show', $slug ) ?></button>
650
  <?php endif ?>
651
  <?php if ('license_key' === $p['id']) : ?>
@@ -658,6 +673,7 @@
658
  'user_name'
659
  ) ) )
660
  ) : ?>
 
661
  <form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST"
662
  onsubmit="var val = prompt('<?php echo esc_attr( sprintf(
663
  /* translators: %s: User's account property (e.g. name, email) */
@@ -668,9 +684,10 @@
668
  <input type="hidden" name="fs_<?php echo $p['id'] ?>_<?php echo $fs->get_unique_affix() ?>"
669
  value="">
670
  <?php wp_nonce_field( 'update_' . $p['id'] ) ?>
671
- <input type="submit" class="button button-small"
672
  value="<?php echo fs_esc_attr_x_inline( 'Edit', 'verb', 'edit', $slug ) ?>">
673
  </form>
 
674
  <?php elseif ( 'user_id' === $p['id'] && ! empty( $ids_of_installs_activated_with_foreign_licenses ) ) : ?>
675
  <input id="fs_change_user" type="submit" class="button button-small"
676
  value="<?php echo fs_esc_attr_inline( 'Change User', 'change-user', $slug ) ?>">
@@ -730,12 +747,30 @@
730
  <div class="fs-table-body">
731
  <table class="widefat">
732
  <?php
 
 
733
  foreach ( $site_view_params as $view_params ) {
734
  fs_require_template(
735
  'account/partials/site.php',
736
  $view_params
737
  );
738
- } ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
  </table>
740
  </div>
741
  </div>
@@ -839,7 +874,7 @@
839
 
840
  <?php
841
  if ( $show_billing ) {
842
- $view_params = array( 'id' => $VARS['id'] );
843
  fs_require_once_template( 'account/billing.php', $view_params );
844
  fs_require_once_template( 'account/payments.php', $view_params );
845
  }
@@ -919,7 +954,7 @@
919
 
920
  if ( ! isChecked || confirm( '<?php echo $confirmation_message ?>' ) ) {
921
  $.ajax( {
922
- url : ajaxurl,
923
  method: 'POST',
924
  data : {
925
  action : '<?php echo $fs->get_ajax_action( 'set_beta_mode' ) ?>',
@@ -1062,7 +1097,7 @@
1062
  var $toggleLink = $( this );
1063
 
1064
  $.ajax( {
1065
- url : ajaxurl,
1066
  method: 'POST',
1067
  data : {
1068
  action : '<?php echo $fs->get_ajax_action( 'toggle_whitelabel_mode' ) ?>',
@@ -1095,4 +1130,4 @@
1095
  'module_slug' => $slug,
1096
  'module_version' => $fs->get_plugin_version(),
1097
  );
1098
- fs_require_template( 'powered-by.php', $params );
46
  $site = $fs->get_site();
47
  $name = $user->get_name();
48
  $license = $fs->_get_license();
49
+ $is_license_foreign = ( is_object( $license ) && $user->id != $license->user_id );
50
  $is_data_debug_mode = $fs->is_data_debug_mode();
51
  $is_whitelabeled = $fs->is_whitelabeled();
52
  $subscription = ( is_object( $license ) ?
59
  $show_upgrade = ( ! $is_whitelabeled && $has_paid_plan && ! $is_paying && ! $is_paid_trial );
60
  $trial_plan = $fs->get_trial_plan();
61
 
62
+ $is_plan_change_supported = (
63
+ ! $fs->is_single_plan() &&
64
+ ! $fs->apply_filters( 'hide_plan_change', false )
65
+ );
66
+
67
  if ( $has_paid_plan ) {
68
  $fs->_add_license_activation_dialog_box();
69
  }
70
 
71
+ if ( $fs->should_handle_user_change() ) {
72
+ $fs->_add_email_address_update_dialog_box();
73
+ }
74
+
75
  $ids_of_installs_activated_with_foreign_licenses = $fs->should_handle_user_change() ?
76
  $fs->get_installs_ids_with_foreign_licenses() :
77
  array();
96
  ) );
97
  }
98
 
99
+ $show_billing = ( ! $is_whitelabeled && ! $fs->apply_filters( 'hide_billing_and_payments_info', false ) );
100
+ if ( $show_billing ) {
101
+ $payments = $fs->_fetch_payments();
102
 
103
+ $show_billing = ( is_array( $payments ) && 0 < count( $payments ) );
104
+ }
105
 
106
 
107
+ $has_tabs = $fs->_add_tabs_before_content();
108
 
109
  if ( $has_tabs ) {
110
  $query_params['tabs'] = 'true';
153
  $install = $fs->get_install_by_blog_id( $site_info['blog_id'] );
154
  $view_params = array(
155
  'freemius' => $fs,
156
+ 'user' => $fs->get_user(),
157
  'license' => $license,
158
  'site' => $site_info,
159
  'install' => $install,
248
  $addons_to_show = array_unique( array_merge( $installed_addons_ids, $account_addons ) );
249
 
250
  $is_active_bundle_subscription = ( is_object( $bundle_subscription ) && $bundle_subscription->is_active() );
251
+
252
+ $available_license = ( $fs->is_free_plan() && ! fs_is_network_admin() ) ?
253
+ $fs->_get_available_premium_license( $site->is_localhost() ) :
254
+ null;
255
+
256
+ $available_license_paid_plan = is_object( $available_license ) ?
257
+ $fs->_get_plan_by_id( $available_license->plan_id ) :
258
+ null;
259
  ?>
260
  <div class="wrap fs-section">
261
  <?php if ( ! $has_tabs && ! $fs->apply_filters( 'hide_account_tabs', false ) ) : ?>
296
  <li>&nbsp;&bull;&nbsp;</li>
297
  <?php endif ?>
298
  <?php if ( $show_billing ) : ?>
299
+ <li><a href="#fs_billing"><i class="dashicons dashicons-portfolio"></i> <?php fs_esc_html_echo_inline( 'Billing & Invoices', 'billing-invoices', $slug ) ?></a></li>
300
  <li>&nbsp;&bull;&nbsp;</li>
301
  <?php endif ?>
302
  <?php if ( ! $is_whitelabeled ) : ?>
303
  <?php if ( ! $is_paying ) : ?>
304
  <li>
305
+ <?php
306
+ $view_params = array(
307
+ 'freemius' => $fs,
308
+ 'license' => $available_license,
309
+ 'license_paid_plan' => $available_license_paid_plan,
310
+ );
311
+ fs_require_template( 'account/partials/disconnect-button.php', $view_params ); ?>
 
 
 
 
 
312
  </li>
313
  <li>&nbsp;&bull;&nbsp;</li>
314
  <?php endif ?>
347
  </li>
348
  <li>&nbsp;&bull;&nbsp;</li>
349
  <?php endif ?>
350
+ <?php if ( $is_plan_change_supported ) : ?>
351
  <li>
352
  <a href="<?php echo $fs->get_upgrade_url() ?>"><i
353
  class="dashicons dashicons-grid-view"></i> <?php echo esc_html( $change_plan_text ) ?></a>
383
 
384
  $profile = array();
385
 
386
+ if ( ! $is_whitelabeled ) {
387
  $profile[] = array(
388
  'id' => 'user_name',
389
  'title' => fs_text_inline( 'Name', 'name', $slug ),
452
  'value' => $fs->get_plugin_version()
453
  );
454
 
455
+ if ( ! fs_is_network_admin() && $is_premium ) {
456
  $profile[] = array(
457
  'id' => 'beta_program',
458
  'title' => '',
554
  <?php endif ?>
555
  <?php if ( ! $is_whitelabeled ) : ?>
556
  <div class="button-group">
 
557
  <?php if ( is_object( $available_license ) ) : ?>
 
558
  <?php
559
  $view_params = array(
560
  'freemius' => $fs,
561
  'slug' => $slug,
562
  'license' => $available_license,
563
+ 'plan' => $available_license_paid_plan,
564
  'is_localhost' => $site->is_localhost(),
565
  'install_id' => $site->id,
566
  'class' => 'button-primary',
577
  <input type="hidden" name="fs_action"
578
  value="<?php echo $fs->get_unique_affix() ?>_sync_license">
579
  <?php wp_nonce_field( $fs->get_unique_affix() . '_sync_license' ) ?>
580
+ <?php if ( $show_upgrade || $is_plan_change_supported ) : ?>
581
  <a href="<?php echo $fs->get_upgrade_url() ?>"
582
  class="button<?php
583
  echo $show_upgrade ?
660
  <?php endif ?>
661
  <?php
662
  elseif ( in_array( $p['id'], array( 'license_key', 'site_secret_key' ) ) ) : ?>
663
+ <?php if ( ! $is_whitelabeled && ( 'site_secret_key' === $p['id'] || ! $is_license_foreign ) ) : ?>
664
  <button class="button button-small fs-toggle-visibility"><?php fs_esc_html_echo_x_inline( 'Show', 'verb', 'show', $slug ) ?></button>
665
  <?php endif ?>
666
  <?php if ('license_key' === $p['id']) : ?>
673
  'user_name'
674
  ) ) )
675
  ) : ?>
676
+ <?php if ( 'email' !== $p['id'] || ! fs_is_network_admin() ) : ?>
677
  <form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST"
678
  onsubmit="var val = prompt('<?php echo esc_attr( sprintf(
679
  /* translators: %s: User's account property (e.g. name, email) */
684
  <input type="hidden" name="fs_<?php echo $p['id'] ?>_<?php echo $fs->get_unique_affix() ?>"
685
  value="">
686
  <?php wp_nonce_field( 'update_' . $p['id'] ) ?>
687
+ <input type="submit" class="button button-small <?php if ( 'email' === $p['id'] ) echo 'button-edit-email-address' ?>"
688
  value="<?php echo fs_esc_attr_x_inline( 'Edit', 'verb', 'edit', $slug ) ?>">
689
  </form>
690
+ <?php endif ?>
691
  <?php elseif ( 'user_id' === $p['id'] && ! empty( $ids_of_installs_activated_with_foreign_licenses ) ) : ?>
692
  <input id="fs_change_user" type="submit" class="button button-small"
693
  value="<?php echo fs_esc_attr_inline( 'Change User', 'change-user', $slug ) ?>">
747
  <div class="fs-table-body">
748
  <table class="widefat">
749
  <?php
750
+ $current_blog_id = get_current_blog_id();
751
+
752
  foreach ( $site_view_params as $view_params ) {
753
  fs_require_template(
754
  'account/partials/site.php',
755
  $view_params
756
  );
757
+ }
758
+
759
+ /**
760
+ * It's possible for the `Freemius::switch_to_blog()` method to be called within the `site.php` template and this changes the Freemius instance's context, so this check is for restoring the previous context based on the previously retrieved site.
761
+ *
762
+ * @author Leo Fajardo (@leorw)
763
+ * @since 2.5.0
764
+ */
765
+ $current_install = $fs->get_site();
766
+
767
+ if (
768
+ is_object( $site ) &&
769
+ ( ! is_object( $current_install ) || $current_install->id != $site->id )
770
+ ) {
771
+ $fs->switch_to_blog( $current_blog_id, $site, true );
772
+ }
773
+ ?>
774
  </table>
775
  </div>
776
  </div>
874
 
875
  <?php
876
  if ( $show_billing ) {
877
+ $view_params = array( 'id' => $VARS['id'], 'payments' => $payments );
878
  fs_require_once_template( 'account/billing.php', $view_params );
879
  fs_require_once_template( 'account/payments.php', $view_params );
880
  }
954
 
955
  if ( ! isChecked || confirm( '<?php echo $confirmation_message ?>' ) ) {
956
  $.ajax( {
957
+ url : <?php echo Freemius::ajax_url() ?>,
958
  method: 'POST',
959
  data : {
960
  action : '<?php echo $fs->get_ajax_action( 'set_beta_mode' ) ?>',
1097
  var $toggleLink = $( this );
1098
 
1099
  $.ajax( {
1100
+ url : <?php echo Freemius::ajax_url() ?>,
1101
  method: 'POST',
1102
  data : {
1103
  action : '<?php echo $fs->get_ajax_action( 'toggle_whitelabel_mode' ) ?>',
1130
  'module_slug' => $slug,
1131
  'module_version' => $fs->get_plugin_version(),
1132
  );
1133
+ fs_require_template( 'powered-by.php', $params );
freemius/templates/account/billing.php CHANGED
@@ -377,7 +377,7 @@
377
  });
378
 
379
  $.ajax({
380
- url : ajaxurl,
381
  method : 'POST',
382
  data : {
383
  action : '<?php echo $fs->get_ajax_action( 'update_billing' ) ?>',
377
  });
378
 
379
  $.ajax({
380
+ url : <?php echo Freemius::ajax_url() ?>,
381
  method : 'POST',
382
  data : {
383
  action : '<?php echo $fs->get_ajax_action( 'update_billing' ) ?>',
freemius/templates/account/partials/disconnect-button.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2016, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * @var array $VARS
15
+ * @var Freemius $fs
16
+ */
17
+ $fs = $VARS['freemius'];
18
+
19
+ /**
20
+ * @var FS_Plugin_License $license
21
+ */
22
+ $license = $VARS['license'];
23
+ /**
24
+ * @var FS_Plugin_Plan $license_paid_plan
25
+ */
26
+ $license_paid_plan = $VARS['license_paid_plan'];
27
+
28
+ $license_subscription = ( is_object( $license ) && is_object( $license_paid_plan ) ) ?
29
+ $fs->_get_subscription( $license->id ) :
30
+ null;
31
+
32
+ $has_active_subscription = (
33
+ is_object( $license_subscription ) &&
34
+ $license_subscription->is_active()
35
+ );
36
+
37
+ $button_id = "fs_disconnect_button_{$fs->get_id()}";
38
+
39
+ $website_link = sprintf( '<a href="#" tabindex="-1">%s</a>', fs_strip_url_protocol( untrailingslashit( Freemius::get_unfiltered_site_url() ) ) );
40
+ ?>
41
+ <script type="text/javascript">
42
+ // Wrap in a IFFE to prevent leaking global variables.
43
+ ( function( $ ) {
44
+ $( document ).ready( function() {
45
+ var $modal = $( '#fs_modal_delete_site' );
46
+
47
+ $( '#<?php echo $button_id ?>' ).on( 'click', function( e ) {
48
+ // Prevent the form being submitted.
49
+ e.preventDefault();
50
+
51
+ $( document.body ).append( $modal );
52
+ $modal.show();
53
+ } );
54
+
55
+ $modal.on( 'click', '.button-close', function ( evt ) {
56
+ $modal.hide();
57
+ } );
58
+
59
+ $modal.on( 'click', '.button-primary', function ( evt ) {
60
+ $( '#<?php echo $button_id ?>' ).closest( 'form' )[0].submit();
61
+ } );
62
+ } );
63
+ } )( jQuery );
64
+ </script>
65
+ <div id="fs_modal_delete_site" class="fs-modal active" style="display: none">
66
+ <div class="fs-modal-dialog">
67
+ <div class="fs-modal-header">
68
+ <h4><?php echo $fs->esc_html_inline( 'Disconnect', 'disconnect' ) ?></h4>
69
+ </div>
70
+ <div class="fs-modal-body">
71
+ <?php if ( ! is_object( $license ) ) : ?>
72
+ <p><?php echo
73
+ // translators: %1$s is replaced with the website's homepage address, %2$s is replaced with the plugin name.
74
+ sprintf( esc_html( $fs->get_text_inline( 'By disconnecting the website, previously shared diagnostic data about %1$s will be deleted and no longer visible to %2$s.', 'disconnect-intro-paid' ) ), $website_link, '<b>' . $fs->get_plugin_title() . '</b>' ) ?></p>
75
+ <?php else : ?>
76
+ <p><?php echo
77
+ // translators: %s is replaced with the website's homepage address.
78
+ sprintf( esc_html( $fs->get_text_inline( 'Disconnecting the website will permanently remove %s from your User Dashboard\'s account.', 'disconnect-intro-paid' ) ), $website_link ) ?></p>
79
+ <?php endif ?>
80
+
81
+ <?php if ( $has_active_subscription ) : ?>
82
+ <p><?php echo
83
+ // translators: %1$s is replaced by the paid plan name, %2$s is replaced with an anchor link with the text "User Dashboard".
84
+ sprintf( esc_html( $fs->get_text_inline( 'If you wish to cancel your %1$s plan\'s subscription instead, please navigate to the %2$s and cancel it there.', 'disconnect-subscription-disclaimer' ) ), $license_paid_plan->title, sprintf( '<a href="https://users.freemius.com" target="_blank" rel="noreferrer noopener nofollow">%s</a>', $fs->get_text_inline( 'User Dashboard', 'user-dashboard' ) )
85
+ ) ?></p>
86
+ <?php endif ?>
87
+
88
+ <p><?php echo esc_html( $fs->get_text_inline( 'Are you sure you would like to proceed with the disconnection?', 'disconnect-confirm' ) ) ?></p>
89
+ </div>
90
+ <div class="fs-modal-footer">
91
+ <button class="button button-primary<?php if ( is_object( $license ) ) : ?> warn<?php endif ?>" tabindex="2"><?php echo $fs->esc_html_inline( 'Yes', 'yes' ) . ' - ' . $fs->esc_html_inline( 'Disconnect', 'disconnect' ) ?></button>
92
+ <button class="button button-secondary button-close" tabindex="1"><?php echo esc_html( $fs->get_text_inline( 'Cancel', 'cancel' ) ) ?></button>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ <form action="<?php echo esc_attr( $fs->_get_admin_page_url( 'account' ) ); ?>" method="POST">
97
+ <input type="hidden" name="fs_action" value="delete_account">
98
+ <?php wp_nonce_field( 'delete_account' ) ?>
99
+
100
+ <a id="<?php echo $button_id ?>" href="#" class="fs-button-inline">
101
+ <i class="dashicons dashicons-no"></i>
102
+ <?php echo $fs->esc_html_inline( 'Disconnect', 'disconnect' ) ?>
103
+ </a>
104
+ </form>
freemius/templates/account/partials/site.php CHANGED
@@ -23,7 +23,7 @@
23
  $is_whitelabeled = $fs->is_whitelabeled();
24
  $has_paid_plan = $fs->has_paid_plan();
25
  $is_premium = $fs->is_premium();
26
- $main_user = $fs->get_user();
27
  $blog_id = $site['blog_id'];
28
 
29
  $install = $VARS['install'];
@@ -32,7 +32,7 @@
32
  $trial_plan = $fs->get_trial_plan();
33
  $free_text = fs_text_inline( 'Free', 'free', $slug );
34
 
35
- if ( $is_whitelabeled && $fs->is_delegated_connection( $blog_id ) ) {
36
  $is_whitelabeled = $fs->is_whitelabeled( true, $blog_id );
37
  }
38
  ?>
@@ -159,14 +159,15 @@
159
  <td><?php if ( ! FS_Plugin_License::is_valid_id( $install->license_id ) ) : ?>
160
  <!-- Toggle Usage Tracking -->
161
  <?php $action = 'toggle_tracking' ?>
 
162
  <form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
163
  <input type="hidden" name="fs_action" value="<?php echo $action ?>">
164
  <?php wp_nonce_field( trim( "{$action}:{$blog_id}:{$install->id}", ':' ) ) ?>
165
  <input type="hidden" name="install_id" value="<?php echo $install->id ?>">
166
  <input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
167
- <button class="fs-toggle-tracking button button-small<?php if ( $install->is_disconnected ) {
168
  echo ' button-primary';
169
- } ?>" data-is-disconnected="<?php echo $install->is_disconnected ? 'true' : 'false' ?>"><?php $install->is_disconnected ? fs_esc_html_echo_inline( 'Opt In', 'opt-in', $slug ) : fs_esc_html_echo_inline( 'Opt Out', 'opt-out', $slug ) ?></button>
170
  </form>
171
  <!--/ Toggle Usage Tracking -->
172
  <?php endif ?></td>
@@ -174,7 +175,7 @@
174
  <?php $row_index ++ ?>
175
  <!--/ Blog ID -->
176
 
177
- <?php if ( $is_registered && $install->user_id != $main_user->id ) : ?>
178
  <?php
179
  /**
180
  * @var FS_User $user
@@ -226,7 +227,8 @@
226
  <td>
227
  <nobr><?php fs_esc_html_echo_inline( 'Public Key', 'public-key', $slug ) ?>:</nobr>
228
  </td>
229
- <td colspan="2"><code><?php echo htmlspecialchars( $install->public_key ) ?></code></td>
 
230
  </tr>
231
  <?php $row_index ++ ?>
232
  <!--/ Public Key -->
23
  $is_whitelabeled = $fs->is_whitelabeled();
24
  $has_paid_plan = $fs->has_paid_plan();
25
  $is_premium = $fs->is_premium();
26
+ $main_user = $VARS['user'];
27
  $blog_id = $site['blog_id'];
28
 
29
  $install = $VARS['install'];
32
  $trial_plan = $fs->get_trial_plan();
33
  $free_text = fs_text_inline( 'Free', 'free', $slug );
34
 
35
+ if ( $is_whitelabeled && is_object( $install ) && $fs->is_delegated_connection( $blog_id ) ) {
36
  $is_whitelabeled = $fs->is_whitelabeled( true, $blog_id );
37
  }
38
  ?>
159
  <td><?php if ( ! FS_Plugin_License::is_valid_id( $install->license_id ) ) : ?>
160
  <!-- Toggle Usage Tracking -->
161
  <?php $action = 'toggle_tracking' ?>
162
+ <?php $is_disconnected = ! FS_Permission_Manager::instance( $fs )->is_homepage_url_tracking_allowed( $blog_id ) ?>
163
  <form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
164
  <input type="hidden" name="fs_action" value="<?php echo $action ?>">
165
  <?php wp_nonce_field( trim( "{$action}:{$blog_id}:{$install->id}", ':' ) ) ?>
166
  <input type="hidden" name="install_id" value="<?php echo $install->id ?>">
167
  <input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
168
+ <button class="fs-toggle-tracking button button-small<?php if ( $is_disconnected ) {
169
  echo ' button-primary';
170
+ } ?>" data-is-disconnected="<?php echo $is_disconnected ? 'true' : 'false' ?>"><?php $is_disconnected ? fs_esc_html_echo_inline( 'Opt In', 'opt-in', $slug ) : fs_esc_html_echo_inline( 'Opt Out', 'opt-out', $slug ) ?></button>
171
  </form>
172
  <!--/ Toggle Usage Tracking -->
173
  <?php endif ?></td>
175
  <?php $row_index ++ ?>
176
  <!--/ Blog ID -->
177
 
178
+ <?php if ( $install->user_id != $main_user->id ) : ?>
179
  <?php
180
  /**
181
  * @var FS_User $user
227
  <td>
228
  <nobr><?php fs_esc_html_echo_inline( 'Public Key', 'public-key', $slug ) ?>:</nobr>
229
  </td>
230
+ <td><code><?php echo htmlspecialchars( $install->public_key ) ?></code></td>
231
+ <td></td>
232
  </tr>
233
  <?php $row_index ++ ?>
234
  <!--/ Public Key -->
freemius/templates/account/payments.php CHANGED
@@ -10,19 +10,19 @@
10
  exit;
11
  }
12
 
13
- /**
14
- * @var array $VARS
15
- * @var Freemius $fs
16
- */
17
- $fs = freemius( $VARS['id'] );
18
 
19
- $slug = $fs->get_slug();
 
 
 
20
 
21
- $payments = $fs->_fetch_payments();
22
-
23
- $show_payments = ( is_array( $payments ) && 0 < count( $payments ) );
24
 
25
- if ( $show_payments ) :
26
  ?>
27
  <div class="postbox">
28
  <div id="fs_payments">
@@ -56,4 +56,3 @@
56
  </div>
57
  </div>
58
  <?php
59
- endif;
10
  exit;
11
  }
12
 
13
+ /**
14
+ * @var array $VARS
15
+ * @var Freemius $fs
16
+ */
17
+ $fs = freemius( $VARS['id'] );
18
 
19
+ /**
20
+ * @var FS_Payment[] $payments
21
+ */
22
+ $payments = $VARS['payments'];
23
 
24
+ $slug = $fs->get_slug();
 
 
25
 
 
26
  ?>
27
  <div class="postbox">
28
  <div id="fs_payments">
56
  </div>
57
  </div>
58
  <?php
 
freemius/templates/admin-notice.php CHANGED
@@ -45,6 +45,9 @@
45
  case 'promotion':
46
  echo 'updated promotion';
47
  break;
 
 
 
48
  case 'update':
49
  // echo 'update-nag update';
50
  // break;
@@ -64,7 +67,7 @@
64
  } ?>"><?php if ( ! empty( $VARS['plugin'] ) ) : ?>
65
  <label class="fs-plugin-title"><?php echo $VARS['plugin'] ?></label>
66
  <?php endif ?>
67
- <?php if ( ! empty( $VARS['sticky'] ) ) : ?>
68
  <div class="fs-close"><i class="dashicons dashicons-no"
69
  title="<?php echo esc_attr( $dismiss_text ) ?>"></i> <span><?php echo esc_html( $dismiss_text ) ?></span>
70
  </div>
45
  case 'promotion':
46
  echo 'updated promotion';
47
  break;
48
+ case 'warn':
49
+ echo 'notice notice-warning';
50
+ break;
51
  case 'update':
52
  // echo 'update-nag update';
53
  // break;
67
  } ?>"><?php if ( ! empty( $VARS['plugin'] ) ) : ?>
68
  <label class="fs-plugin-title"><?php echo $VARS['plugin'] ?></label>
69
  <?php endif ?>
70
+ <?php if ( ! empty( $VARS['sticky'] ) && ( ! isset( $VARS['dismissible'] ) || false !== $VARS['dismissible'] ) ) : ?>
71
  <div class="fs-close"><i class="dashicons dashicons-no"
72
  title="<?php echo esc_attr( $dismiss_text ) ?>"></i> <span><?php echo esc_html( $dismiss_text ) ?></span>
73
  </div>
freemius/templates/auto-installation.php CHANGED
@@ -164,7 +164,7 @@
164
  }
165
 
166
  $.ajax({
167
- url : ajaxurl,
168
  method : 'POST',
169
  data : data,
170
  success: function (resultObj) {
164
  }
165
 
166
  $.ajax({
167
+ url : <?php echo Freemius::ajax_url() ?>,
168
  method : 'POST',
169
  data : data,
170
  success: function (resultObj) {
freemius/templates/clone-resolution-js.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.0
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+ ?>
13
+ <script type="text/javascript">
14
+ ( function( $ ) {
15
+ var $errorMessage = null;
16
+
17
+ $( document ).ready( function() {
18
+ var $cloneResolutionNotice = $( 'div[data-id="clone_resolution_options_notice"], div[data-id="temporary_duplicate_notice"]' );
19
+
20
+ if ( 0 === $cloneResolutionNotice.length ) {
21
+ return;
22
+ }
23
+
24
+ $errorMessage = $cloneResolutionNotice.find( '#fs_clone_resolution_error_message' );
25
+
26
+ /**
27
+ * Triggers an AJAX request when the license activation link or any of the buttons on the clone resolution options notice is clicked. The AJAX request will then handle the action the user has chosen.
28
+ */
29
+ $cloneResolutionNotice.on( 'click', '.button, #fs_temporary_duplicate_license_activation_link', function( evt ) {
30
+ evt.preventDefault();
31
+
32
+ var $this = $( this );
33
+
34
+ if ( $this.hasClass( 'disabled' ) ) {
35
+ return;
36
+ }
37
+
38
+ var $body = $( 'body' ),
39
+ $optionsContainer = $this.parents( '.fs-clone-resolution-options-container' ),
40
+ cursor = $body.css( 'cursor' ),
41
+ beforeUnload = function() {
42
+ return '<?php fs_esc_js_echo_inline( 'Please wait', 'please-wait' ) ?>';
43
+ };
44
+
45
+ $.ajax( {
46
+ // Get the parent options container from the child as `$cloneResolutionNotice` can have different AJAX URLs if both the manual clone resolution options and temporary duplicate notices are shown (for different subsites in a multisite network).
47
+ url : $optionsContainer.data( 'ajax-url' ),
48
+ method : 'POST',
49
+ data : {
50
+ action : '<?php echo $VARS['ajax_action'] ?>',
51
+ security : '<?php echo wp_create_nonce( $VARS['ajax_action'] ) ?>',
52
+ clone_action: $this.data( 'clone-action' ),
53
+ blog_id : $optionsContainer.data( 'blog-id' )
54
+ },
55
+ beforeSend: function() {
56
+ $body.css( { cursor: 'wait' } );
57
+
58
+ $cloneResolutionNotice.find( '.button' ).addClass( 'disabled' );
59
+
60
+ $( window ).on( 'beforeunload', beforeUnload );
61
+ },
62
+ success : function( resultObj ) {
63
+ $( window ).off( 'beforeunload', beforeUnload );
64
+
65
+ if ( resultObj.data.redirect_url && '' !== resultObj.data.redirect_url ) {
66
+ window.location = resultObj.data.redirect_url;
67
+ } else {
68
+ window.location.reload();
69
+ }
70
+ },
71
+ error : function() {
72
+ $body.css( { cursor: cursor } );
73
+ $cloneResolutionNotice.find( '.button' ).removeClass( 'disabled' );
74
+ }
75
+ } );
76
+ } );
77
+ } );
78
+ } )( jQuery );
79
+ </script>
freemius/templates/connect.php CHANGED
@@ -25,6 +25,15 @@
25
 
26
  $fs->_enqueue_connect_essentials();
27
 
 
 
 
 
 
 
 
 
 
28
  $current_user = Freemius::_get_current_wp_user();
29
 
30
  $first_name = $current_user->user_firstname;
@@ -32,7 +41,7 @@
32
  $first_name = $current_user->nickname;
33
  }
34
 
35
- $site_url = get_site_url();
36
  $protocol_pos = strpos( $site_url, '://' );
37
  if ( false !== $protocol_pos ) {
38
  $site_url = substr( $site_url, $protocol_pos + 3 );
@@ -118,6 +127,14 @@
118
  if ( is_null( $is_gdpr_required ) ) {
119
  $is_gdpr_required = $fs->fetch_and_store_current_user_gdpr_anonymously();
120
  }
 
 
 
 
 
 
 
 
121
  ?>
122
  <?php
123
  if ( $is_optin_dialog ) { ?>
@@ -138,26 +155,54 @@
138
  * @author Vova Feldman
139
  * @since 2.3.2
140
  */
141
- $fs->do_action( 'connect/before' );
142
  ?>
143
  <div id="fs_connect"
144
  class="wrap<?php if ( ! fs_is_network_admin() && ( ! $fs->is_enable_anonymous() || $is_pending_activation || $require_license_key ) ) {
145
  echo ' fs-anonymous-disabled';
146
  } ?><?php echo $require_license_key ? ' require-license-key' : '' ?>">
147
- <div class="fs-visual">
148
- <b class="fs-site-icon"><i class="dashicons dashicons-wordpress"></i></b>
149
- <i class="dashicons dashicons-plus fs-first"></i>
150
- <?php
151
- $vars = array( 'id' => $fs->get_id() );
152
- fs_require_once_template( 'plugin-icon.php', $vars );
153
- ?>
154
- <i class="dashicons dashicons-plus fs-second"></i>
155
- <img class="fs-connect-logo" width="80" height="80" src="//img.freemius.com/connect-logo.png"/>
156
- </div>
 
 
 
 
157
  <div class="fs-content">
 
 
158
  <?php if ( ! empty( $error ) ) : ?>
159
- <p class="fs-error"><?php echo esc_html( $error ) ?></p>
160
  <?php endif ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  <p><?php
162
  $button_label = fs_text_inline( 'Allow & Continue', 'opt-in-connect', $slug );
163
  $message = '';
@@ -168,16 +213,14 @@
168
  $message = $fs->apply_filters( 'pending_activation_message', sprintf(
169
  /* translators: %s: name (e.g. Thanks John!) */
170
  fs_text_inline( 'Thanks %s!', 'thanks-x', $slug ) . '<br>' .
171
- fs_text_inline( 'You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s.', 'pending-activation-message', $slug ),
172
  $first_name,
173
  '<b>' . $fs->get_plugin_name() . '</b>',
174
  '<b>' . $current_user->user_email . '</b>',
175
- fs_text_inline( 'complete the install', 'complete-the-install', $slug )
176
  ) );
177
  } else if ( $require_license_key ) {
178
- $button_label = $is_network_upgrade_mode ?
179
- fs_text_inline( 'Activate License', 'agree-activate-license', $slug ) :
180
- fs_text_inline( 'Agree & Activate License', 'agree-activate-license', $slug );
181
 
182
  $message = $fs->apply_filters(
183
  'connect-message_on-premium',
@@ -186,21 +229,32 @@
186
  $fs->get_plugin_name()
187
  );
188
  } else {
189
- $filter = 'connect_message';
190
- $default_optin_message = $is_gdpr_required ?
191
- fs_text_inline( 'Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s.', 'connect-message', $slug) :
192
- fs_text_inline( 'Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking with %4$s.', 'connect-message', $slug);
193
 
194
- if ( $fs->is_plugin_update() ) {
 
 
 
 
 
 
195
  // If Freemius was added on a plugin update, set different
196
  // opt-in message.
197
- $default_optin_message = $is_gdpr_required ?
198
- fs_text_inline( 'Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s. If you skip this, that\'s okay! %1$s will still work just fine.', 'connect-message_on-update', $slug ) :
199
- fs_text_inline( 'Never miss an important update - opt in to our security & feature updates notifications, and non-sensitive diagnostic tracking with %4$s. If you skip this, that\'s okay! %1$s will still work just fine.', 'connect-message_on-update', $slug );
200
 
201
- // If user customized the opt-in message on update, use
 
 
 
 
 
 
 
 
 
 
 
202
  // that message. Otherwise, fallback to regular opt-in
203
- // custom message if exist.
204
  if ( $fs->has_filter( 'connect_message_on_update' ) ) {
205
  $filter = 'connect_message_on_update';
206
  }
@@ -208,17 +262,12 @@
208
 
209
  $message = $fs->apply_filters(
210
  $filter,
211
- ($is_network_upgrade_mode ?
212
- '' :
213
- /* translators: %s: name (e.g. Hey John,) */
214
- $hey_x_text . '<br>'
215
- ) .
216
  sprintf(
217
- esc_html( $default_optin_message ),
218
  '<b>' . esc_html( $fs->get_plugin_name() ) . '</b>',
219
  '<b>' . $current_user->user_login . '</b>',
220
  '<a href="' . $site_url . '" target="_blank" rel="noopener noreferrer">' . $site_url . '</a>',
221
- $freemius_link
222
  ),
223
  $first_name,
224
  $fs->get_plugin_name(),
@@ -266,7 +315,7 @@
266
  * @author Vova Feldman
267
  * @since 2.1.2
268
  */
269
- $fs->do_action( 'connect/after_license_input' );
270
  ?>
271
 
272
  <?php
@@ -311,8 +360,12 @@
311
  echo fs_get_template( 'partials/network-activation.php', $vars );
312
  ?>
313
  <?php endif ?>
 
 
314
  </div>
315
  <div class="fs-actions">
 
 
316
  <?php if ( $fs->is_enable_anonymous() && ! $is_pending_activation && ( ! $require_license_key || $is_network_upgrade_mode ) ) : ?>
317
  <a id="skip_activation" href="<?php echo fs_nonce_url( $fs->_get_admin_page_url( '', array( 'fs_action' => $fs->get_unique_affix() . '_skip_activation' ), $is_network_level_activation ), $fs->get_unique_affix() . '_skip_activation' ) ?>"
318
  class="button button-secondary" tabindex="2"><?php fs_esc_html_echo_x_inline( 'Skip', 'verb', 'skip', $slug ) ?></a>
@@ -326,6 +379,7 @@
326
  value="<?php echo $fs->get_unique_affix() ?>_activate_existing">
327
  <?php wp_nonce_field( 'activate_existing_' . $fs->get_public_key() ) ?>
328
  <input type="hidden" name="is_extensions_tracking_allowed" value="1">
 
329
  <button class="button button-primary" tabindex="1"
330
  type="submit"><?php echo esc_html( $button_label ) ?></button>
331
  </form>
@@ -336,6 +390,7 @@
336
  <input type="hidden" name="<?php echo $name ?>" value="<?php echo esc_attr( $value ) ?>">
337
  <?php endforeach ?>
338
  <input type="hidden" name="is_extensions_tracking_allowed" value="1">
 
339
  <button class="button button-primary" tabindex="1"
340
  type="submit"<?php if ( $require_license_key ) {
341
  echo ' disabled="disabled"';
@@ -345,109 +400,42 @@
345
  <?php if ( $require_license_key ) : ?>
346
  <a id="license_issues_link" href="<?php echo $fs->apply_filters( 'known_license_issues_url', 'https://freemius.com/help/documentation/wordpress-sdk/license-activation-issues/' ) ?>" target="_blank"><?php fs_esc_html_echo_inline( 'License issues?', 'license-issues', $slug ) ?></a>
347
  <?php endif ?>
 
 
348
  </div><?php
 
349
 
350
  // Set core permission list items.
351
  $permissions = array();
352
 
353
- /**
354
- * When activating a license key the information of the admin is not collected, we gather the user info from the license.
355
- *
356
- * @since 2.3.2
357
- * @author Vova Feldman
358
- */
359
- if ( ! $require_license_key ) {
360
- $permissions['profile'] = array(
361
- 'icon-class' => 'dashicons dashicons-admin-users',
362
- 'label' => $fs->get_text_inline( 'Your Profile Overview', 'permissions-profile' ),
363
- 'desc' => $fs->get_text_inline( 'Name and email address', 'permissions-profile_desc' ),
364
- 'priority' => 5,
365
- );
366
- }
367
-
368
- $permissions['site'] = array(
369
- 'icon-class' => 'dashicons dashicons-admin-settings',
370
- 'tooltip' => ( $require_license_key ? sprintf( $fs->get_text_inline( 'So you can manage and control your license remotely from the User Dashboard.', 'permissions-site_tooltip' ), $fs->get_module_type() ) : '' ),
371
- 'label' => $fs->get_text_inline( 'Your Site Overview', 'permissions-site' ),
372
- 'desc' => $fs->get_text_inline( 'Site URL, WP version, PHP info', 'permissions-site_desc' ),
373
- 'priority' => 10,
374
- );
375
-
376
- if ( ! $require_license_key ) {
377
- $permissions['notices'] = array(
378
- 'icon-class' => 'dashicons dashicons-testimonial',
379
- 'label' => $fs->get_text_inline( 'Admin Notices', 'permissions-admin-notices' ),
380
- 'desc' => $fs->get_text_inline( 'Updates, announcements, marketing, no spam', 'permissions-newsletter_desc' ),
381
- 'priority' => 13,
382
- );
383
- }
384
-
385
- $permissions['events'] = array(
386
- 'icon-class' => 'dashicons dashicons-admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ),
387
- 'tooltip' => ( $require_license_key ? sprintf( $fs->get_text_inline( 'So you can reuse the license when the %s is no longer active.', 'permissions-events_tooltip' ), $fs->get_module_type() ) : '' ),
388
- 'label' => sprintf( $fs->get_text_inline( 'Current %s Status', 'permissions-events' ), ucfirst( $fs->get_module_type() ) ),
389
- 'desc' => $fs->get_text_inline( 'Active, deactivated, or uninstalled', 'permissions-events_desc' ),
390
- 'priority' => 20,
391
- );
392
-
393
  // Add newsletter permissions if enabled.
394
  if ( $is_gdpr_required || $fs->is_permission_requested( 'newsletter' ) ) {
395
- $permissions['newsletter'] = array(
396
- 'icon-class' => 'dashicons dashicons-email-alt',
397
- 'label' => $fs->get_text_inline( 'Newsletter', 'permissions-newsletter' ),
398
- 'desc' => $fs->get_text_inline( 'Updates, announcements, marketing, no spam', 'permissions-newsletter_desc' ),
399
- 'priority' => 15,
400
- );
401
  }
402
 
403
- $permissions['extensions'] = array(
404
- 'icon-class' => 'dashicons dashicons-menu',
405
- 'label' => $fs->get_text_inline( 'Plugins & Themes', 'permissions-extensions' ) . ( $require_license_key ? ' (' . $fs->get_text_inline( 'optional' ) . ')' : '' ),
406
- 'tooltip' => $fs->get_text_inline( 'To help us troubleshoot any potential issues that may arise from other plugin or theme conflicts.', 'permissions-events_tooltip' ),
407
- 'desc' => $fs->get_text_inline( 'Title, slug, version, and is active', 'permissions-extensions_desc' ),
408
- 'priority' => 25,
409
- 'optional' => true,
410
- 'default' => $fs->apply_filters( 'permission_extensions_default', ! $require_license_key )
411
  );
412
 
413
- // Allow filtering of the permissions list.
414
- $permissions = $fs->apply_filters( 'permission_list', $permissions );
415
-
416
- // Sort by priority.
417
- uasort( $permissions, 'fs_sort_by_priority' );
418
-
419
  if ( ! empty( $permissions ) ) : ?>
420
  <div class="fs-permissions">
421
- <?php if ( $require_license_key ) : ?>
422
- <p class="fs-license-sync-disclaimer"><?php
423
- echo sprintf(
424
- fs_esc_html_inline( 'The %1$s will periodically send %2$s to %3$s for security & feature updates delivery, and license management.', 'license-sync-disclaimer', $slug ),
425
- $fs->get_module_label( true ),
426
- sprintf('<a class="fs-trigger" href="#" tabindex="1">%s</a>', fs_esc_html_inline('diagnostic data', 'send-data')),
427
- '<a class="fs-tooltip-trigger' . (is_rtl() ? ' rtl' : '') . '" href="' . $freemius_site_url . '" target="_blank" rel="noopener" tabindex="1">freemius.com <i class="dashicons dashicons-editor-help" style="text-decoration: none;"><span class="fs-tooltip" style="width: 170px">' . $fs->get_text_inline( 'Freemius is our licensing and software updates engine', 'permissions-extensions_desc' ) . '</span></i></a>'
428
- ) ?></p>
429
- <?php else : ?>
430
- <a class="fs-trigger" href="#" tabindex="1"><?php fs_esc_html_echo_inline( 'What permissions are being granted?', 'what-permissions', $slug ) ?></a>
431
  <?php endif ?>
432
  <ul><?php
433
- foreach ( $permissions as $id => $permission ) : ?>
434
- <li id="fs-permission-<?php echo esc_attr( $id ); ?>"
435
- class="fs-permission fs-<?php echo esc_attr( $id ); ?>">
436
- <i class="<?php echo esc_attr( $permission['icon-class'] ); ?>"></i>
437
- <?php if ( isset( $permission['optional'] ) && true === $permission['optional'] ) : ?>
438
- <div class="fs-switch fs-small fs-round fs-<?php echo (! isset( $permission['default'] ) || true === $permission['default'] ) ? 'on' : 'off' ?>">
439
- <div class="fs-toggle"></div>
440
- </div>
441
- <?php endif ?>
442
-
443
- <div class="fs-permission-description">
444
- <span<?php if ( ! empty($permission['tooltip']) ) : ?> class="fs-tooltip-trigger"<?php endif ?>><?php echo esc_html( $permission['label'] ); ?><?php if ( ! empty($permission['tooltip']) ) : ?><i class="dashicons dashicons-editor-help"><span class="fs-tooltip" style="width: 200px"><?php echo $permission['tooltip'] ?></span></i><?php endif ?></span>
445
-
446
- <p><?php echo esc_html( $permission['desc'] ); ?></p>
447
- </div>
448
- </li>
449
- <?php endforeach; ?>
450
- </ul>
451
  </div>
452
  <?php endif ?>
453
  <?php if ( $is_premium_code && $is_freemium ) : ?>
@@ -463,7 +451,10 @@
463
  </p>
464
  </div>
465
  <?php endif ?>
 
466
  <div class="fs-terms">
 
 
467
  <a href="https://freemius.com/privacy/" target="_blank" rel="noopener"
468
  tabindex="1"><?php fs_esc_html_echo_inline( 'Privacy Policy', 'privacy-policy', $slug ) ?></a>
469
  &nbsp;&nbsp;-&nbsp;&nbsp;
@@ -477,7 +468,7 @@
477
  * @author Vova Feldman
478
  * @since 2.3.2
479
  */
480
- $fs->do_action( 'connect/after' );
481
 
482
  if ( $is_optin_dialog ) { ?>
483
  </div>
@@ -531,13 +522,12 @@
531
  // Reset loading mode.
532
  $primaryCta.html(primaryCtaLabel);
533
  $primaryCta.prop('disabled', false);
534
- $(document.body).css({'cursor': 'auto'});
535
- $('.fs-loading').removeClass('fs-loading');
536
 
537
  console.log('resetLoadingMode - Primary button was enabled');
538
  },
539
  setLoadingMode = function () {
540
- $(document.body).css({'cursor': 'wait'});
541
  };
542
 
543
  $('.fs-actions .button').on('click', function () {
@@ -601,6 +591,11 @@
601
  updatePrimaryCtaText( actionType );
602
  });
603
 
 
 
 
 
 
604
  $sitesListContainer.delegate( '.action', 'click', function( evt ) {
605
  var $this = $( evt.target );
606
  if ( $this.hasClass( 'selected' ) ) {
@@ -707,15 +702,25 @@
707
  var ajaxOptin = ( requireLicenseKey || isNetworkActive );
708
 
709
  $form.on('submit', function () {
710
- var $extensionsPermission = $('#fs-permission-extensions .fs-switch'),
711
- isExtensionsTrackingAllowed = ($extensionsPermission.length > 0) ?
712
- $extensionsPermission.hasClass('fs-on') :
713
  null;
714
 
715
- if (null === isExtensionsTrackingAllowed) {
716
- $('input[name=is_extensions_tracking_allowed]').remove();
 
 
 
 
 
717
  } else {
718
- $('input[name=is_extensions_tracking_allowed]').val(isExtensionsTrackingAllowed ? 1 : 0);
 
 
 
 
 
719
  }
720
 
721
  /**
@@ -772,6 +777,8 @@
772
  data.is_marketing_allowed = isMarketingAllowed;
773
 
774
  data.is_extensions_tracking_allowed = isExtensionsTrackingAllowed;
 
 
775
  }
776
 
777
  $marketingOptin.removeClass( 'error' );
@@ -794,7 +801,6 @@
794
  url : $this.find( '.url' ).val(),
795
  title : $this.find( '.title' ).val(),
796
  language: $this.find( '.language' ).val(),
797
- charset : $this.find( '.charset' ).val(),
798
  blog_id : $this.find( '.blog-id' ).find( 'span' ).text()
799
  };
800
 
@@ -822,7 +828,7 @@
822
  * @since 1.2.1.5
823
  */
824
  $.ajax({
825
- url : ajaxurl,
826
  method : 'POST',
827
  data : data,
828
  success: function (result) {
@@ -858,6 +864,15 @@
858
  return true;
859
  });
860
 
 
 
 
 
 
 
 
 
 
861
  $primaryCta.on('click', function () {
862
  console.log('Primary button was clicked');
863
 
@@ -874,12 +889,6 @@
874
  return false;
875
  });
876
 
877
- $( '.fs-switch' ).click( function () {
878
- $(this)
879
- .toggleClass( 'fs-on' )
880
- .toggleClass( 'fs-off' );
881
- });
882
-
883
  if (requireLicenseKey) {
884
  /**
885
  * Submit license key on enter.
@@ -1005,7 +1014,7 @@
1005
  $primaryCta.html('<?php fs_esc_js_echo_inline( 'Please wait', 'please-wait', $slug ) ?>...');
1006
 
1007
  $.ajax({
1008
- url : ajaxurl,
1009
  method : 'POST',
1010
  data : {
1011
  action : '<?php echo $fs->get_ajax_action( 'fetch_is_marketing_required_flag_value' ) ?>',
@@ -1035,4 +1044,4 @@
1035
 
1036
  //endregion
1037
  })(jQuery);
1038
- </script>
25
 
26
  $fs->_enqueue_connect_essentials();
27
 
28
+ /**
29
+ * Enqueueing the styles in `_enqueue_connect_essentials()` is too late, as we need them in the HEADER. Therefore, inject the styles inline to avoid FOUC.
30
+ *
31
+ * @author Vova Feldman (@svovaf)
32
+ */
33
+ echo "<style>\n";
34
+ include WP_FS__DIR_CSS . '/admin/connect.css';
35
+ echo "</style>\n";
36
+
37
  $current_user = Freemius::_get_current_wp_user();
38
 
39
  $first_name = $current_user->user_firstname;
41
  $first_name = $current_user->nickname;
42
  }
43
 
44
+ $site_url = Freemius::get_unfiltered_site_url();
45
  $protocol_pos = strpos( $site_url, '://' );
46
  if ( false !== $protocol_pos ) {
47
  $site_url = substr( $site_url, $protocol_pos + 3 );
127
  if ( is_null( $is_gdpr_required ) ) {
128
  $is_gdpr_required = $fs->fetch_and_store_current_user_gdpr_anonymously();
129
  }
130
+
131
+ $activation_state = array(
132
+ 'is_license_activation' => $require_license_key,
133
+ 'is_pending_activation' => $is_pending_activation,
134
+ 'is_gdpr_required' => $is_gdpr_required,
135
+ 'is_network_level_activation' => $is_network_level_activation,
136
+ 'is_dialog' => $is_optin_dialog,
137
+ );
138
  ?>
139
  <?php
140
  if ( $is_optin_dialog ) { ?>
155
  * @author Vova Feldman
156
  * @since 2.3.2
157
  */
158
+ $fs->do_action( 'connect/before', $activation_state );
159
  ?>
160
  <div id="fs_connect"
161
  class="wrap<?php if ( ! fs_is_network_admin() && ( ! $fs->is_enable_anonymous() || $is_pending_activation || $require_license_key ) ) {
162
  echo ' fs-anonymous-disabled';
163
  } ?><?php echo $require_license_key ? ' require-license-key' : '' ?>">
164
+ <div class="fs-header">
165
+ <!-- <b class="fs-site-icon"><i class="dashicons dashicons-wordpress-alt"></i></b>-->
166
+ <?php
167
+ $size = 50;
168
+ $vars = array(
169
+ 'id' => $fs->get_id(),
170
+ 'size' => $size,
171
+ );
172
+
173
+ fs_require_once_template( 'plugin-icon.php', $vars );
174
+ ?>
175
+ <!-- <img class="fs-connect-logo" width="--><?php //echo $size ?><!--" height="--><?php //echo $size ?><!--" src="//img.freemius.com/logo/connect.svg"/>-->
176
+ </div>
177
+ <div class="fs-box-container">
178
  <div class="fs-content">
179
+ <?php $fs->do_action( 'connect/before_message', $activation_state ) ?>
180
+
181
  <?php if ( ! empty( $error ) ) : ?>
182
+ <p class="fs-error"><?php echo $fs->apply_filters( 'connect_error_esc_html', esc_html( $error ) ) ?></p>
183
  <?php endif ?>
184
+ <?php
185
+ if ( ! $is_pending_activation && ! $require_license_key ) {
186
+ if ( ! $fs->is_plugin_update() ) {
187
+ echo $fs->apply_filters( 'connect-header', sprintf(
188
+ '<h2 style="text-align: center">%s</h2>',
189
+ esc_html( fs_text_inline( 'Never miss an important update', 'connect-header' ) )
190
+ ) );
191
+ } else {
192
+ echo $fs->apply_filters( 'connect-header_on-update', sprintf(
193
+ '<h2>%s</h2>',
194
+ sprintf(
195
+ esc_html(
196
+ /* translators: %1$s: plugin name (e.g., "Awesome Plugin"); %2$s: version (e.g., "1.2.3") */
197
+ fs_text_inline('Thank you for updating to %1$s v%2$s!', 'connect-header_on-update' )
198
+ ),
199
+ esc_html( $fs->get_plugin_name() ),
200
+ $fs->get_plugin_version()
201
+ )
202
+ ) );
203
+ }
204
+ }
205
+ ?>
206
  <p><?php
207
  $button_label = fs_text_inline( 'Allow & Continue', 'opt-in-connect', $slug );
208
  $message = '';
213
  $message = $fs->apply_filters( 'pending_activation_message', sprintf(
214
  /* translators: %s: name (e.g. Thanks John!) */
215
  fs_text_inline( 'Thanks %s!', 'thanks-x', $slug ) . '<br>' .
216
+ fs_text_inline( 'You should receive a confirmation email for %s to your mailbox at %s. Please make sure you click the button in that email to %s.', 'pending-activation-message', $slug ),
217
  $first_name,
218
  '<b>' . $fs->get_plugin_name() . '</b>',
219
  '<b>' . $current_user->user_email . '</b>',
220
+ fs_text_inline( 'complete the opt-in', 'complete-the-opt-in', $slug )
221
  ) );
222
  } else if ( $require_license_key ) {
223
+ $button_label = fs_text_inline( 'Activate License', 'activate-license', $slug );
 
 
224
 
225
  $message = $fs->apply_filters(
226
  'connect-message_on-premium',
229
  $fs->get_plugin_name()
230
  );
231
  } else {
232
+ $filter = 'connect_message';
 
 
 
233
 
234
+ if ( ! $fs->is_plugin_update() ) {
235
+ $default_optin_message = esc_html( sprintf( ( $is_gdpr_required ?
236
+ /* translators: %s: module type (plugin, theme, or add-on) */
237
+ fs_text_inline( 'Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to.', 'connect-message', $slug ) :
238
+ /* translators: %s: module type (plugin, theme, or add-on) */
239
+ fs_text_inline( 'Opt in to get email notifications for security & feature updates, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to.', 'connect-message', $slug ) ), $fs->get_module_label( true ) ) );
240
+ } else {
241
  // If Freemius was added on a plugin update, set different
242
  // opt-in message.
 
 
 
243
 
244
+ /* translators: %s: module type (plugin, theme, or add-on) */
245
+ $default_optin_message = esc_html( sprintf( fs_text_inline( 'We have introduced this opt-in so you never miss an important update and help us make the %s more compatible with your site and better at doing what you need it to.', 'connect-message_on-update_why' ), $fs->get_module_label( true ) ) );
246
+
247
+ $default_optin_message .= '<br><br>' . esc_html( $is_gdpr_required ?
248
+ fs_text_inline( 'Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info.', 'connect-message_on-update', $slug ) :
249
+ fs_text_inline( 'Opt in to get email notifications for security & feature updates, and to share some basic WordPress environment info.', 'connect-message_on-update', $slug ) );
250
+
251
+ if ( $fs->is_enable_anonymous() ) {
252
+ $default_optin_message .= ' ' . esc_html( fs_text_inline( 'If you skip this, that\'s okay! %1$s will still work just fine.', 'connect-message_on-update_skip', $slug ) );
253
+ }
254
+
255
+ // If user customized the opt-in message on update, use
256
  // that message. Otherwise, fallback to regular opt-in
257
+ // custom message if exists.
258
  if ( $fs->has_filter( 'connect_message_on_update' ) ) {
259
  $filter = 'connect_message_on_update';
260
  }
262
 
263
  $message = $fs->apply_filters(
264
  $filter,
 
 
 
 
 
265
  sprintf(
266
+ $default_optin_message,
267
  '<b>' . esc_html( $fs->get_plugin_name() ) . '</b>',
268
  '<b>' . $current_user->user_login . '</b>',
269
  '<a href="' . $site_url . '" target="_blank" rel="noopener noreferrer">' . $site_url . '</a>',
270
+ $freemius_link
271
  ),
272
  $first_name,
273
  $fs->get_plugin_name(),
315
  * @author Vova Feldman
316
  * @since 2.1.2
317
  */
318
+ $fs->do_action( 'connect/after_license_input', $activation_state );
319
  ?>
320
 
321
  <?php
360
  echo fs_get_template( 'partials/network-activation.php', $vars );
361
  ?>
362
  <?php endif ?>
363
+
364
+ <?php $fs->do_action( 'connect/after_message', $activation_state ) ?>
365
  </div>
366
  <div class="fs-actions">
367
+ <?php $fs->do_action( 'connect/before_actions', $activation_state ) ?>
368
+
369
  <?php if ( $fs->is_enable_anonymous() && ! $is_pending_activation && ( ! $require_license_key || $is_network_upgrade_mode ) ) : ?>
370
  <a id="skip_activation" href="<?php echo fs_nonce_url( $fs->_get_admin_page_url( '', array( 'fs_action' => $fs->get_unique_affix() . '_skip_activation' ), $is_network_level_activation ), $fs->get_unique_affix() . '_skip_activation' ) ?>"
371
  class="button button-secondary" tabindex="2"><?php fs_esc_html_echo_x_inline( 'Skip', 'verb', 'skip', $slug ) ?></a>
379
  value="<?php echo $fs->get_unique_affix() ?>_activate_existing">
380
  <?php wp_nonce_field( 'activate_existing_' . $fs->get_public_key() ) ?>
381
  <input type="hidden" name="is_extensions_tracking_allowed" value="1">
382
+ <input type="hidden" name="is_diagnostic_tracking_allowed" value="1">
383
  <button class="button button-primary" tabindex="1"
384
  type="submit"><?php echo esc_html( $button_label ) ?></button>
385
  </form>
390
  <input type="hidden" name="<?php echo $name ?>" value="<?php echo esc_attr( $value ) ?>">
391
  <?php endforeach ?>
392
  <input type="hidden" name="is_extensions_tracking_allowed" value="1">
393
+ <input type="hidden" name="is_diagnostic_tracking_allowed" value="1">
394
  <button class="button button-primary" tabindex="1"
395
  type="submit"<?php if ( $require_license_key ) {
396
  echo ' disabled="disabled"';
400
  <?php if ( $require_license_key ) : ?>
401
  <a id="license_issues_link" href="<?php echo $fs->apply_filters( 'known_license_issues_url', 'https://freemius.com/help/documentation/wordpress-sdk/license-activation-issues/' ) ?>" target="_blank"><?php fs_esc_html_echo_inline( 'License issues?', 'license-issues', $slug ) ?></a>
402
  <?php endif ?>
403
+
404
+ <?php $fs->do_action( 'connect/after_actions', $activation_state ) ?>
405
  </div><?php
406
+ $permission_manager = FS_Permission_Manager::instance( $fs );
407
 
408
  // Set core permission list items.
409
  $permissions = array();
410
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  // Add newsletter permissions if enabled.
412
  if ( $is_gdpr_required || $fs->is_permission_requested( 'newsletter' ) ) {
413
+ $permissions[] = $permission_manager->get_newsletter_permission();
 
 
 
 
 
414
  }
415
 
416
+ $permissions = $permission_manager->get_permissions(
417
+ $require_license_key,
418
+ $permissions
 
 
 
 
 
419
  );
420
 
 
 
 
 
 
 
421
  if ( ! empty( $permissions ) ) : ?>
422
  <div class="fs-permissions">
423
+ <?php if ( $require_license_key ) : ?>
424
+ <a class="fs-trigger wp-core-ui" href="#" tabindex="1" style="color: inherit;"><?php echo sprintf(
425
+ fs_esc_html_inline( 'For delivery of security & feature updates, and license management, %s needs to', 'license-sync-disclaimer', $slug ) . '<b class="fs-arrow"></b>',
426
+ sprintf( '<nobr class="button-link" style="color: inherit;">%s</nobr>', $fs->get_plugin_title() )
427
+ ) ?></a>
428
+ <?php else : ?>
429
+ <a class="fs-trigger wp-core-ui" href="#" tabindex="1" style="color: inherit;"><?php printf(
430
+ fs_esc_html_inline( 'This will allow %s to', 'this-will-allow-x', $slug ) . '<b class="fs-arrow"></b>',
431
+ sprintf( '<nobr class="button-link" style="color: inherit;">%s</nobr>', $fs->get_plugin_title() )
432
+ ) ?></a>
433
  <?php endif ?>
434
  <ul><?php
435
+ foreach ( $permissions as $permission ) {
436
+ $permission_manager->render_permission( $permission );
437
+ }
438
+ ?></ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  </div>
440
  <?php endif ?>
441
  <?php if ( $is_premium_code && $is_freemium ) : ?>
451
  </p>
452
  </div>
453
  <?php endif ?>
454
+ </div>
455
  <div class="fs-terms">
456
+ <a class="fs-tooltip-trigger<?php echo is_rtl() ? ' rtl' : '' ?>" href="<?php echo $freemius_site_url ?>" target="_blank" rel="noopener" tabindex="1">Powered by Freemius<?php if ( $require_license_key ) : ?> <span class="fs-tooltip" style="width: 170px"><?php echo $fs->get_text_inline( 'Freemius is our licensing and software updates engine', 'permissions-extensions_desc' ) ?></span><?php endif ?></a>
457
+ &nbsp;&nbsp;-&nbsp;&nbsp;
458
  <a href="https://freemius.com/privacy/" target="_blank" rel="noopener"
459
  tabindex="1"><?php fs_esc_html_echo_inline( 'Privacy Policy', 'privacy-policy', $slug ) ?></a>
460
  &nbsp;&nbsp;-&nbsp;&nbsp;
468
  * @author Vova Feldman
469
  * @since 2.3.2
470
  */
471
+ $fs->do_action( 'connect/after', $activation_state );
472
 
473
  if ( $is_optin_dialog ) { ?>
474
  </div>
522
  // Reset loading mode.
523
  $primaryCta.html(primaryCtaLabel);
524
  $primaryCta.prop('disabled', false);
525
+ $( '.fs-loading' ).removeClass( 'fs-loading' );
 
526
 
527
  console.log('resetLoadingMode - Primary button was enabled');
528
  },
529
  setLoadingMode = function () {
530
+ $( document.body ).addClass( 'fs-loading' );
531
  };
532
 
533
  $('.fs-actions .button').on('click', function () {
591
  updatePrimaryCtaText( actionType );
592
  });
593
 
594
+ $sitesListContainer.delegate( 'td:not(:first-child)', 'click', function() {
595
+ // If a site row is clicked, trigger a click on the checkbox.
596
+ $( this ).parent().find( 'td:first-child input' ).click();
597
+ } );
598
+
599
  $sitesListContainer.delegate( '.action', 'click', function( evt ) {
600
  var $this = $( evt.target );
601
  if ( $this.hasClass( 'selected' ) ) {
702
  var ajaxOptin = ( requireLicenseKey || isNetworkActive );
703
 
704
  $form.on('submit', function () {
705
+ var $extensionsPermission = $( '#fs_permission_extensions .fs-switch' ),
706
+ isExtensionsTrackingAllowed = ( $extensionsPermission.length > 0 ) ?
707
+ $extensionsPermission.hasClass( 'fs-on' ) :
708
  null;
709
 
710
+ var $diagnosticPermission = $( '#fs_permission_diagnostic .fs-switch' ),
711
+ isDiagnosticTrackingAllowed = ( $diagnosticPermission.length > 0 ) ?
712
+ $diagnosticPermission.hasClass( 'fs-on' ) :
713
+ null;
714
+
715
+ if ( null === isExtensionsTrackingAllowed ) {
716
+ $( 'input[name=is_extensions_tracking_allowed]' ).remove();
717
  } else {
718
+ $( 'input[name=is_extensions_tracking_allowed]' ).val( isExtensionsTrackingAllowed ? 1 : 0 );
719
+ }
720
+
721
+ // We are not showing switch to enable/disable diagnostic tracking while activating free version. So, don't remove hidden `is_diagnostic_tracking_allowed` element from DOM and change the value only if switch is available.
722
+ if ( null !== isDiagnosticTrackingAllowed ) {
723
+ $( 'input[name=is_diagnostic_tracking_allowed]' ).val( isDiagnosticTrackingAllowed ? 1 : 0 );
724
  }
725
 
726
  /**
777
  data.is_marketing_allowed = isMarketingAllowed;
778
 
779
  data.is_extensions_tracking_allowed = isExtensionsTrackingAllowed;
780
+
781
+ data.is_diagnostic_tracking_allowed = isDiagnosticTrackingAllowed;
782
  }
783
 
784
  $marketingOptin.removeClass( 'error' );
801
  url : $this.find( '.url' ).val(),
802
  title : $this.find( '.title' ).val(),
803
  language: $this.find( '.language' ).val(),
 
804
  blog_id : $this.find( '.blog-id' ).find( 'span' ).text()
805
  };
806
 
828
  * @since 1.2.1.5
829
  */
830
  $.ajax({
831
+ url : <?php echo Freemius::ajax_url() ?>,
832
  method : 'POST',
833
  data : data,
834
  success: function (result) {
864
  return true;
865
  });
866
 
867
+ $( '#fs_connect .fs-permissions .fs-switch' ).on( 'click', function () {
868
+ $( this )
869
+ .toggleClass( 'fs-on' )
870
+ .toggleClass( 'fs-off' );
871
+
872
+ $( this ).closest( '.fs-permission' )
873
+ .toggleClass( 'fs-disabled' );
874
+ });
875
+
876
  $primaryCta.on('click', function () {
877
  console.log('Primary button was clicked');
878
 
889
  return false;
890
  });
891
 
 
 
 
 
 
 
892
  if (requireLicenseKey) {
893
  /**
894
  * Submit license key on enter.
1014
  $primaryCta.html('<?php fs_esc_js_echo_inline( 'Please wait', 'please-wait', $slug ) ?>...');
1015
 
1016
  $.ajax({
1017
+ url : <?php echo Freemius::ajax_url() ?>,
1018
  method : 'POST',
1019
  data : {
1020
  action : '<?php echo $fs->get_ajax_action( 'fetch_is_marketing_required_flag_value' ) ?>',
1044
 
1045
  //endregion
1046
  })(jQuery);
1047
+ </script>
freemius/templates/connect/index.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+ // Hide file structure from users on unprotected servers.
freemius/templates/connect/permission.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2022, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.1
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * @var array $VARS
15
+ * @var array $permission {
16
+ * @type string $id
17
+ * @type bool $default
18
+ * @type string $icon-class
19
+ * @type bool $optional
20
+ * @type string $label
21
+ * @type string $tooltip
22
+ * @type string $desc
23
+ * }
24
+ */
25
+ $permission = $VARS;
26
+
27
+ $is_permission_on = ( ! isset( $permission['default'] ) || true === $permission['default'] );
28
+ ?>
29
+ <li id="fs_permission_<?php echo esc_attr( $permission['id'] ) ?>" data-permission-id="<?php echo esc_attr( $permission['id'] ) ?>"
30
+ class="fs-permission fs-<?php echo esc_attr( $permission['id'] ); ?><?php echo ( ! $is_permission_on ) ? ' fs-disabled' : ''; ?>">
31
+ <i class="<?php echo esc_attr( $permission['icon-class'] ); ?>"></i>
32
+ <?php if ( isset( $permission['optional'] ) && true === $permission['optional'] ) : ?>
33
+ <div class="fs-switch fs-small fs-round fs-<?php echo $is_permission_on ? 'on' : 'off' ?>">
34
+ <div class="fs-toggle"></div>
35
+ </div>
36
+ <?php endif ?>
37
+
38
+ <div class="fs-permission-description">
39
+ <span<?php if ( ! empty( $permission['tooltip'] ) ) : ?> class="fs-tooltip-trigger"<?php endif ?>><?php echo esc_html( $permission['label'] ); ?><?php if ( ! empty( $permission['tooltip'] ) ) : ?><i class="dashicons dashicons-editor-help"><span class="fs-tooltip" style="width: 200px"><?php echo esc_html( $permission['tooltip'] ) ?></span></i><?php endif ?></span>
40
+
41
+ <p><?php echo esc_html( $permission['desc'] ); ?></p>
42
+ </div>
43
+ </li>
freemius/templates/connect/permissions-group.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2022, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.1
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * @var array $VARS
15
+ *
16
+ * @var array $permission_group {
17
+ * @type Freemius $fs
18
+ * @type string $id
19
+ * @type string $desc
20
+ * @type array $prompt
21
+ * @type array $permissions
22
+ * @type bool $is_enabled
23
+ * }
24
+ */
25
+ $permission_group = $VARS;
26
+
27
+ $fs = $permission_group[ 'fs' ];
28
+
29
+ $permission_manager = FS_Permission_Manager::instance( $fs );
30
+
31
+ $opt_out_text = $fs->get_text_x_inline( 'Opt Out', 'verb', 'opt-out' );
32
+ $opt_in_text = $fs->get_text_x_inline( 'Opt In', 'verb', 'opt-in' );
33
+
34
+ if ( empty( $permission_group[ 'prompt' ] ) ) {
35
+ $is_enabled = false;
36
+
37
+ foreach ( $permission_group[ 'permissions' ] as $permission ) {
38
+ if ( true === $permission[ 'default' ] ) {
39
+ // Even if one of the permissions is on, treat as if the entire group is on.
40
+ $is_enabled = true;
41
+ break;
42
+ }
43
+ }
44
+ } else {
45
+ $is_enabled = ( isset( $permission_group['is_enabled'] ) && true === $permission_group['is_enabled'] );
46
+ }
47
+ ?>
48
+ <div class="fs-permissions-section fs-<?php echo esc_attr( $permission_group[ 'id' ] ) ?>-permissions">
49
+ <div>
50
+ <div class="fs-permissions-section--header">
51
+ <a class="fs-group-opt-out-button"
52
+ data-type="<?php echo esc_attr( $permission_group['type'] ) ?>"
53
+ data-group-id="<?php echo esc_attr( $permission_group[ 'id' ] ) ?>"
54
+ data-is-enabled="<?php echo $is_enabled ? 'true' : 'false' ?>"
55
+ href="#"><?php echo esc_html( $is_enabled ? $opt_out_text : $opt_in_text ) ?></a>
56
+ <span class="fs-permissions-section--header-title"><?php
57
+ // The title is already HTML-escaped.
58
+ echo $permission_group[ 'title' ]
59
+ ?></span>
60
+ </div>
61
+ <p class="fs-permissions-section--desc"><?php
62
+ // The description is already HTML-escaped.
63
+ echo $permission_group['desc']
64
+ ?></p></div>
65
+ <ul>
66
+ <?php
67
+ foreach ( $permission_group['permissions'] as $permission ) {
68
+ $permission_manager->render_permission( $permission );
69
+ }
70
+ ?>
71
+ </ul>
72
+ </div>
freemius/templates/contact.php CHANGED
@@ -69,7 +69,7 @@
69
  $query_params = array_merge( $_GET, array_merge( $context_params, array(
70
  'plugin_version' => $fs->get_plugin_version(),
71
  'wp_login_url' => wp_login_url(),
72
- 'site_url' => get_site_url(),
73
  // 'wp_admin_css' => get_bloginfo('wpurl') . "/wp-admin/load-styles.php?c=1&load=buttons,wp-admin,dashicons",
74
  ) ) );
75
 
69
  $query_params = array_merge( $_GET, array_merge( $context_params, array(
70
  'plugin_version' => $fs->get_plugin_version(),
71
  'wp_login_url' => wp_login_url(),
72
+ 'site_url' => Freemius::get_unfiltered_site_url(),
73
  // 'wp_admin_css' => get_bloginfo('wpurl') . "/wp-admin/load-styles.php?c=1&load=buttons,wp-admin,dashicons",
74
  ) ) );
75
 
freemius/templates/debug.php CHANGED
@@ -16,6 +16,10 @@
16
 
17
  $off_text = fs_text_x_inline( 'Off', 'as turned off' );
18
  $on_text = fs_text_x_inline( 'On', 'as turned on' );
 
 
 
 
19
  ?>
20
  <h1><?php echo fs_text_inline( 'Freemius Debug' ) . ' - ' . fs_text_inline( 'SDK' ) . ' v.' . $fs_active_plugins->newest->version ?></h1>
21
  <div>
@@ -35,7 +39,7 @@
35
  .toggleClass( 'fs-on' )
36
  .toggleClass( 'fs-off' );
37
 
38
- $.post( ajaxurl, {
39
  action: 'fs_toggle_debug_mode',
40
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
41
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_toggle_debug_mode' ) ); ?>,
@@ -79,6 +83,16 @@
79
  <button class="button"><?php fs_esc_html_echo_inline( 'Clear Updates Transients' ) ?></button>
80
  </form>
81
  </td>
 
 
 
 
 
 
 
 
 
 
82
  <td>
83
  <!-- Sync Data with Server -->
84
  <form action="" method="POST">
@@ -102,6 +116,15 @@
102
  <td>
103
  <button id="fs_set_db_option" class="button"><?php fs_esc_html_echo_inline( 'Set DB Option' ) ?></button>
104
  </td>
 
 
 
 
 
 
 
 
 
105
  </tr>
106
  </tbody>
107
  </table>
@@ -111,7 +134,7 @@
111
  var optionName = prompt('Please enter the option name:');
112
 
113
  if (optionName) {
114
- $.post(ajaxurl, {
115
  action : 'fs_get_db_option',
116
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
117
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_db_option' ) ); ?>,
@@ -132,7 +155,7 @@
132
  var optionValue = prompt('Please enter the option value:');
133
 
134
  if (optionValue) {
135
- $.post(ajaxurl, {
136
  action : 'fs_set_db_option',
137
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
138
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_set_db_option' ) ); ?>,
@@ -175,6 +198,10 @@
175
  'key' => 'WP_FS__DIR',
176
  'val' => WP_FS__DIR,
177
  ),
 
 
 
 
178
  )
179
  ?>
180
  <br>
@@ -229,7 +256,7 @@
229
  WP_FS__MODULE_TYPE_THEME
230
  );
231
  ?>
232
-
233
  <?php foreach ( $module_types as $module_type ) : ?>
234
  <?php $modules = fs_get_entities( $fs_options->get_option( $module_type . 's' ), FS_Plugin::get_class_name() ) ?>
235
  <?php if ( is_array( $modules ) && count( $modules ) > 0 ) : ?>
@@ -245,7 +272,7 @@
245
  <th><?php fs_esc_html_echo_inline( 'Freemius State' ) ?></th>
246
  <th><?php fs_esc_html_echo_inline( 'Module Path' ) ?></th>
247
  <th><?php fs_esc_html_echo_inline( 'Public Key' ) ?></th>
248
- <?php if ( is_multisite() ) : ?>
249
  <th><?php fs_esc_html_echo_inline( 'Network Blog' ) ?></th>
250
  <th><?php fs_esc_html_echo_inline( 'Network User' ) ?></th>
251
  <?php endif ?>
@@ -268,7 +295,14 @@
268
  }
269
  }
270
  ?>
271
- <?php $fs = $is_active ? freemius( $data->id ) : null ?>
 
 
 
 
 
 
 
272
  <tr<?php if ( $is_active ) {
273
  if ( $fs->has_api_connectivity() && $fs->is_on() ) {
274
  echo ' style="background: #E6FFE6; font-weight: bold"';
@@ -298,7 +332,7 @@
298
  } ?></td>
299
  <td><?php echo $data->file ?></td>
300
  <td><?php echo $data->public_key ?></td>
301
- <?php if ( is_multisite() ) : ?>
302
  <?php
303
  $network_blog_id = null;
304
  $network_user = null;
@@ -352,8 +386,7 @@
352
  */
353
  $sites_map = $VARS[ $module_type . '_sites' ];
354
 
355
- $is_multisite = is_multisite();
356
- $all_plans = false;
357
  ?>
358
  <?php if ( is_array( $sites_map ) && count( $sites_map ) > 0 ) : ?>
359
  <h2><?php echo esc_html( sprintf(
@@ -379,15 +412,37 @@
379
  </tr>
380
  </thead>
381
  <tbody>
 
382
  <?php foreach ( $sites_map as $slug => $sites ) : ?>
383
- <?php if ( ! is_array( $sites ) ) {
384
- $sites = array( $sites );
385
- } ?>
386
  <?php foreach ( $sites as $site ) : ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  <tr>
388
- <td><?php echo $site->id ?></td>
 
 
 
 
 
389
  <?php if ( $is_multisite ) : ?>
390
- <td><?php echo $site->blog_id ?></td>
391
  <td><?php echo fs_strip_url_protocol( $site->url ) ?></td>
392
  <?php endif ?>
393
  <td><?php echo $slug ?></td>
@@ -484,8 +539,15 @@
484
  * @var FS_User[] $users
485
  */
486
  $users = $VARS['users'];
 
487
  $users_with_developer_license_by_id = array();
488
 
 
 
 
 
 
 
489
  foreach ( $module_types as $module_type ) {
490
  /**
491
  * @var FS_Plugin_License[] $licenses
@@ -578,7 +640,7 @@
578
  <td><?php echo $license->is_block_features ? 'Blocking' : 'Flexible' ?></td>
579
  <td><?php echo $license->is_whitelabeled ? 'Whitelabeled' : 'Normal' ?></td>
580
  <td><?php
581
- echo $license->is_whitelabeled ?
582
  $license->get_html_escaped_masked_secret_key() :
583
  esc_html( $license->secret_key );
584
  ?></td>
@@ -726,7 +788,7 @@
726
  offset = 0;
727
  }
728
 
729
- $.post(ajaxurl, {
730
  action : 'fs_get_debug_log',
731
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
732
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_debug_log' ) ); ?>,
16
 
17
  $off_text = fs_text_x_inline( 'Off', 'as turned off' );
18
  $on_text = fs_text_x_inline( 'On', 'as turned on' );
19
+
20
+ $has_any_active_clone = false;
21
+
22
+ $is_multisite = is_multisite();
23
  ?>
24
  <h1><?php echo fs_text_inline( 'Freemius Debug' ) . ' - ' . fs_text_inline( 'SDK' ) . ' v.' . $fs_active_plugins->newest->version ?></h1>
25
  <div>
39
  .toggleClass( 'fs-on' )
40
  .toggleClass( 'fs-off' );
41
 
42
+ $.post( <?php echo Freemius::ajax_url() ?>, {
43
  action: 'fs_toggle_debug_mode',
44
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
45
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_toggle_debug_mode' ) ); ?>,
83
  <button class="button"><?php fs_esc_html_echo_inline( 'Clear Updates Transients' ) ?></button>
84
  </form>
85
  </td>
86
+ <?php if ( Freemius::is_deactivation_snoozed() ) : ?>
87
+ <td>
88
+ <!-- Reset Deactivation Snoozing -->
89
+ <form action="" method="POST">
90
+ <input type="hidden" name="fs_action" value="reset_deactivation_snoozing">
91
+ <?php wp_nonce_field( 'reset_deactivation_snoozing' ) ?>
92
+ <button class="button"><?php fs_esc_html_echo_inline( 'Reset Deactivation Snoozing' ) ?> (Expires in <?php echo ( Freemius::deactivation_snooze_expires_at() - time() ) ?> sec)</button>
93
+ </form>
94
+ </td>
95
+ <?php endif ?>
96
  <td>
97
  <!-- Sync Data with Server -->
98
  <form action="" method="POST">
116
  <td>
117
  <button id="fs_set_db_option" class="button"><?php fs_esc_html_echo_inline( 'Set DB Option' ) ?></button>
118
  </td>
119
+ <td>
120
+ <?php
121
+ $fs_debug_page_url = 'admin.php?page=freemius&fs_action=allow_clone_resolution_notice';
122
+ $fs_debug_page_url = fs_is_network_admin() ?
123
+ network_admin_url( $fs_debug_page_url ) :
124
+ admin_url( $fs_debug_page_url );
125
+ ?>
126
+ <a href="<?php echo wp_nonce_url( $fs_debug_page_url, 'fs_allow_clone_resolution_notice' ) ?>" class="button button-primary">Resolve Clone(s)</a>
127
+ </td>
128
  </tr>
129
  </tbody>
130
  </table>
134
  var optionName = prompt('Please enter the option name:');
135
 
136
  if (optionName) {
137
+ $.post(<?php echo Freemius::ajax_url() ?>, {
138
  action : 'fs_get_db_option',
139
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
140
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_db_option' ) ); ?>,
155
  var optionValue = prompt('Please enter the option value:');
156
 
157
  if (optionValue) {
158
+ $.post(<?php echo Freemius::ajax_url() ?>, {
159
  action : 'fs_set_db_option',
160
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
161
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_set_db_option' ) ); ?>,
198
  'key' => 'WP_FS__DIR',
199
  'val' => WP_FS__DIR,
200
  ),
201
+ array(
202
+ 'key' => 'wp_using_ext_object_cache()',
203
+ 'val' => wp_using_ext_object_cache() ? 'true' : 'false',
204
+ ),
205
  )
206
  ?>
207
  <br>
256
  WP_FS__MODULE_TYPE_THEME
257
  );
258
  ?>
259
+ <?php $active_modules_by_id = array() ?>
260
  <?php foreach ( $module_types as $module_type ) : ?>
261
  <?php $modules = fs_get_entities( $fs_options->get_option( $module_type . 's' ), FS_Plugin::get_class_name() ) ?>
262
  <?php if ( is_array( $modules ) && count( $modules ) > 0 ) : ?>
272
  <th><?php fs_esc_html_echo_inline( 'Freemius State' ) ?></th>
273
  <th><?php fs_esc_html_echo_inline( 'Module Path' ) ?></th>
274
  <th><?php fs_esc_html_echo_inline( 'Public Key' ) ?></th>
275
+ <?php if ( $is_multisite ) : ?>
276
  <th><?php fs_esc_html_echo_inline( 'Network Blog' ) ?></th>
277
  <th><?php fs_esc_html_echo_inline( 'Network User' ) ?></th>
278
  <?php endif ?>
295
  }
296
  }
297
  ?>
298
+ <?php
299
+ $fs = null;
300
+ if ( $is_active ) {
301
+ $fs = freemius( $data->id );
302
+
303
+ $active_modules_by_id[ $data->id ] = true;
304
+ }
305
+ ?>
306
  <tr<?php if ( $is_active ) {
307
  if ( $fs->has_api_connectivity() && $fs->is_on() ) {
308
  echo ' style="background: #E6FFE6; font-weight: bold"';
332
  } ?></td>
333
  <td><?php echo $data->file ?></td>
334
  <td><?php echo $data->public_key ?></td>
335
+ <?php if ( $is_multisite ) : ?>
336
  <?php
337
  $network_blog_id = null;
338
  $network_user = null;
386
  */
387
  $sites_map = $VARS[ $module_type . '_sites' ];
388
 
389
+ $all_plans = false;
 
390
  ?>
391
  <?php if ( is_array( $sites_map ) && count( $sites_map ) > 0 ) : ?>
392
  <h2><?php echo esc_html( sprintf(
412
  </tr>
413
  </thead>
414
  <tbody>
415
+ <?php $site_url = null ?>
416
  <?php foreach ( $sites_map as $slug => $sites ) : ?>
 
 
 
417
  <?php foreach ( $sites as $site ) : ?>
418
+ <?php
419
+ $blog_id = $is_multisite ?
420
+ $site->blog_id :
421
+ null;
422
+
423
+ if ( is_null( $site_url ) || $is_multisite ) {
424
+ $site_url = Freemius::get_unfiltered_site_url(
425
+ $blog_id,
426
+ true,
427
+ true
428
+ );
429
+ }
430
+
431
+ $is_active_clone = ( $site->is_clone( $site_url ) && isset( $active_modules_by_id[ $site->plugin_id ] ) );
432
+
433
+ if ( $is_active_clone ) {
434
+ $has_any_active_clone = true;
435
+ }
436
+ ?>
437
  <tr>
438
+ <td>
439
+ <?php echo $site->id ?>
440
+ <?php if ( $is_active_clone ) : ?>
441
+ <label class="fs-tag fs-warn">Clone</label>
442
+ <?php endif ?>
443
+ </td>
444
  <?php if ( $is_multisite ) : ?>
445
+ <td><?php echo $blog_id ?></td>
446
  <td><?php echo fs_strip_url_protocol( $site->url ) ?></td>
447
  <?php endif ?>
448
  <td><?php echo $slug ?></td>
539
  * @var FS_User[] $users
540
  */
541
  $users = $VARS['users'];
542
+ $user_ids_map = array();
543
  $users_with_developer_license_by_id = array();
544
 
545
+ if ( is_array( $users ) && ! empty( $users ) ) {
546
+ foreach ( $users as $user ) {
547
+ $user_ids_map[ $user->id ] = true;
548
+ }
549
+ }
550
+
551
  foreach ( $module_types as $module_type ) {
552
  /**
553
  * @var FS_Plugin_License[] $licenses
640
  <td><?php echo $license->is_block_features ? 'Blocking' : 'Flexible' ?></td>
641
  <td><?php echo $license->is_whitelabeled ? 'Whitelabeled' : 'Normal' ?></td>
642
  <td><?php
643
+ echo ( $license->is_whitelabeled || ! isset( $user_ids_map[ $license->user_id ] ) ) ?
644
  $license->get_html_escaped_masked_secret_key() :
645
  esc_html( $license->secret_key );
646
  ?></td>
788
  offset = 0;
789
  }
790
 
791
+ $.post(<?php echo Freemius::ajax_url() ?>, {
792
  action : 'fs_get_debug_log',
793
  // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
794
  _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_debug_log' ) ); ?>,
freemius/templates/firewall-issues-js.php CHANGED
@@ -48,8 +48,7 @@
48
 
49
  $( this ).css({'cursor': 'wait'});
50
 
51
- // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
52
- $.post( ajaxurl, data, function( response ) {
53
  if ( 1 == response ) {
54
  // Refresh page on success.
55
  location.reload();
48
 
49
  $( this ).css({'cursor': 'wait'});
50
 
51
+ $.post( <?php echo Freemius::ajax_url() ?>, data, function( response ) {
 
52
  if ( 1 == response ) {
53
  // Refresh page on success.
54
  location.reload();
freemius/templates/forms/affiliation.php CHANGED
@@ -13,8 +13,10 @@
13
  /**
14
  * @var array $VARS
15
  * @var Freemius $fs
 
16
  */
17
- $fs = freemius( $VARS['id'] );
 
18
 
19
  $slug = $fs->get_slug();
20
 
@@ -22,7 +24,6 @@
22
  $affiliate = $fs->get_affiliate();
23
  $affiliate_terms = $fs->get_affiliate_terms();
24
 
25
- $plugin_title = $fs->get_plugin_title();
26
  $module_type = $fs->is_plugin() ?
27
  WP_FS__MODULE_TYPE_PLUGIN :
28
  WP_FS__MODULE_TYPE_THEME;
@@ -45,7 +46,7 @@
45
  $promotion_method_mobile_apps = false;
46
  $statistics_information = false;
47
  $promotion_method_description = false;
48
- $members_dashboard_login_url = 'https://members.freemius.com/login/';
49
 
50
  $affiliate_application_data = $fs->get_affiliate_application_data();
51
 
@@ -71,7 +72,7 @@
71
  $current_user = Freemius::_get_current_wp_user();
72
  $full_name = trim( $current_user->user_firstname . ' ' . $current_user->user_lastname );
73
  $email_address = $current_user->user_email;
74
- $domain = fs_strip_url_protocol( get_site_url() );
75
  }
76
 
77
  $affiliate_tracking = 30;
@@ -365,7 +366,7 @@
365
  }
366
 
367
  $.ajax({
368
- url : ajaxurl,
369
  method : 'POST',
370
  data : {
371
  action : '<?php echo $fs->get_ajax_action( 'submit_affiliate_application' ) ?>',
@@ -506,4 +507,3 @@
506
  'module_version' => $fs->get_plugin_version(),
507
  );
508
  fs_require_template( 'powered-by.php', $params );
509
- ?>
13
  /**
14
  * @var array $VARS
15
  * @var Freemius $fs
16
+ * @var string $plugin_title
17
  */
18
+ $fs = freemius( $VARS['id'] );
19
+ $plugin_title = $VARS['plugin_title'];
20
 
21
  $slug = $fs->get_slug();
22
 
24
  $affiliate = $fs->get_affiliate();
25
  $affiliate_terms = $fs->get_affiliate_terms();
26
 
 
27
  $module_type = $fs->is_plugin() ?
28
  WP_FS__MODULE_TYPE_PLUGIN :
29
  WP_FS__MODULE_TYPE_THEME;
46
  $promotion_method_mobile_apps = false;
47
  $statistics_information = false;
48
  $promotion_method_description = false;
49
+ $members_dashboard_login_url = 'https://users.freemius.com/login';
50
 
51
  $affiliate_application_data = $fs->get_affiliate_application_data();
52
 
72
  $current_user = Freemius::_get_current_wp_user();
73
  $full_name = trim( $current_user->user_firstname . ' ' . $current_user->user_lastname );
74
  $email_address = $current_user->user_email;
75
+ $domain = Freemius::get_unfiltered_site_url( null, true );
76
  }
77
 
78
  $affiliate_tracking = 30;
366
  }
367
 
368
  $.ajax({
369
+ url : <?php echo Freemius::ajax_url() ?>,
370
  method : 'POST',
371
  data : {
372
  action : '<?php echo $fs->get_ajax_action( 'submit_affiliate_application' ) ?>',
507
  'module_version' => $fs->get_plugin_version(),
508
  );
509
  fs_require_template( 'powered-by.php', $params );
 
freemius/templates/forms/data-debug-mode.php CHANGED
@@ -140,7 +140,7 @@ HTML;
140
  };
141
 
142
  $.ajax( {
143
- url : ajaxurl,
144
  method : 'POST',
145
  data : data,
146
  beforeSend: function () {
140
  };
141
 
142
  $.ajax( {
143
+ url : <?php echo Freemius::ajax_url() ?>,
144
  method : 'POST',
145
  data : data,
146
  beforeSend: function () {
freemius/templates/forms/deactivation/form.php CHANGED
@@ -24,6 +24,7 @@
24
  $anonymous_feedback_checkbox_html = '';
25
 
26
  $reasons_list_items_html = '';
 
27
 
28
  if ( $show_deactivation_feedback_form ) {
29
  $reasons = $VARS['reasons'];
@@ -64,6 +65,41 @@ HTML;
64
  fs_esc_html_inline( 'Anonymous feedback', 'anonymous-feedback', $slug )
65
  );
66
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
68
 
69
  // Aliases.
@@ -71,6 +107,13 @@ HTML;
71
  $theme_text = fs_text_inline( 'Theme', 'theme', $slug );
72
  $activate_x_text = fs_text_inline( 'Activate %s', 'activate-x', $slug );
73
 
 
 
 
 
 
 
 
74
  fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
75
 
76
  if ( ! empty( $subscription_cancellation_dialog_box_template_params ) ) {
@@ -92,6 +135,7 @@ HTML;
92
  + ' </div>'
93
  + ' <div class="fs-modal-footer">'
94
  + ' <?php echo $anonymous_feedback_checkbox_html ?>'
 
95
  + ' <a href="#" class="button button-secondary button-deactivate"></a>'
96
  + ' <a href="#" class="button button-secondary button-close"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></a>'
97
  + ' </div>'
@@ -101,6 +145,7 @@ HTML;
101
  selectedReasonID = false,
102
  redirectLink = '',
103
  $anonymousFeedback = $modal.find( '.anonymous-feedback-label' ),
 
104
  isAnonymous = <?php echo ( $is_anonymous ? 'true' : 'false' ); ?>,
105
  otherReasonID = <?php echo Freemius::REASON_OTHER; ?>,
106
  dontShareDataReasonID = <?php echo Freemius::REASON_DONT_LIKE_TO_SHARE_MY_INFORMATION; ?>,
@@ -135,7 +180,7 @@ HTML;
135
  ?>
136
 
137
  $.ajax({
138
- url : ajaxurl,
139
  method : 'POST',
140
  data : {
141
  action : '<?php echo $fs->get_ajax_action( 'cancel_subscription_or_trial' ) ?>',
@@ -230,10 +275,6 @@ HTML;
230
  } ?>
231
 
232
  $modal.on('input propertychange', '.reason-input input', function () {
233
- if (!isOtherReasonSelected()) {
234
- return;
235
- }
236
-
237
  var reason = $(this).val().trim();
238
 
239
  /**
@@ -241,9 +282,12 @@ HTML;
241
  * to change the message color back to default.
242
  */
243
  if (reason.length > 0) {
244
- $('.message').removeClass('error-message');
245
- enableDeactivateButton();
246
- }
 
 
 
247
  });
248
 
249
  $modal.on('blur', '.reason-input input', function () {
@@ -260,8 +304,8 @@ HTML;
260
  */
261
  if (0 === $userReason.val().trim().length) {
262
  $('.message').addClass('error-message');
263
- disableDeactivateButton();
264
- }
265
  }, 150);
266
  });
267
 
@@ -276,15 +320,34 @@ HTML;
276
  var _this = $(this);
277
 
278
  if (_this.hasClass('allow-deactivate')) {
279
- var $radio = $modal.find('input[type="radio"]:checked');
 
 
 
 
 
 
 
280
 
281
- if (0 === $radio.length) {
 
 
 
 
 
 
 
 
 
 
 
 
282
  if ( ! deleteThemeUpdateData ) {
283
  // If no selected reason, just deactivate the plugin.
284
  window.location.href = redirectLink;
285
  } else {
286
  $.ajax({
287
- url : ajaxurl,
288
  method : 'POST',
289
  data : {
290
  action : '<?php echo $fs->get_ajax_action( 'delete_theme_update_data' ) ?>',
@@ -292,8 +355,7 @@ HTML;
292
  module_id: '<?php echo $fs->get_id() ?>'
293
  },
294
  beforeSend: function() {
295
- _parent.find( '.fs-modal-footer .button' ).addClass( 'disabled' );
296
- _parent.find( '.fs-modal-footer .button-secondary' ).text( 'Processing...' );
297
  },
298
  complete : function() {
299
  window.location.href = redirectLink;
@@ -304,28 +366,27 @@ HTML;
304
  return;
305
  }
306
 
307
- var $selected_reason = $radio.parents('li:first'),
308
- $input = $selected_reason.find('textarea, input[type="text"]'),
309
- userReason = ( 0 !== $input.length ) ? $input.val().trim() : '';
310
 
311
- if (isOtherReasonSelected() && ( '' === userReason )) {
312
- return;
313
- }
314
 
315
  $.ajax({
316
- url : ajaxurl,
317
  method : 'POST',
318
  data : {
319
- action : '<?php echo $fs->get_ajax_action( 'submit_uninstall_reason' ) ?>',
320
- security : '<?php echo $fs->get_ajax_security( 'submit_uninstall_reason' ) ?>',
321
- module_id : '<?php echo $fs->get_id() ?>',
322
- reason_id : $radio.val(),
323
- reason_info : userReason,
324
- is_anonymous: isAnonymousFeedback()
 
325
  },
326
  beforeSend: function () {
327
- _parent.find('.fs-modal-footer .button').addClass('disabled');
328
- _parent.find('.fs-modal-footer .button-secondary').text('Processing...');
329
  },
330
  complete : function () {
331
  // Do not show the dialog box, deactivate the plugin.
@@ -365,20 +426,17 @@ HTML;
365
 
366
  $modal.find('.reason-input').remove();
367
  $modal.find( '.internal-message' ).hide();
368
- $modal.find('.button-deactivate').html('<?php echo esc_js( sprintf(
369
- fs_text_inline( 'Submit & %s', 'deactivation-modal-button-submit' , $slug ),
370
- $fs->is_plugin() ?
371
- $deactivate_text :
372
- sprintf( $activate_x_text, $theme_text )
373
- ) ) ?>');
374
-
375
- enableDeactivateButton();
376
 
377
  if ( _parent.hasClass( 'has-internal-message' ) ) {
378
  _parent.find( '.internal-message' ).show();
379
  }
380
 
381
- if (_parent.hasClass('has-input')) {
 
 
 
 
382
  var inputType = _parent.data('input-type'),
383
  inputPlaceholder = _parent.data('input-placeholder'),
384
  reasonInputHtml = '<div class="reason-input"><span class="message"></span>' + ( ( 'textfield' === inputType ) ? '<input type="text" maxlength="128" />' : '<textarea rows="5" maxlength="128"></textarea>' ) + '</div>';
@@ -388,11 +446,60 @@ HTML;
388
 
389
  if (isOtherReasonSelected()) {
390
  showMessage('<?php echo esc_js( fs_text_inline( 'Kindly tell us the reason so we can improve.', 'ask-for-reason-message' , $slug ) ); ?>');
391
- disableDeactivateButton();
392
- }
393
  }
 
 
 
 
 
 
 
394
  });
395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  // If the user has clicked outside the window, cancel it.
397
  $modal.on('click', function (evt) {
398
  var $target = $(evt.target);
@@ -453,8 +560,6 @@ HTML;
453
  function resetModal() {
454
  selectedReasonID = false;
455
 
456
- enableDeactivateButton();
457
-
458
  // Uncheck all radio buttons.
459
  $modal.find('input[type="radio"]').prop('checked', false);
460
 
@@ -463,8 +568,8 @@ HTML;
463
 
464
  $modal.find('.message').hide();
465
 
466
- if ( isAnonymous ) {
467
- $anonymousFeedback.find( 'input' ).prop( 'checked', false );
468
 
469
  // Hide, since by default there is no selected reason.
470
  $anonymousFeedback.hide();
@@ -491,13 +596,31 @@ HTML;
491
  $modal.find('.message').text(message).show();
492
  }
493
 
494
- function enableDeactivateButton() {
495
- $modal.find('.button-deactivate').removeClass('disabled');
496
- }
 
 
 
 
 
 
497
 
498
- function disableDeactivateButton() {
499
- $modal.find('.button-deactivate').addClass('disabled');
500
- }
 
 
 
 
 
 
 
 
 
 
 
 
501
 
502
  function showPanel(panelType) {
503
  $modal.find( '.fs-modal-panel' ).removeClass( 'active' );
24
  $anonymous_feedback_checkbox_html = '';
25
 
26
  $reasons_list_items_html = '';
27
+ $snooze_select_html = '';
28
 
29
  if ( $show_deactivation_feedback_form ) {
30
  $reasons = $VARS['reasons'];
65
  fs_esc_html_inline( 'Anonymous feedback', 'anonymous-feedback', $slug )
66
  );
67
  }
68
+
69
+ $snooze_periods = array(
70
+ array(
71
+ 'increment' => fs_text_inline( 'hour', $slug ),
72
+ 'quantity' => number_format_i18n(1),
73
+ 'value' => 6 * WP_FS__TIME_10_MIN_IN_SEC,
74
+ ),
75
+ array(
76
+ 'increment' => fs_text_inline( 'hours', $slug ),
77
+ 'quantity' => number_format_i18n(24),
78
+ 'value' => WP_FS__TIME_24_HOURS_IN_SEC,
79
+ ),
80
+ array(
81
+ 'increment' => fs_text_inline( 'days', $slug ),
82
+ 'quantity' => number_format_i18n(7),
83
+ 'value' => WP_FS__TIME_WEEK_IN_SEC,
84
+ ),
85
+ array(
86
+ 'increment' => fs_text_inline( 'days', $slug ),
87
+ 'quantity' => number_format_i18n(30),
88
+ 'value' => 30 * WP_FS__TIME_24_HOURS_IN_SEC,
89
+ ),
90
+ );
91
+
92
+ $snooze_select_html = '<select>';
93
+ foreach ($snooze_periods as $period) {
94
+ $snooze_select_html .= sprintf(
95
+ '<option value="%s">%s %s</option>',
96
+ $period['value'],
97
+ $period['quantity'],
98
+ $period['increment']
99
+ );
100
+ }
101
+
102
+ $snooze_select_html .= '</select>';
103
  }
104
 
105
  // Aliases.
107
  $theme_text = fs_text_inline( 'Theme', 'theme', $slug );
108
  $activate_x_text = fs_text_inline( 'Activate %s', 'activate-x', $slug );
109
 
110
+ $submit_deactivate_text = sprintf(
111
+ fs_text_inline( 'Submit & %s', 'deactivation-modal-button-submit', $slug ),
112
+ $fs->is_plugin() ?
113
+ $deactivate_text :
114
+ sprintf( $activate_x_text, $theme_text )
115
+ );
116
+
117
  fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
118
 
119
  if ( ! empty( $subscription_cancellation_dialog_box_template_params ) ) {
135
  + ' </div>'
136
  + ' <div class="fs-modal-footer">'
137
  + ' <?php echo $anonymous_feedback_checkbox_html ?>'
138
+ + ' <label style="display: none" class="feedback-from-snooze-label"><input type="checkbox" class="feedback-from-snooze-checkbox"> <span><?php fs_esc_js_echo_inline( 'Snooze this panel during troubleshooting', 'snooze-panel-during-troubleshooting', $slug ) ?></span><span style="display: none"><?php fs_esc_js_echo_inline( 'Snooze this panel for', 'snooze-panel-for', $slug ) ?> <?php echo $snooze_select_html ?></span></label>'
139
  + ' <a href="#" class="button button-secondary button-deactivate"></a>'
140
  + ' <a href="#" class="button button-secondary button-close"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></a>'
141
  + ' </div>'
145
  selectedReasonID = false,
146
  redirectLink = '',
147
  $anonymousFeedback = $modal.find( '.anonymous-feedback-label' ),
148
+ $feedbackSnooze = $modal.find( '.feedback-from-snooze-label' ),
149
  isAnonymous = <?php echo ( $is_anonymous ? 'true' : 'false' ); ?>,
150
  otherReasonID = <?php echo Freemius::REASON_OTHER; ?>,
151
  dontShareDataReasonID = <?php echo Freemius::REASON_DONT_LIKE_TO_SHARE_MY_INFORMATION; ?>,
180
  ?>
181
 
182
  $.ajax({
183
+ url : <?php echo Freemius::ajax_url() ?>,
184
  method : 'POST',
185
  data : {
186
  action : '<?php echo $fs->get_ajax_action( 'cancel_subscription_or_trial' ) ?>',
275
  } ?>
276
 
277
  $modal.on('input propertychange', '.reason-input input', function () {
 
 
 
 
278
  var reason = $(this).val().trim();
279
 
280
  /**
282
  * to change the message color back to default.
283
  */
284
  if (reason.length > 0) {
285
+ $('.message').removeClass('error-message');
286
+ }
287
+
288
+ toggleDeactivationButtonPrimary( reason.length > 0 );
289
+
290
+ changeDeactivateButtonText();
291
  });
292
 
293
  $modal.on('blur', '.reason-input input', function () {
304
  */
305
  if (0 === $userReason.val().trim().length) {
306
  $('.message').addClass('error-message');
307
+ changeDeactivateButtonText();
308
+ }
309
  }, 150);
310
  });
311
 
320
  var _this = $(this);
321
 
322
  if (_this.hasClass('allow-deactivate')) {
323
+ var
324
+ $radio = $modal.find('input[type="radio"]:checked'),
325
+ isReasonSelected = (0 < $radio.length),
326
+ userReason = '';
327
+
328
+ if ( isReasonSelected ) {
329
+ var $selectedReason = $radio.parents('li:first'),
330
+ $reasonInput = $selectedReason.find('textarea, input[type="text"]');
331
 
332
+ if ( 0 < $reasonInput.length ) {
333
+ userReason = $reasonInput.val().trim();
334
+ }
335
+ }
336
+
337
+ if ( otherReasonID == selectedReasonID && '' === userReason ) {
338
+ // If the 'Other' is selected and a reason is not provided (aka it's empty), treat it as if a reason wasn't selected at all.
339
+ isReasonSelected = false;
340
+ }
341
+
342
+ _parent.find( '.fs-modal-footer .button' ).addClass( 'disabled' );
343
+
344
+ if ( ! isReasonSelected ) {
345
  if ( ! deleteThemeUpdateData ) {
346
  // If no selected reason, just deactivate the plugin.
347
  window.location.href = redirectLink;
348
  } else {
349
  $.ajax({
350
+ url : <?php echo Freemius::ajax_url() ?>,
351
  method : 'POST',
352
  data : {
353
  action : '<?php echo $fs->get_ajax_action( 'delete_theme_update_data' ) ?>',
355
  module_id: '<?php echo $fs->get_id() ?>'
356
  },
357
  beforeSend: function() {
358
+ _parent.find( '.fs-modal-footer .button-deactivate' ).text( '<?php echo esc_js( fs_text_inline( 'Processing', 'processing', $slug ) ) ?>...' );
 
359
  },
360
  complete : function() {
361
  window.location.href = redirectLink;
366
  return;
367
  }
368
 
369
+ var snoozePeriod = 0,
370
+ shouldSnooze = $feedbackSnooze.find( '.feedback-from-snooze-checkbox' ).is( ':checked' );
 
371
 
372
+ if ( shouldSnooze && <?php echo Freemius::REASON_TEMPORARY_DEACTIVATION ?> == selectedReasonID ) {
373
+ snoozePeriod = parseInt($feedbackSnooze.find('select').val(), 10);
374
+ }
375
 
376
  $.ajax({
377
+ url : <?php echo Freemius::ajax_url() ?>,
378
  method : 'POST',
379
  data : {
380
+ action : '<?php echo $fs->get_ajax_action( 'submit_uninstall_reason' ) ?>',
381
+ security : '<?php echo $fs->get_ajax_security( 'submit_uninstall_reason' ) ?>',
382
+ module_id : '<?php echo $fs->get_id() ?>',
383
+ reason_id : $radio.val(),
384
+ reason_info : userReason,
385
+ is_anonymous : isAnonymousFeedback(),
386
+ snooze_period: snoozePeriod
387
  },
388
  beforeSend: function () {
389
+ _parent.find('.fs-modal-footer .button-deactivate').text('<?php echo esc_js( fs_text_inline( 'Processing', 'processing', $slug ) ) ?>...');
 
390
  },
391
  complete : function () {
392
  // Do not show the dialog box, deactivate the plugin.
426
 
427
  $modal.find('.reason-input').remove();
428
  $modal.find( '.internal-message' ).hide();
429
+ $modal.find('.button-deactivate').html('<?php echo esc_js( $submit_deactivate_text ) ?>');
 
 
 
 
 
 
 
430
 
431
  if ( _parent.hasClass( 'has-internal-message' ) ) {
432
  _parent.find( '.internal-message' ).show();
433
  }
434
 
435
+ if ( ! _parent.hasClass('has-input') ) {
436
+ toggleDeactivationButtonPrimary( true );
437
+ } else {
438
+ toggleDeactivationButtonPrimary( false );
439
+
440
  var inputType = _parent.data('input-type'),
441
  inputPlaceholder = _parent.data('input-placeholder'),
442
  reasonInputHtml = '<div class="reason-input"><span class="message"></span>' + ( ( 'textfield' === inputType ) ? '<input type="text" maxlength="128" />' : '<textarea rows="5" maxlength="128"></textarea>' ) + '</div>';
446
 
447
  if (isOtherReasonSelected()) {
448
  showMessage('<?php echo esc_js( fs_text_inline( 'Kindly tell us the reason so we can improve.', 'ask-for-reason-message' , $slug ) ); ?>');
449
+ changeDeactivateButtonText();
450
+ }
451
  }
452
+
453
+ $anonymousFeedback.toggle( <?php echo Freemius::REASON_TEMPORARY_DEACTIVATION ?> != selectedReasonID );
454
+ $feedbackSnooze.toggle( <?php echo Freemius::REASON_TEMPORARY_DEACTIVATION ?> == selectedReasonID );
455
+
456
+ if ( <?php echo Freemius::REASON_TEMPORARY_DEACTIVATION ?> == selectedReasonID ) {
457
+ updateDeactivationButtonOnTrouble();
458
+ }
459
  });
460
 
461
+ var toggleDeactivationButtonPrimary = function ( isPrimary ) {
462
+ if ( isPrimary ) {
463
+ $modal.find('.button-deactivate')
464
+ .removeClass( 'button-secondary' )
465
+ .addClass( 'button-primary' );
466
+ } else {
467
+ $modal.find('.button-deactivate')
468
+ .addClass( 'button-secondary' )
469
+ .removeClass( 'button-primary' );
470
+ }
471
+ };
472
+
473
+ var snooze = false;
474
+
475
+ var updateDeactivationButtonOnTrouble = function () {
476
+ if ( snooze ) {
477
+ $modal.find('.button-deactivate').html('<?php echo esc_js( sprintf(
478
+ fs_text_inline( 'Snooze & %s', 'snooze-modal-button-submit' , $slug ),
479
+ $fs->is_plugin() ?
480
+ $deactivate_text :
481
+ sprintf( $activate_x_text, $theme_text )
482
+ ) ) ?>');
483
+ } else {
484
+ $modal.find('.button-deactivate').html('<?php echo esc_js(
485
+ $fs->is_plugin() ?
486
+ $deactivate_text :
487
+ sprintf( $activate_x_text, $theme_text )
488
+ ) ?>');
489
+ }
490
+ };
491
+
492
+ $feedbackSnooze.on( 'click', 'input', function () {
493
+ var $spans = $feedbackSnooze.find( 'span' );
494
+
495
+ snooze = ( ! snooze );
496
+
497
+ $( $spans[0] ).toggle();
498
+ $( $spans[1] ).toggle();
499
+
500
+ updateDeactivationButtonOnTrouble();
501
+ });
502
+
503
  // If the user has clicked outside the window, cancel it.
504
  $modal.on('click', function (evt) {
505
  var $target = $(evt.target);
560
  function resetModal() {
561
  selectedReasonID = false;
562
 
 
 
563
  // Uncheck all radio buttons.
564
  $modal.find('input[type="radio"]').prop('checked', false);
565
 
568
 
569
  $modal.find('.message').hide();
570
 
571
+ if ( isAnonymous ) {
572
+ $anonymousFeedback.find( 'input' ).prop( 'checked', <?php echo $fs->apply_filters( 'default_to_anonymous_feedback', false ) ? 'true' : 'false' ?> );
573
 
574
  // Hide, since by default there is no selected reason.
575
  $anonymousFeedback.hide();
596
  $modal.find('.message').text(message).show();
597
  }
598
 
599
+ /**
600
+ * @author Xiaheng Chen (@xhchen)
601
+ *
602
+ * @since 2.4.2
603
+ */
604
+ function changeDeactivateButtonText() {
605
+ if ( ! isOtherReasonSelected()) {
606
+ return;
607
+ }
608
 
609
+ var
610
+ $userReason = $modal.find('.reason-input input'),
611
+ $deactivateButton = $modal.find('.button-deactivate');
612
+
613
+ if (0 === $userReason.val().trim().length) {
614
+ // If the reason is empty, just change the text to 'Deactivate' (plugin) or 'Activate themeX' (theme).
615
+ $deactivateButton.html('<?php echo
616
+ $fs->is_plugin() ?
617
+ $deactivate_text :
618
+ sprintf( $activate_x_text, $theme_text )
619
+ ?>');
620
+ } else {
621
+ $deactivateButton.html('<?php echo esc_js( $submit_deactivate_text ) ?>');
622
+ }
623
+ }
624
 
625
  function showPanel(panelType) {
626
  $modal.find( '.fs-modal-panel' ).removeClass( 'active' );
freemius/templates/forms/email-address-update.php ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ *
7
+ * @author Leo Fajardo (@leorw)
8
+ * @since 2.5.0
9
+ */
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * @var array $VARS
17
+ */
18
+ $fs = freemius( $VARS['id'] );
19
+ $slug = $fs->get_slug();
20
+
21
+ $user = $fs->get_user();
22
+ $current_email_address = $user->email;
23
+
24
+ fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
25
+ ?>
26
+ <script type="text/javascript">
27
+ ( function ( $ ) {
28
+ var modalHtml =
29
+ '<div class="fs-modal fs-modal-email-address-update">'
30
+ + ' <div class="fs-modal-dialog">'
31
+ + ' <div class="fs-modal-header">'
32
+ + ' <h4><?php fs_esc_attr_echo_inline( 'Email address update', 'email-address-update', $slug ) ?></h4>'
33
+ + ' <a href="!#" class="fs-close"><i class="dashicons dashicons-no" title="<?php echo esc_js( fs_text_x_inline( 'Dismiss', 'close window', 'dismiss', $slug ) ) ?>"></i></a>'
34
+ + ' </div>'
35
+ + ' <div class="fs-modal-body">'
36
+ + ' <div class="fs-modal-panel active">'
37
+ + ' <div class="notice notice-error inline fs-update-error-message"><p></p></div>'
38
+ + ' <p><?php fs_echo_inline( 'Enter the new email address', 'enter-new-email-address' , $slug ) ?>:</p>'
39
+ + ' <input type="text" class="fs-new-email-address-input" />'
40
+ + ' <div class="fs-email-addresses-ownership-options" style="display: none">'
41
+ + ' <p><?php echo sprintf(
42
+ fs_esc_html_inline( 'Are both %s and %s your email addresses?', 'email-addresses-ownership-confirmation' , $slug ),
43
+ sprintf( '<strong>%s</strong>', $current_email_address ),
44
+ '<strong class="fs-new-email-address"></strong>'
45
+ ) ?></p>'
46
+ + ' <ul>'
47
+ + ' <li>'
48
+ + ' <label>'
49
+ + ' <span><input type="radio" name="email-addresses-ownership" value="both"/></span>'
50
+ + ' <span><?php fs_echo_inline( 'Yes - both addresses are mine', 'both-addresses-are-mine', $slug ); ?></span>'
51
+ + ' </label>'
52
+ + ' </li>'
53
+ + ' <li>'
54
+ + ' <label>'
55
+ + ' <span><input type="radio" name="email-addresses-ownership" value="current"/></span>'
56
+ + ' <div><?php echo sprintf(
57
+ fs_esc_html_inline( "%s is my client's email address", 'client-email-address-confirmation', $slug ),
58
+ '<strong class="fs-new-email-address"></strong>'
59
+ ) ?></span>'
60
+ + ' </label>'
61
+ + ' </li>'
62
+ + ' <li>'
63
+ + ' <label>'
64
+ + ' <span><input type="radio" name="email-addresses-ownership" value="new"/></span>'
65
+ + ' <div><?php echo sprintf(
66
+ fs_esc_html_inline( "%s is my email address", 'email-address-ownership-confirmation', $slug ),
67
+ '<strong class="fs-new-email-address"></strong>'
68
+ ) ?></span>'
69
+ + ' </label>'
70
+ + ' </li>'
71
+ + ' </ul>'
72
+ + ' </div>'
73
+ + ' <div class="fs-assets-transfership-options" style="display: none">'
74
+ + ' <p><?php echo sprintf(
75
+ fs_esc_html_inline( 'Would you like to merge %s into %s?', 'accounts-merge-confirmation' , $slug ),
76
+ sprintf( '<strong>%s</strong>', $current_email_address ),
77
+ '<strong class="fs-new-email-address"></strong>'
78
+ ) ?></p>'
79
+ + ' <ul>'
80
+ + ' <li>'
81
+ + ' <label>'
82
+ + ' <span><input type="radio" name="assets-transfer-type" value="all" /></span>'
83
+ + ' <span><?php echo sprintf(
84
+ fs_esc_html_inline( 'Yes - move all my data and assets from %s to %s', 'move-all-data-and-assets-into-new-account', $slug ),
85
+ sprintf( '<strong>%s</strong>', $current_email_address ),
86
+ '<strong class="fs-new-email-address"></strong>'
87
+ ) ?></span>'
88
+ + ' </label>'
89
+ + ' </li>'
90
+ + ' <li>'
91
+ + ' <label>'
92
+ + ' <span><input type="radio" name="assets-transfer-type" value="plugin" /></span>'
93
+ + ' <span><?php echo sprintf(
94
+ fs_esc_html_inline( "No - only move this site's data to %s", 'move-only-plugin-data-into-new-account', $slug ),
95
+ '<strong class="fs-new-email-address"></strong>'
96
+ ) ?></span>'
97
+ + ' </label>'
98
+ + ' </li>'
99
+ + ' </ul>'
100
+ + ' </div>'
101
+ + ' </div>'
102
+ + ' </div>'
103
+ + ' <div class="fs-modal-footer">'
104
+ + ' <button class="button button-primary button-update" disabled><?php fs_esc_js_echo_inline( 'Update', 'update-email-address', $slug ) ?></button>'
105
+ + ' <button class="button button-secondary button-close"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></button>'
106
+ + ' </div>'
107
+ + ' </div>'
108
+ + '</div>',
109
+ $modal = $( modalHtml ),
110
+ $updateButton = $modal.find( '.button-update' ),
111
+ $updateResultMessage = $modal.find( '.fs-update-error-message' ),
112
+ selectedEmailAddressesOwnershipOption = null,
113
+ selectedAssetsTransfershipOption = null,
114
+ previousEmailAddress = '',
115
+ $body = $( 'body' );
116
+
117
+ $modal.appendTo( $body );
118
+
119
+ registerEventHandlers();
120
+
121
+ function registerEventHandlers() {
122
+ $body.on( 'click', '#fs_account_details .button-edit-email-address', function ( evt ) {
123
+ evt.preventDefault();
124
+
125
+ showModal( evt );
126
+ } );
127
+
128
+ $modal.on( 'input propertychange keyup paste delete cut', '.fs-new-email-address-input', function () {
129
+ var emailAddress = $( this ).val().trim();
130
+
131
+ if ( emailAddress === previousEmailAddress ) {
132
+ return;
133
+ }
134
+
135
+ var isValidEmailAddressInput = isValidEmailAddress( emailAddress );
136
+
137
+ toggleOptions( isValidEmailAddressInput );
138
+
139
+ if ( ! isValidEmailAddressInput ) {
140
+ disableUpdateButton();
141
+ } else {
142
+ $modal.find( '.fs-new-email-address').text( emailAddress );
143
+
144
+ maybeEnableUpdateButton();
145
+ }
146
+
147
+ previousEmailAddress = emailAddress;
148
+ } );
149
+
150
+ $modal.on( 'blur', '.fs-new-email-address-input', function() {
151
+ var emailAddress = $( this ).val().trim(),
152
+ isValidEmailAddressInput = isValidEmailAddress( emailAddress );
153
+
154
+ toggleOptions( isValidEmailAddressInput );
155
+
156
+ if ( ! isValidEmailAddressInput ) {
157
+ disableUpdateButton();
158
+ }
159
+ } );
160
+
161
+ $modal.on( 'click', '.fs-close, .button-secondary', function () {
162
+ closeModal();
163
+ return false;
164
+ } );
165
+
166
+ $modal.on( 'click', '.fs-modal-footer .button-update', function ( evt ) {
167
+ if ( ! isValidEmailAddress( previousEmailAddress ) ) {
168
+ return;
169
+ }
170
+
171
+ if ( previousEmailAddress === '<?php echo $current_email_address ?>' ) {
172
+ closeModal();
173
+ return;
174
+ }
175
+
176
+ var transferType = 'transfer';
177
+
178
+ if ( 'current' === selectedEmailAddressesOwnershipOption ) {
179
+ transferType = 'transfer_to_client';
180
+ } else if (
181
+ 'both' === selectedEmailAddressesOwnershipOption &&
182
+ 'all' === selectedAssetsTransfershipOption
183
+ ) {
184
+ transferType = 'merge';
185
+ }
186
+
187
+ $.ajax( {
188
+ url : <?php echo Freemius::ajax_url() ?>,
189
+ method : 'POST',
190
+ data : {
191
+ action : '<?php echo $fs->get_ajax_action( 'update_email_address' ) ?>',
192
+ security : '<?php echo $fs->get_ajax_security( 'update_email_address' ) ?>',
193
+ module_id : '<?php echo $fs->get_id() ?>',
194
+ transfer_type: transferType,
195
+ email_address: previousEmailAddress
196
+ },
197
+ beforeSend: function () {
198
+ disableUpdateButton();
199
+
200
+ $updateButton.find( '.fs-modal-footer .button' ).prop( 'disabled', true );
201
+ $updateButton.text( 'Processing...' );
202
+ },
203
+ success : function( result ) {
204
+ if ( result.success ) {
205
+ // Redirect to the "Account" page.
206
+ window.location.reload();
207
+ } else {
208
+ if ('change_ownership' === result.error.code) {
209
+ window.location = result.error.url;
210
+ } else {
211
+ showError(result.error.message ? result.error.message : result.error);
212
+ resetUpdateButton();
213
+ }
214
+ }
215
+ },
216
+ error : function () {
217
+ showError( '<?php fs_esc_js_echo_inline( 'Unexpected error, try again in 5 minutes. If the error persists, please contact support.', 'unexpected-error', $slug ) ?>' );
218
+
219
+ resetUpdateButton();
220
+ }
221
+ } );
222
+ } );
223
+
224
+ $modal.on( 'click', 'input[type="radio"]', function () {
225
+ var $selectedOption = $( this ),
226
+ selectedOptionValue = $selectedOption.val();
227
+
228
+ // If the selection has not changed, do not proceed.
229
+ if (
230
+ selectedEmailAddressesOwnershipOption === selectedOptionValue ||
231
+ selectedAssetsTransfershipOption === selectedOptionValue
232
+ ) {
233
+ return;
234
+ }
235
+
236
+ if ( 'assets-transfer-type' === $selectedOption.attr( 'name' ) ) {
237
+ selectedAssetsTransfershipOption = selectedOptionValue;
238
+ } else {
239
+ selectedEmailAddressesOwnershipOption = selectedOptionValue;
240
+
241
+ if ( 'both' !== selectedEmailAddressesOwnershipOption ) {
242
+ $modal.find( '.fs-assets-transfership-options' ).hide();
243
+ } else {
244
+ $modal.find( '.fs-assets-transfership-options' ).show();
245
+ $modal.find( '.fs-assets-transfership-options input[type="radio"]' ).prop('checked', false);
246
+
247
+ selectedAssetsTransfershipOption = null;
248
+
249
+ disableUpdateButton();
250
+ }
251
+ }
252
+
253
+ if ( isValidEmailAddress( $( '.fs-new-email-address-input' ).val().trim() ) ) {
254
+ maybeEnableUpdateButton();
255
+ }
256
+ });
257
+ }
258
+
259
+ function showModal() {
260
+ resetModal();
261
+
262
+ // Display the dialog box.
263
+ $modal.addClass( 'active' );
264
+ $modal.find( '.fs-new-email-address-input' ).focus();
265
+
266
+ $( 'body' ).addClass( 'has-fs-modal' );
267
+ }
268
+
269
+ function closeModal() {
270
+ selectedEmailAddressesOwnershipOption = null;
271
+
272
+ disableUpdateButton();
273
+
274
+ $modal.removeClass( 'active' );
275
+
276
+ $( 'body' ).removeClass( 'has-fs-modal' );
277
+ }
278
+
279
+ function resetModal() {
280
+ hideError();
281
+
282
+ // Deselect all radio buttons.
283
+ $modal.find( 'input[type="radio"]' ).prop( 'checked', false );
284
+
285
+ // Clear the value of the email address text field.
286
+ $modal.find( 'input[type="text"]' ).val( '' );
287
+
288
+ toggleOptions( false );
289
+
290
+ disableUpdateButton();
291
+
292
+ $updateButton.text( <?php echo json_encode( fs_text_inline( 'Update', 'update-email-address', $slug ) ) ?> );
293
+ }
294
+
295
+ function resetUpdateButton() {
296
+ maybeEnableUpdateButton();
297
+
298
+ $updateButton.text( <?php echo json_encode( fs_text_inline( 'Update', 'update-email-address', $slug ) ) ?> );
299
+ }
300
+
301
+ function maybeEnableUpdateButton() {
302
+ if ( null === selectedEmailAddressesOwnershipOption ) {
303
+ return;
304
+ }
305
+
306
+ if (
307
+ 'both' === selectedEmailAddressesOwnershipOption &&
308
+ null === selectedAssetsTransfershipOption
309
+ ) {
310
+ return;
311
+ }
312
+
313
+ $updateButton.prop( 'disabled', false );
314
+ }
315
+
316
+ function disableUpdateButton() {
317
+ $updateButton.prop( 'disabled', true );
318
+ }
319
+
320
+ function hideError() {
321
+ $updateResultMessage.hide();
322
+ }
323
+
324
+ function showError( msg ) {
325
+ $updateResultMessage.find( ' > p' ).html( msg );
326
+ $updateResultMessage.show();
327
+ }
328
+
329
+ function isValidEmailAddress( emailAddress ) {
330
+ if ( '' === emailAddress ) {
331
+ return false;
332
+ }
333
+
334
+ return /[0-9a-zA-Z][a-zA-Z\+0-9\.\_\-]*@[0-9a-zA-Z\-]+(\.[a-zA-Z]{2,24}){1,3}/.test( emailAddress );
335
+ }
336
+
337
+ function toggleOptions( show ) {
338
+ $modal.find( '.fs-email-addresses-ownership-options' ).toggle( show );
339
+
340
+ if ( ! show ) {
341
+ $modal.find( '.fs-assets-transfership-options' ).hide();
342
+ } else if ( 'both' === selectedEmailAddressesOwnershipOption ) {
343
+ $modal.find( '.fs-assets-transfership-options' ).show();
344
+ }
345
+ }
346
+ } )( jQuery );
347
+ </script>
freemius/templates/forms/license-activation.php CHANGED
@@ -30,17 +30,10 @@
30
  if ( $fs->is_registered() ) {
31
  $activate_button_text = $header_title;
32
  } else {
33
- $freemius_site_url = $fs->has_paid_plan() ?
34
- 'https://freemius.com/' :
35
- // Insights platform information.
36
- $fs->get_usage_tracking_terms_url();
37
-
38
- $freemius_link = '<a href="' . $freemius_site_url . '" target="_blank" rel="noopener" tabindex="0">freemius.com</a>';
39
-
40
  $message_below_input_field = sprintf(
41
- fs_text_inline( 'The %1$s will be periodically sending data to %2$s to check for security and feature updates, and verify the validity of your license.', 'license-sync-disclaimer', $slug ),
42
  $fs->get_module_label( true ),
43
- $freemius_link
44
  );
45
 
46
  $activate_button_text = fs_text_inline( 'Agree & Activate License', 'agree-activate-license', $slug );
@@ -59,23 +52,51 @@
59
  if ( $is_network_activation ) {
60
  $all_sites = Freemius::get_sites();
61
 
 
 
 
62
  foreach ( $all_sites as $site ) {
63
  $site_details = $fs->get_site_info( $site );
64
 
 
 
 
 
65
  $blog_id = Freemius::get_site_blog_id( $site );
66
  $install = $fs->get_install_by_blog_id($blog_id);
67
 
68
- if ( is_object( $install ) && FS_Plugin_License::is_valid_id( $install->license_id ) ) {
69
- $site_details['license_id'] = $install->license_id;
70
- }
 
71
 
72
- $sites_details[] = $site_details;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
  if ( $is_network_activation ) {
76
  $vars = array(
77
  'id' => $fs->get_id(),
78
- 'sites' => $sites_details,
79
  'require_license_key' => true
80
  );
81
 
@@ -341,7 +362,7 @@ HTML;
341
  $activateLicenseButton.html( '<?php fs_esc_js_echo_inline( 'Please wait', 'please-wait', $slug ) ?>...' );
342
 
343
  $.ajax( {
344
- url : ajaxurl,
345
  method : 'POST',
346
  data : {
347
  action : '<?php echo $fs->get_ajax_action( 'fetch_is_marketing_required_flag_value' ) ?>',
@@ -589,7 +610,6 @@ HTML;
589
  url : $this.find( '.url' ).val(),
590
  title : $this.find( '.title' ).val(),
591
  language: $this.find( '.language' ).val(),
592
- charset : $this.find( '.charset' ).val(),
593
  blog_id : $this.find( '.blog-id' ).find( 'span' ).text()
594
  };
595
 
@@ -607,7 +627,7 @@ HTML;
607
  }
608
 
609
  $.ajax({
610
- url: ajaxurl,
611
  method: 'POST',
612
  data: data,
613
  beforeSend: function () {
@@ -867,4 +887,4 @@ HTML;
867
  }
868
  });
869
  })( jQuery );
870
- </script>
30
  if ( $fs->is_registered() ) {
31
  $activate_button_text = $header_title;
32
  } else {
 
 
 
 
 
 
 
33
  $message_below_input_field = sprintf(
34
+ fs_text_inline( 'The %1$s will be periodically sending essential license data to %2$s to check for security and feature updates, and verify the validity of your license.', 'license-sync-disclaimer', $slug ),
35
  $fs->get_module_label( true ),
36
+ "<b>{$fs->get_plugin_title()}</b>"
37
  );
38
 
39
  $activate_button_text = fs_text_inline( 'Agree & Activate License', 'agree-activate-license', $slug );
52
  if ( $is_network_activation ) {
53
  $all_sites = Freemius::get_sites();
54
 
55
+ $subsite_data_by_install_id = array();
56
+ $install_url_by_install_id = array();
57
+
58
  foreach ( $all_sites as $site ) {
59
  $site_details = $fs->get_site_info( $site );
60
 
61
+ if ( FS_Clone_Manager::instance()->is_temporary_duplicate_by_blog_id( $site_details['blog_id'] ) ) {
62
+ continue;
63
+ }
64
+
65
  $blog_id = Freemius::get_site_blog_id( $site );
66
  $install = $fs->get_install_by_blog_id($blog_id);
67
 
68
+ if ( is_object( $install ) ) {
69
+ if ( isset( $subsite_data_by_install_id[ $install->id ] ) ) {
70
+ $clone_subsite_data = $subsite_data_by_install_id[ $install->id ];
71
+ $clone_install_url = $install_url_by_install_id[ $install->id ];
72
 
73
+ if (
74
+ /**
75
+ * If we already have an install with the same URL as the subsite it's stored in, skip the current subsite. Otherwise, replace the existing install's data with the current subsite's install's data if the URLs match.
76
+ *
77
+ * @author Leo Fajardo (@leorw)
78
+ * @since 2.5.0
79
+ */
80
+ fs_strip_url_protocol( untrailingslashit( $clone_install_url ) ) === fs_strip_url_protocol( untrailingslashit( $clone_subsite_data['url'] ) ) ||
81
+ fs_strip_url_protocol( untrailingslashit( $install->url ) ) !== fs_strip_url_protocol( untrailingslashit( $site_details['url'] ) )
82
+ ) {
83
+ continue;
84
+ }
85
+ }
86
+
87
+ if ( FS_Plugin_License::is_valid_id( $install->license_id ) ) {
88
+ $site_details['license_id'] = $install->license_id;
89
+ }
90
+
91
+ $subsite_data_by_install_id[ $install->id ] = $site_details;
92
+ $install_url_by_install_id[ $install->id ] = $install->url;
93
+ }
94
  }
95
 
96
  if ( $is_network_activation ) {
97
  $vars = array(
98
  'id' => $fs->get_id(),
99
+ 'sites' => array_values( $subsite_data_by_install_id ),
100
  'require_license_key' => true
101
  );
102
 
362
  $activateLicenseButton.html( '<?php fs_esc_js_echo_inline( 'Please wait', 'please-wait', $slug ) ?>...' );
363
 
364
  $.ajax( {
365
+ url : <?php echo Freemius::ajax_url() ?>,
366
  method : 'POST',
367
  data : {
368
  action : '<?php echo $fs->get_ajax_action( 'fetch_is_marketing_required_flag_value' ) ?>',
610
  url : $this.find( '.url' ).val(),
611
  title : $this.find( '.title' ).val(),
612
  language: $this.find( '.language' ).val(),
 
613
  blog_id : $this.find( '.blog-id' ).find( 'span' ).text()
614
  };
615
 
627
  }
628
 
629
  $.ajax({
630
+ url: <?php echo Freemius::ajax_url() ?>,
631
  method: 'POST',
632
  data: data,
633
  beforeSend: function () {
887
  }
888
  });
889
  })( jQuery );
890
+ </script>
freemius/templates/forms/optout.php CHANGED
@@ -17,320 +17,166 @@
17
  $fs = freemius( $VARS['id'] );
18
  $slug = $fs->get_slug();
19
 
20
- $action = $fs->is_tracking_allowed() ?
21
- 'stop_tracking' :
22
- 'allow_tracking';
23
-
24
  $reconnect_url = $fs->get_activation_url( array(
25
  'nonce' => wp_create_nonce( $fs->get_unique_affix() . '_reconnect' ),
26
  'fs_action' => ( $fs->get_unique_affix() . '_reconnect' ),
27
  ) );
28
 
29
- $plugin_title = "<strong>{$fs->get_plugin()->title}</strong>";
30
- $opt_out_text = fs_text_x_inline( 'Opt Out', 'verb', 'opt-out', $slug );
31
- $opt_in_text = fs_text_x_inline( 'Opt In', 'verb', 'opt-in', $slug );
32
-
33
- if ( $fs->is_premium() ) {
34
- $opt_in_message_appreciation = fs_text_inline( 'Connectivity to the licensing engine was successfully re-established. Automatic security & feature updates are now available through the WP Admin Dashboard.', 'premium-opt-in-message-appreciation', $slug );
35
-
36
- $opt_out_message_subtitle = sprintf( fs_text_inline( 'Warning: Opting out will block automatic updates', 'premium-opt-out-message-appreciation', $slug ), $fs->get_module_type() );
37
- $opt_out_message_usage_tracking = sprintf( fs_text_inline( 'Ongoing connectivity with the licensing engine is essential for receiving automatic security & feature updates of the paid product. To receive these updates, data like your license key, %1$s version, and WordPress version, is periodically sent to the server to check for updates. By opting out, you understand that your site won\'t receive automatic updates for %2$s from within the WP Admin Dashboard. This can put your site at risk, and we highly recommend to keep this connection active. If you do choose to opt-out, you\'ll need to check for %1$s updates and install them manually.', 'premium-opt-out-message-usage-tracking', $slug ), $fs->get_module_type(), $plugin_title );
38
-
39
- $primary_cta_label = fs_text_inline( 'I\'d like to keep automatic updates', 'premium-opt-out-cancel', $slug );
40
- } else {
41
- $opt_in_message_appreciation = sprintf( fs_text_inline( 'We appreciate your help in making the %s better by letting us track some usage data.', 'opt-in-message-appreciation', $slug ), $fs->get_module_type() );
42
-
43
- $opt_out_message_subtitle = $opt_in_message_appreciation;
44
- $opt_out_message_usage_tracking = sprintf( fs_text_inline( "Usage tracking is done in the name of making %s better. Making a better user experience, prioritizing new features, and more good things. We'd really appreciate if you'll reconsider letting us continue with the tracking.", 'opt-out-message-usage-tracking', $slug ), $plugin_title );
45
- $primary_cta_label = fs_text_inline( 'On second thought - I want to continue helping', 'opt-out-cancel', $slug );
46
- }
47
-
48
- $opt_out_message_clicking_opt_out = sprintf(
49
- fs_text_inline( 'By clicking "Opt Out", we will no longer be sending any data from %s to %s.', 'opt-out-message-clicking-opt-out', $slug ),
50
- $plugin_title,
51
- sprintf(
52
- '<a href="%s" target="_blank" rel="noopener">%s</a>',
53
- 'https://freemius.com',
54
- 'freemius.com'
55
- )
56
- );
57
 
58
- $admin_notice_params = array(
59
- 'id' => '',
60
- 'slug' => $fs->get_id(),
61
- 'type' => 'success',
62
- 'sticky' => false,
63
- 'plugin' => $fs->get_plugin()->title,
64
- 'message' => $opt_in_message_appreciation
65
- );
66
-
67
- $admin_notice_html = fs_get_template( 'admin-notice.php', $admin_notice_params );
68
-
69
- $modal_content_html = "
70
- <h2" . ( $fs->is_premium() ? ' style="color: red"' : '' ) . ">{$opt_out_message_subtitle}</h2>
71
- <div class=\"notice notice-error inline opt-out-error-message\"><p></p></div>
72
- <p>{$opt_out_message_usage_tracking}</p>
73
- <p>{$opt_out_message_clicking_opt_out}</p>
74
- <label class=\"fs-permission-extensions\"><div class=\"fs-switch fs-small fs-round fs-" . ( $fs->is_extensions_tracking_allowed() ? 'on' : 'off' ) . "\"><div class=\"fs-toggle\"></div></div> " . fs_text_inline( 'Plugins & themes tracking' ) . " <span class=\"fs-switch-feedback success\"></span></label>";
75
 
76
  fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
 
77
  fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  <script type="text/javascript">
80
  (function( $ ) {
81
  $( document ).ready(function() {
82
- var modalContentHtml = <?php echo json_encode( $modal_content_html ) ?>,
83
- modalHtml =
84
- '<div class="fs-modal fs-modal-opt-out">'
85
- + ' <div class="fs-modal-dialog">'
86
- + ' <div class="fs-modal-header">'
87
- + ' <h4><?php echo esc_js( $opt_out_text ) ?></h4>'
88
- + ' </div>'
89
- + ' <div class="fs-modal-body">'
90
- + ' <div class="fs-modal-panel active">' + modalContentHtml + '</div>'
91
- + ' </div>'
92
- + ' <div class="fs-modal-footer">'
93
- + ' <button class="button <?php echo $fs->is_premium() ? 'button-primary warn' : 'button-secondary' ?> button-opt-out" tabindex="1"><?php echo esc_js( $opt_out_text ) ?></button>'
94
- + ' <button class="button <?php echo $fs->is_premium() ? 'button-secondary' : 'button-primary' ?> button-close" tabindex="2"><?php echo esc_js( $primary_cta_label ) ?></button>'
95
- + ' </div>'
96
- + ' </div>'
97
- + '</div>',
98
- $modal = $(modalHtml),
99
- $adminNotice = $( <?php echo json_encode( $admin_notice_html ) ?> ),
100
- action = '<?php echo $action ?>',
101
- actionLinkSelector = 'span.opt-in-or-opt-out.<?php echo $slug ?> a',
102
- $optOutButton = $modal.find( '.button-opt-out' ),
103
- $optOutErrorMessage = $modal.find( '.opt-out-error-message' ),
104
- $extensionsTracking = $modal.find( '.fs-permission-extensions' ),
105
- $body = $( 'body' ),
106
- moduleID = '<?php echo $fs->get_id() ?>';
107
-
108
- $modal.data( 'action', action );
109
- $modal.appendTo( $body );
110
-
111
- function registerActionLinkClick() {
112
- $body.on( 'click', actionLinkSelector, function( evt ) {
113
- evt.preventDefault();
114
-
115
- if ( 'stop_tracking' == $modal.data( 'action' ) ) {
116
- showModal();
117
- } else {
118
- optIn();
119
- }
120
-
121
- return false;
122
- });
123
- }
124
-
125
- function registerEventHandlers() {
126
- registerActionLinkClick();
127
-
128
- $modal.on( 'click', '.button-opt-out', function( evt ) {
129
- evt.preventDefault();
130
-
131
- if ( $( this ).hasClass( 'disabled' ) ) {
132
- return;
133
- }
134
-
135
- disableOptOutButton();
136
- optOut();
137
- });
138
-
139
- // If the user has clicked outside the window, close the modal.
140
- $modal.on( 'click', '.fs-close, .button-close', function() {
141
- closeModal();
142
- return false;
143
- });
144
- }
145
-
146
- <?php if ( $fs->is_registered() ) : ?>
147
- registerEventHandlers();
148
- <?php endif ?>
149
-
150
- function showModal() {
151
- resetModal();
152
-
153
- // Display the dialog box.
154
- $modal.addClass( 'active' );
155
- $body.addClass( 'has-fs-modal' );
156
- }
157
-
158
- function closeModal() {
159
- $modal.removeClass( 'active' );
160
- $body.removeClass( 'has-fs-modal' );
161
- }
162
-
163
- function resetOptOutButton() {
164
- enableOptOutButton();
165
- $optOutButton.text( <?php echo json_encode( $opt_out_text ) ?> );
166
- }
167
-
168
- function resetModal() {
169
- hideError();
170
- resetOptOutButton();
171
- }
172
-
173
- function optIn() {
174
- sendRequest();
175
- }
176
-
177
- function optOut() {
178
- sendRequest();
179
- }
180
-
181
- function sendRequest() {
182
- var $actionLink = $( actionLinkSelector );
183
-
184
- $.ajax({
185
- url: ajaxurl,
186
- method: 'POST',
187
- data: {
188
- action : ( 'stop_tracking' == action ?
189
- '<?php echo $fs->get_ajax_action( 'stop_tracking' ) ?>' :
190
- '<?php echo $fs->get_ajax_action( 'allow_tracking' ) ?>'
191
- ),
192
- security : ( 'stop_tracking' == action ?
193
- '<?php echo $fs->get_ajax_security( 'stop_tracking' ) ?>' :
194
- '<?php echo $fs->get_ajax_security( 'allow_tracking' ) ?>'
195
- ),
196
- module_id: moduleID,
197
- _wp_http_referer: '<?php echo $fs->current_page_url() ?>'
198
- },
199
- beforeSend: function() {
200
- if ( 'allow_tracking' == action ) {
201
- $actionLink.text( '<?php fs_esc_js_echo_inline( 'Opting in', 'opting-in', $slug ) ?>...' );
202
- } else {
203
- $optOutButton.text( '<?php fs_esc_js_echo_inline( 'Opting out', 'opting-out', $slug ) ?>...' );
204
- }
205
- },
206
- success: function( resultObj ) {
207
- if ( resultObj.success ) {
208
- if ( 'allow_tracking' == action ) {
209
- action = 'stop_tracking';
210
- $actionLink.text( '<?php echo esc_js( $opt_out_text ) ?>' );
211
- showOptInAppreciationMessageAndScrollToTop();
212
- } else {
213
- action = 'allow_tracking';
214
- $actionLink.text( '<?php echo esc_js( $opt_in_text ) ?>' );
215
- closeModal();
216
-
217
- if ( $adminNotice.length > 0 ) {
218
- $adminNotice.remove();
219
- }
220
- }
221
-
222
- $modal.data( 'action', action );
223
- } else {
224
- showError( resultObj.error );
225
- resetOptOutButton();
226
- }
227
- }
228
- });
229
- }
230
-
231
- var isUpdatingPermission = false;
232
- $extensionsTracking.on('click', function() {
233
- if (isUpdatingPermission) {
234
- return false;
235
- }
236
-
237
- isUpdatingPermission = true;
238
-
239
- var $switch = $extensionsTracking.find( '.fs-switch' ),
240
- $switchFeedback = $extensionsTracking.find( '.fs-switch-feedback' );
241
-
242
- $switch
243
- .toggleClass( 'fs-on' )
244
- .toggleClass( 'fs-off' );
245
-
246
- $switchFeedback.html( '<i class="fs-ajax-spinner"></i>' );
247
-
248
- $.ajax({
249
- url: ajaxurl,
250
- method: 'POST',
251
- data: {
252
- action : '<?php echo $fs->get_ajax_action( 'update_tracking_permission' ) ?>',
253
- security : '<?php echo $fs->get_ajax_security( 'update_tracking_permission' ) ?>',
254
- module_id : moduleID,
255
- _wp_http_referer: '<?php echo $fs->current_page_url() ?>',
256
- permission: 'extensions',
257
- is_enabled: $switch.hasClass('fs-on')
258
- },
259
- success: function( resultObj ) {
260
- if ( resultObj.success ) {
261
- $switchFeedback.html( '<i class="dashicons dashicons-yes"></i> <?php echo esc_js( fs_text_inline( 'Saved', 'saved', $slug ) ) ?>' )
262
- } else {
263
- $switch
264
- .toggleClass( 'fs-on' )
265
- .toggleClass( 'fs-off' );
266
- }
267
-
268
- isUpdatingPermission = false;
269
- }
270
- });
271
- });
272
-
273
- function enableOptOutButton() {
274
- $optOutButton.removeClass( 'disabled' );
275
- }
276
-
277
- function disableOptOutButton() {
278
- $optOutButton.addClass( 'disabled' );
279
- }
280
-
281
- function hideError() {
282
- $optOutErrorMessage.hide();
283
- }
284
-
285
- function showOptInAppreciationMessageAndScrollToTop() {
286
- $adminNotice.insertAfter( $( '#wpbody-content' ).find( ' > .wrap > h1' ) );
287
- window.scrollTo(0, 0);
288
- }
289
-
290
- function showError( msg ) {
291
- $optOutErrorMessage.find( ' > p' ).html( msg );
292
- $optOutErrorMessage.show();
293
- }
294
-
295
- <?php if ( $fs->is_theme() ) : ?>
296
- /**
297
- * Add opt-in/out button to the active theme's buttons collection
298
- * in the theme's extended details overlay.
299
- *
300
- * @author Vova Feldman (@svovaf)
301
- * @since 1.2.2.7
302
- */
303
- $('.theme-overlay').contentChange(function () {
304
- if (0 === $('.theme-overlay.active').length) {
305
- // Add opt-in/out button only to the currently active theme.
306
- return;
307
- }
308
-
309
- if ($('#fs_theme_opt_in_out').length > 0){
310
- // Button already there.
311
- return;
312
- }
313
-
314
- var label = (('stop_tracking' == action) ?
315
- '<?php echo esc_js( $opt_out_text ) ?>' :
316
- '<?php echo esc_js( $opt_in_text ) ?>'),
317
- href = (('stop_tracking' != action) ?
318
- '<?php echo ( $fs->is_registered() ? '' : esc_js( $reconnect_url ) ) ?>' :
319
- '');
320
-
321
- var $actionLink = $('<a id="fs_theme_opt_in_out" href="' + encodeURI(href) + '" class="button">' + label + '</a>');
322
-
323
- actionLinkSelector = '#fs_theme_opt_in_out';
324
-
325
- $modal.data( 'action', action );
326
-
327
- $('.theme-wrap .theme-actions .active-theme').append($actionLink);
328
-
329
- if ('' === href) {
330
- registerActionLinkClick();
331
- }
332
- });
333
- <?php endif ?>
334
  });
335
  })( jQuery );
336
  </script>
17
  $fs = freemius( $VARS['id'] );
18
  $slug = $fs->get_slug();
19
 
 
 
 
 
20
  $reconnect_url = $fs->get_activation_url( array(
21
  'nonce' => wp_create_nonce( $fs->get_unique_affix() . '_reconnect' ),
22
  'fs_action' => ( $fs->get_unique_affix() . '_reconnect' ),
23
  ) );
24
 
25
+ $plugin_title = "<strong>" . esc_html( $fs->get_plugin()->title ) . "</strong>";
26
+ $opt_out_text = fs_text_x_inline( 'Opt Out', 'verb', 'opt-out', $slug );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ $permission_manager = FS_Permission_Manager::instance( $fs );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
31
+ fs_enqueue_local_style( 'fs_optout', '/admin/optout.css' );
32
  fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
33
+
34
+ if ( ! $fs->is_premium() ) {
35
+ $optional_permissions = array( $permission_manager->get_extensions_permission( false,
36
+ false,
37
+ true
38
+ ) );
39
+
40
+ $permission_groups = array(
41
+ array(
42
+ 'id' => 'communication',
43
+ 'type' => 'required',
44
+ 'title' => $fs->get_text_inline( 'Communication', 'communication' ),
45
+ 'desc' => '',
46
+ 'permissions' => $permission_manager->get_opt_in_required_permissions( true ),
47
+ 'is_enabled' => $fs->is_registered(),
48
+ 'prompt' => array(
49
+ $fs->esc_html_inline( "Sharing your name and email allows us to keep you in the loop about new features and important updates, warn you about security issues before they become public knowledge, and send you special offers.", 'opt-out-message_user' ),
50
+ sprintf(
51
+ $fs->esc_html_inline( 'By clicking "Opt Out", %s will no longer be able to view your name and email.',
52
+ 'opt-out-message-clicking-opt-out' ),
53
+ $plugin_title
54
+ ),
55
+ ),
56
+ 'prompt_cancel_label' => $fs->get_text_inline( 'Stay Connected', 'stay-connected' )
57
+ ),
58
+ array(
59
+ 'id' => 'diagnostic',
60
+ 'type' => 'required',
61
+ 'title' => $fs->get_text_inline( 'Diagnostic Info', 'diagnostic-info' ),
62
+ 'desc' => '',
63
+ 'permissions' => $permission_manager->get_opt_in_diagnostic_permissions( true ),
64
+ 'is_enabled' => $fs->is_tracking_allowed(),
65
+ 'prompt' => array(
66
+ sprintf(
67
+ $fs->esc_html_inline( 'Sharing diagnostic data helps to provide additional functionality that\'s relevant to your website, avoid WordPress or PHP version incompatibilities that can break the website, and recognize which languages & regions the %s should be translated and tailored to.',
68
+ 'opt-out-message-clicking-opt-out' ),
69
+ $fs->get_module_type()
70
+ ),
71
+ sprintf(
72
+ $fs->esc_html_inline( 'By clicking "Opt Out", diagnostic data will no longer be sent to %s.',
73
+ 'opt-out-message-clicking-opt-out' ),
74
+ $plugin_title
75
+ ),
76
+ ),
77
+ 'prompt_cancel_label' => $fs->get_text_inline( 'Keep Sharing', 'keep-sharing' )
78
+ ),
79
+ array(
80
+ 'id' => 'extensions',
81
+ 'type' => 'optional',
82
+ 'title' => $fs->get_text_inline( 'Extensions', 'extensions' ),
83
+ 'desc' => '',
84
+ 'permissions' => $optional_permissions,
85
+ ),
86
+ );
87
+ } else {
88
+ $optional_permissions = $permission_manager->get_license_optional_permissions( false, true );
89
+
90
+ $permission_groups = array(
91
+ array(
92
+ 'id' => 'essentials',
93
+ 'type' => 'required',
94
+ 'title' => $fs->esc_html_inline( 'Required', 'required' ),
95
+ 'desc' => sprintf( $fs->esc_html_inline( 'For automatic delivery of security & feature updates, and license management & protection, %s needs to:',
96
+ 'license-sync-disclaimer' ),
97
+ '<b>' . esc_html( $fs->get_plugin_title() ) . '</b>' ),
98
+ 'permissions' => $permission_manager->get_license_required_permissions( true ),
99
+ 'is_enabled' => $permission_manager->is_essentials_tracking_allowed(),
100
+ 'prompt' => array(
101
+ sprintf( $fs->esc_html_inline( 'To ensure that security & feature updates are automatically delivered directly to your WordPress Admin Dashboard while protecting your license from unauthorized abuse, %2$s needs to view the website’s homepage URL, %1$s version, SDK version, and whether the %1$s is active.', 'premium-opt-out-message-usage-tracking' ), $fs->get_module_type(), $plugin_title ),
102
+ sprintf( $fs->esc_html_inline( 'By opting out from sharing this information with the updates server, you’ll have to check for new %1$s releases and manually download & install them. Not just a hassle, but missing an update can put your site at risk and cause undue compatibility issues, so we highly recommend keeping these essential permissions on.', 'opt-out-message-clicking-opt-out' ), $fs->get_module_type(), $plugin_title ),
103
+ ),
104
+ 'prompt_cancel_label' => $fs->get_text_inline( 'Keep automatic updates', 'premium-opt-out-cancel' )
105
+ ),
106
+ array(
107
+ 'id' => 'optional',
108
+ 'type' => 'optional',
109
+ 'title' => $fs->esc_html_inline( 'Optional', 'optional' ),
110
+ 'desc' => sprintf( $fs->esc_html_inline( 'For ongoing compatibility with your website, you can optionally allow %s to:',
111
+ 'optional-permissions-disclaimer' ), $plugin_title ),
112
+ 'permissions' => $optional_permissions,
113
+ ),
114
+ );
115
+ }
116
+
117
+ $ajax_action = 'toggle_permission_tracking';
118
+
119
+ $form_id = "fs_opt_out_{$fs->get_id()}";
120
  ?>
121
+ <div id="<?php echo $form_id ?>"
122
+ class="fs-modal fs-modal-opt-out"
123
+ data-plugin-id="<?php echo $fs->get_id() ?>"
124
+ data-action="<?php echo $fs->get_ajax_action( $ajax_action ) ?>"
125
+ data-security="<?php echo $fs->get_ajax_security( $ajax_action ) ?>"
126
+ style="display: none">
127
+ <div class="fs-modal-dialog">
128
+ <div class="fs-modal-header">
129
+ <h4><?php echo esc_html( $opt_out_text ) ?></h4>
130
+ <a href="!#" class="fs-close"><i class="dashicons dashicons-no" title="Dismiss"></i></a>
131
+ </div>
132
+ <div class="fs-opt-out-permissions">
133
+ <div class="fs-modal-body">
134
+ <div class="notice notice-error inline opt-out-error-message"><p></p></div>
135
+ <div class="fs-permissions fs-open">
136
+ <?php foreach ( $permission_groups as $i => $permission_group ) : ?>
137
+ <?php $permission_manager->render_permissions_group( $permission_group ) ?>
138
+ <?php if ( $i < count( $permission_groups ) - 1 ) : ?><hr><?php endif ?>
139
+ <?php endforeach ?>
140
+ </div>
141
+ </div>
142
+ <div class="fs-modal-footer">
143
+ <button class="button button-primary button-close" tabindex="1"><?php echo $fs->esc_html_inline( 'Done', 'done' ) ?></button>
144
+ </div>
145
+ </div>
146
+ <?php foreach ( $permission_groups as $i => $permission_group ) : ?>
147
+ <?php if ( ! empty( $permission_group[ 'prompt' ] ) ) : ?>
148
+ <div class="fs-<?php echo $permission_group[ 'id' ] ?>-opt-out fs-opt-out-disclaimer" data-group-id="<?php echo $permission_group[ 'id' ] ?>" style="display: none">
149
+ <div class="fs-modal-body">
150
+ <div class="fs-modal-panel active">
151
+ <div class="notice notice-error inline opt-out-error-message"><p></p></div>
152
+ <?php foreach ( $permission_group[ 'prompt' ] as $p ) : ?>
153
+ <p><?php echo $p ?></p>
154
+ <?php endforeach ?>
155
+ </div>
156
+ </div>
157
+ <div class="fs-modal-footer">
158
+ <a class="fs-opt-out-button" tabindex="2" href="#"><?php echo esc_html( $opt_out_text ) ?></a>
159
+ <button class="button button-primary fs-opt-out-cancel-button" tabindex="1"><?php echo esc_html( $permission_group[ 'prompt_cancel_label' ] ) ?></button>
160
+ </div>
161
+ </div>
162
+ <?php endif ?>
163
+ <?php endforeach ?>
164
+ </div>
165
+ </div>
166
+
167
+ <?php $permission_manager->require_permissions_js( false ) ?>
168
+
169
  <script type="text/javascript">
170
  (function( $ ) {
171
  $( document ).ready(function() {
172
+ FS.OptOut(
173
+ '<?php echo $fs->get_id() ?>',
174
+ '<?php echo $slug ?>',
175
+ '<?php echo $fs->get_module_type() ?>',
176
+ <?php echo $fs->is_registered( true ) ? 'true' : 'false' ?>,
177
+ <?php echo $fs->is_tracking_allowed() ? 'true' : 'false' ?>,
178
+ '<?php echo esc_js( $reconnect_url ) ?>'
179
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  });
181
  })( jQuery );
182
  </script>
freemius/templates/forms/resend-key.php CHANGED
@@ -54,7 +54,10 @@ HTML;
54
  HTML;
55
  }
56
 
57
- $message_above_input_field = fs_esc_html_inline( "Enter the email address you've used for the upgrade below and we will resend you the license key.", 'ask-for-upgrade-email-address', $slug );
 
 
 
58
  $modal_content_html = <<< HTML
59
  <div class="notice notice-error inline license-resend-message"><p></p></div>
60
  <p>{$message_above_input_field}</p>
@@ -142,7 +145,7 @@ HTML;
142
  }
143
 
144
  $.ajax({
145
- url : ajaxurl,
146
  method : 'POST',
147
  data : {
148
  action : '<?php echo $fs->get_ajax_action( 'resend_license_key' ) ?>',
54
  HTML;
55
  }
56
 
57
+ $message_above_input_field = $fs->is_only_premium() ?
58
+ fs_esc_html_inline( "Enter the email address you've used during the purchase and we will resend you the license key.", 'ask-for-upgrade-email-address-premium-only', $slug ) :
59
+ fs_esc_html_inline( "Enter the email address you've used for the upgrade below and we will resend you the license key.", 'ask-for-upgrade-email-address', $slug );
60
+
61
  $modal_content_html = <<< HTML
62
  <div class="notice notice-error inline license-resend-message"><p></p></div>
63
  <p>{$message_above_input_field}</p>
145
  }
146
 
147
  $.ajax({
148
+ url : <?php echo Freemius::ajax_url() ?>,
149
  method : 'POST',
150
  data : {
151
  action : '<?php echo $fs->get_ajax_action( 'resend_license_key' ) ?>',
freemius/templates/forms/trial-start.php CHANGED
@@ -80,7 +80,7 @@ HTML;
80
  var $button = $(this);
81
 
82
  $.ajax({
83
- url : ajaxurl,
84
  method : 'POST',
85
  data : {
86
  action : '<?php echo $fs->get_ajax_action( 'start_trial' ) ?>',
80
  var $button = $(this);
81
 
82
  $.ajax({
83
+ url : <?php echo Freemius::ajax_url() ?>,
84
  method : 'POST',
85
  data : {
86
  action : '<?php echo $fs->get_ajax_action( 'start_trial' ) ?>',
freemius/templates/forms/user-change.php CHANGED
@@ -194,7 +194,7 @@ HTML;
194
  disableUserChangeButton();
195
 
196
  $.ajax( {
197
- url : ajaxurl,
198
  method : 'POST',
199
  data : {
200
  action : '<?php echo $fs->get_ajax_action( 'change_user' ) ?>',
194
  disableUserChangeButton();
195
 
196
  $.ajax( {
197
+ url : <?php echo Freemius::ajax_url() ?>,
198
  method : 'POST',
199
  data : {
200
  action : '<?php echo $fs->get_ajax_action( 'change_user' ) ?>',
freemius/templates/gdpr-optin-js.php CHANGED
@@ -38,7 +38,7 @@
38
  }
39
 
40
  $.ajax({
41
- url : ajaxurl + '?' + $.param({
42
  action : '<?php echo $fs->get_ajax_action( 'gdpr_optin_action' ) ?>',
43
  security : '<?php echo $fs->get_ajax_security( 'gdpr_optin_action' ) ?>',
44
  module_id: '<?php echo $fs->get_id() ?>'
38
  }
39
 
40
  $.ajax({
41
+ url : <?php echo Freemius::ajax_url() ?> + '?' + $.param({
42
  action : '<?php echo $fs->get_ajax_action( 'gdpr_optin_action' ) ?>',
43
  security : '<?php echo $fs->get_ajax_security( 'gdpr_optin_action' ) ?>',
44
  module_id: '<?php echo $fs->get_id() ?>'
freemius/templates/js/permissions.php ADDED
@@ -0,0 +1,546 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package Freemius
4
+ * @copyright Copyright (c) 2015, Freemius, Inc.
5
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6
+ * @since 2.5.1
7
+ */
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+ ?>
12
+ <script type="text/javascript">
13
+ ( function ( $ ) {
14
+ var global = this;
15
+
16
+ // Namespace.
17
+ global.FS = global.FS || {};
18
+
19
+ //region Switches
20
+
21
+ function toggleSwitches( $switches, isOn ) {
22
+ $switches
23
+ .toggleClass( 'fs-on', ( null != isOn ? ( true === isOn ) : isOn ) )
24
+ .toggleClass( 'fs-off', ( null != isOn ? ( false === isOn ) : isOn ) );
25
+ }
26
+
27
+ function isSwitch( $switch, isOn ) {
28
+ return $switch.hasClass( isOn ? 'fs-on' : 'fs-off' );
29
+ }
30
+
31
+ function getSwitchesStates( $switches, isEnabled ) {
32
+ var switchStates = [];
33
+ for ( var i = 0; i < $switches.length; i++ ) {
34
+ switchStates.push( isSwitch( $( $switches[ i ] ), isEnabled ) );
35
+ }
36
+
37
+ return switchStates;
38
+ }
39
+
40
+ //endregion
41
+
42
+ function toggleGroupOptOut( $button, isEnabled ) {
43
+ setOptInLabel( $button, ! isEnabled );
44
+
45
+ $button.data( 'is-enabled', isEnabled );
46
+ }
47
+
48
+ /**
49
+ * @param {object} $permissionsSection
50
+ *
51
+ * @returns {string[]}
52
+ */
53
+ function getGroupPermissionIDs( $permissionsSection ) {
54
+ var permissions = [];
55
+ $permissionsSection.find( 'ul li').each( function() {
56
+ permissions.push( $( this ).data( 'permission-id' ) );
57
+ });
58
+
59
+ return permissions;
60
+ }
61
+
62
+ function getGroupOptOutButton( $section ) {
63
+ return $section.find( '.fs-group-opt-out-button' );
64
+ }
65
+
66
+ //region Opt-in/out Labels
67
+
68
+ function setUpdatingLabel( $button, isEnabled ) {
69
+ $button.text( isEnabled ?
70
+ '<?php fs_esc_js_echo_inline( 'Opting in', 'opting-in' ) ?>...' :
71
+ '<?php fs_esc_js_echo_inline( 'Opting out', 'opting-out' ) ?>...'
72
+ );
73
+ }
74
+
75
+ function getOptInLabel( isEnabled ) {
76
+ return isEnabled ?
77
+ '<?php echo esc_js( fs_text_x_inline( 'Opt In', 'verb', 'opt-in' ) ) ?>' :
78
+ '<?php echo esc_js( fs_text_x_inline( 'Opt Out', 'verb', 'opt-out' ) ) ?>';
79
+ }
80
+
81
+ function setOptInLabel( $button, isEnabled ) {
82
+ $button.text( getOptInLabel( isEnabled ) );
83
+ }
84
+
85
+ //endregion
86
+
87
+ global.FS.Permissions = function () {
88
+ var isUpdating = false;
89
+
90
+ function updateStarted() {
91
+ isUpdating = true;
92
+ $body.addClass( 'fs-loading' );
93
+ }
94
+
95
+ function updateCompleted() {
96
+ isUpdating = false;
97
+ $body.removeClass( 'fs-loading' );
98
+ }
99
+
100
+ return {
101
+ isUpdating: function() {
102
+ return isUpdating;
103
+ },
104
+ /**
105
+ * @param {Number} pluginID
106
+ * @param {Array} permissions
107
+ * @param {Boolean} isEnabled
108
+ * @param {Callback} [success]
109
+ * @param {Callback} [failure]
110
+ * @param {Callback} [complete]
111
+ */
112
+ updatePermissions: function(
113
+ pluginID,
114
+ permissions,
115
+ isEnabled,
116
+ success,
117
+ failure,
118
+ complete
119
+ ) {
120
+ if ( isUpdating ) {
121
+ return;
122
+ }
123
+
124
+ updateStarted();
125
+
126
+ var
127
+ $permissionsContainer = $( '#fs_opt_out_' + pluginID );
128
+
129
+ $.ajax( {
130
+ url : <?php echo Freemius::ajax_url() ?>,
131
+ method : 'POST',
132
+ data : {
133
+ action : $permissionsContainer.data( 'action' ),
134
+ security : $permissionsContainer.data( 'security' ),
135
+ module_id : pluginID,
136
+ _wp_http_referer: '<?php echo Freemius::current_page_url() ?>',
137
+ permissions : permissions.join( ',' ),
138
+ is_enabled : isEnabled
139
+ },
140
+ success : function ( resultObj ) {
141
+ if ( resultObj.success ) {
142
+ if ( success ) {
143
+ success( resultObj );
144
+ }
145
+ } else {
146
+ if ( failure ) {
147
+ failure( resultObj );
148
+ }
149
+ }
150
+ },
151
+ error : failure,
152
+ complete: function () {
153
+ if ( complete ) {
154
+ complete();
155
+ }
156
+
157
+ updateCompleted();
158
+ }
159
+ });
160
+ },
161
+ updateGroupPermissions: function(
162
+ pluginID,
163
+ groupID,
164
+ isEnabled,
165
+ success,
166
+ failure,
167
+ complete
168
+ ) {
169
+ if ( isUpdating ) {
170
+ return;
171
+ }
172
+
173
+ var
174
+ $modal = $( '#fs_opt_out_' + pluginID ),
175
+ $permissionsSection = $modal.find( '.fs-permissions-section.fs-' + groupID + '-permissions' ),
176
+ $optOutButton = getGroupOptOutButton( $permissionsSection ),
177
+ $permissions = $permissionsSection.find( 'ul li'),
178
+ permissions = [];
179
+
180
+ $permissions.each( function() {
181
+ permissions.push( $( this ).data( 'permission-id' ) );
182
+ });
183
+
184
+ setUpdatingLabel( $optOutButton, isEnabled );
185
+
186
+ this.updatePermissions(
187
+ pluginID,
188
+ permissions,
189
+ isEnabled,
190
+ function( resultObj ) {
191
+ if ( resultObj.success ) {
192
+ toggleGroupOptOut( $optOutButton, isEnabled );
193
+
194
+ // Update permissions state.
195
+ $permissions.toggleClass( 'fs-disabled', ! isEnabled );
196
+
197
+ // Update switches state, if there are any.
198
+ toggleSwitches( $permissions.find( '.fs-switch' ), isEnabled );
199
+
200
+ if ( success ) {
201
+ success();
202
+ }
203
+ }
204
+ },
205
+ function ( resultObj ) {
206
+ setOptInLabel( $optOutButton, isEnabled );
207
+
208
+ if ( failure ) {
209
+ failure( resultObj );
210
+ }
211
+ },
212
+ complete
213
+ );
214
+ }
215
+ };
216
+ }();
217
+
218
+ var $body = $( 'body' )
219
+
220
+ global.FS.OptOut = function (
221
+ pluginID,
222
+ slug,
223
+ type,
224
+ isRegistered,
225
+ isTrackingAllowed,
226
+ reconnectUrl
227
+ ) {
228
+ var $modal = $( '#fs_opt_out_' + pluginID ),
229
+ actionLinkSelector = ('theme' === type ? '#fs_theme_opt_in_out' : 'span.opt-in-or-opt-out.' + slug + ' a' );
230
+
231
+ //region Error Handling
232
+
233
+ function hideError( $optOutErrorMessage ) {
234
+ $optOutErrorMessage = $optOutErrorMessage || $modal.find( '.opt-out-error-message' );
235
+ $optOutErrorMessage.hide();
236
+ }
237
+
238
+ function showError( $optOutErrorMessage, msg ) {
239
+ $optOutErrorMessage.find( ' > p' ).html( msg );
240
+ $optOutErrorMessage.show();
241
+ }
242
+
243
+ //endregion
244
+
245
+ function backToPermissionsList() {
246
+ $modal.find( '.fs-opt-out-disclaimer' )
247
+ .hide();
248
+
249
+ $modal.find( '.fs-opt-out-permissions' )
250
+ .show();
251
+ }
252
+
253
+ function removeFeedbackIndicators() {
254
+ $modal.find( '.fs-switch-feedback' )
255
+ .remove();
256
+ }
257
+
258
+ //region Modal Dialog
259
+
260
+ function closeModal() {
261
+ $modal.removeClass( 'active' );
262
+ $body.removeClass( 'has-fs-modal' );
263
+ $modal.hide();
264
+ }
265
+
266
+ function resetModal() {
267
+ hideError();
268
+ removeFeedbackIndicators();
269
+ backToPermissionsList();
270
+ }
271
+
272
+ function showModal() {
273
+ resetModal();
274
+
275
+ // Display the dialog box.
276
+ $modal.show();
277
+ $modal.addClass( 'active' );
278
+ $body.addClass( 'has-fs-modal' );
279
+ }
280
+
281
+ //endregion
282
+
283
+ function registerActionLinkClick() {
284
+ $body.on( 'click', actionLinkSelector, function( evt ) {
285
+ evt.preventDefault();
286
+
287
+ showModal();
288
+
289
+ return false;
290
+ });
291
+ }
292
+
293
+ function registerEventHandlers() {
294
+ // If the user has clicked outside the window, close the modal.
295
+ $modal.on( 'click', '.fs-close, .button-close', function() {
296
+ closeModal();
297
+ return false;
298
+ } );
299
+
300
+ $modal.on( 'click', '.fs-permissions .fs-switch', function () {
301
+ if ( FS.Permissions.isUpdating() ) {
302
+ return false;
303
+ }
304
+
305
+ var $switch = $( this ),
306
+ $permission = $switch.closest( '.fs-permission' );
307
+
308
+ toggleSwitches( $switch );
309
+
310
+ $permission.toggleClass( 'fs-disabled' );
311
+
312
+ var $optOutContainer = $switch.closest( '.fs-modal-opt-out' );
313
+
314
+ if ( 0 === $optOutContainer.length ) {
315
+ return;
316
+ }
317
+
318
+ // Remove previously added feedback element.
319
+ $switch.closest( '.fs-modal-dialog' )
320
+ .find( '.fs-switch-feedback' )
321
+ .remove();
322
+
323
+ var $switchFeedback = $( '<span class="fs-switch-feedback"><i class="fs-ajax-spinner"></i></span>' );
324
+
325
+ $switch.after( $switchFeedback )
326
+
327
+ var
328
+ permissionID = $permission.data( 'permission-id' ),
329
+ isEnabled = isSwitch( $switch, true );
330
+
331
+ FS.Permissions.updatePermissions(
332
+ $optOutContainer.data( 'plugin-id' ),
333
+ [ permissionID ],
334
+ isEnabled,
335
+ function () {
336
+ $switchFeedback.addClass( 'success' );
337
+ $switchFeedback.html( '<i class="dashicons dashicons-yes"></i> <?php echo esc_js( fs_text_inline( 'Saved', 'saved' ) ) ?>' );
338
+
339
+ var
340
+ $permissionsGroup = $switch.closest( '.fs-permissions-section' ),
341
+ $groupPermissions = $permissionsGroup.find( 'ul li' );
342
+
343
+ var allGroupPermissionsUseSameValue = false;
344
+
345
+ if (
346
+ isEnabled &&
347
+ 0 === $groupPermissions.filter( '.fs-disabled' ).length )
348
+ {
349
+ allGroupPermissionsUseSameValue = true;
350
+ } else if (
351
+ ! isEnabled &&
352
+ $groupPermissions.length === $groupPermissions.filter( '.fs-disabled' ).length
353
+ ) {
354
+ allGroupPermissionsUseSameValue = true;
355
+ }
356
+
357
+ if ( allGroupPermissionsUseSameValue ) {
358
+ toggleGroupOptOut( getGroupOptOutButton( $permissionsGroup ), isEnabled );
359
+ }
360
+ },
361
+ function () {
362
+ // Revert switch.
363
+ toggleSwitches( $switch );
364
+
365
+ $switchFeedback.remove();
366
+ }
367
+ )
368
+ });
369
+
370
+ // Move back to the permissions list if cancelling opt-out.
371
+ $modal.on( 'click', '.fs-opt-out-disclaimer .fs-opt-out-cancel-button', function ( evt ) {
372
+ backToPermissionsList();
373
+ });
374
+
375
+ $modal.on( 'click', '.fs-opt-out-disclaimer .fs-modal-footer .fs-opt-out-button', function ( evt ) {
376
+ var
377
+ $optOutButton = $( this ),
378
+ $actionLink = $( actionLinkSelector ),
379
+ isEnabled = true,
380
+ $optOutDisclaimer = $( $optOutButton.closest( '.fs-opt-out-disclaimer' )[ 0 ] ),
381
+ groupID = $optOutDisclaimer.data( 'group-id' ),
382
+ $errorMessage = $optOutDisclaimer.find( '.opt-out-error-message' );
383
+
384
+ setUpdatingLabel( $optOutButton, ! isEnabled );
385
+
386
+ $optOutDisclaimer.find( '.button-primary' ).prop( 'disabled', true );
387
+
388
+ hideError( $errorMessage );
389
+
390
+ FS.Permissions.updateGroupPermissions(
391
+ pluginID,
392
+ groupID,
393
+ ! isEnabled,
394
+ function () {
395
+ if ( 'communication' === groupID ) {
396
+ window.location.reload();
397
+ } else {
398
+ setOptInLabel( $actionLink, ! isEnabled );
399
+
400
+ backToPermissionsList();
401
+ }
402
+ },
403
+ function ( resultObj ) {
404
+ setOptInLabel( $optOutButton, false );
405
+
406
+ showError( $errorMessage, resultObj.error );
407
+ },
408
+ function () {
409
+ if ( 'communication' !== groupID ) {
410
+ setOptInLabel( $optOutButton, false );
411
+ }
412
+
413
+ $optOutDisclaimer.find( '.button-primary' ).prop( 'disabled', false );
414
+ }
415
+ );
416
+ } );
417
+
418
+ $modal.on( 'click', '.fs-group-opt-out-button', function ( evt ) {
419
+ evt.preventDefault();
420
+
421
+ if ( FS.Permissions.isUpdating() ) {
422
+ return;
423
+ }
424
+
425
+ var
426
+ $optOutButton = $( this ),
427
+ groupID = $optOutButton.data( 'group-id' ),
428
+ isEnabled = $optOutButton.data( 'is-enabled' ),
429
+ $optOutDisclaimer = $modal.find( '.fs-' + groupID + '-opt-out' ),
430
+ isConfirmRequired = ( 0 < $optOutDisclaimer.length ),
431
+ $errorMessage = $modal.find( '.fs-opt-out-permissions .opt-out-error-message' );
432
+
433
+ $errorMessage.hide();
434
+
435
+ if ( isConfirmRequired ) {
436
+ if ( isEnabled ) {
437
+ // Move to disclaimer window.
438
+ $modal.find( '.fs-opt-out-permissions' )
439
+ .hide();
440
+
441
+ $optOutDisclaimer.show();
442
+ } else {
443
+ // Opt-in.
444
+ FS.Permissions.updateGroupPermissions(
445
+ pluginID,
446
+ groupID,
447
+ ! isEnabled,
448
+ ( 'communication' !== groupID ) ?
449
+ null :
450
+ function () {
451
+ window.location.reload();
452
+ },
453
+ function ( resultObj ) {
454
+ showError( $errorMessage, resultObj.error );
455
+ }
456
+ );
457
+ }
458
+ } else {
459
+ // Remove previously added feedback element.
460
+ $modal.find( '.fs-switch-feedback' )
461
+ .remove();
462
+
463
+ var $switches = $optOutButton.closest( '.fs-permissions-section' )
464
+ .find( '.fs-permission .fs-switch' );
465
+
466
+ var switchStates = getSwitchesStates( $switches, isEnabled );
467
+
468
+ toggleSwitches( $switches, ! isEnabled );
469
+
470
+ $switches.closest( '.fs-permission' )
471
+ .toggleClass( 'fs-disabled', isEnabled );
472
+
473
+ var $switchFeedback = $( '<span class="fs-switch-feedback"><i class="fs-ajax-spinner"></i></span>' );
474
+
475
+ $optOutButton.after( $switchFeedback )
476
+
477
+ setUpdatingLabel( $optOutButton, ! isEnabled );
478
+
479
+ FS.Permissions.updatePermissions(
480
+ pluginID,
481
+ getGroupPermissionIDs( $modal.find( '.fs-permissions-section.fs-' + groupID + '-permissions' ) ),
482
+ ! isEnabled,
483
+ function () {
484
+ $switchFeedback.addClass( 'success' );
485
+ $switchFeedback.html( '<i class="dashicons dashicons-yes"></i> <?php echo esc_js( fs_text_inline( 'Saved', 'saved' ) ) ?>' );
486
+
487
+ toggleGroupOptOut( $optOutButton, ! isEnabled );
488
+ },
489
+ function () {
490
+ // Revert switches to their previous state.
491
+ for ( var i = 0; i < switchStates.length; i++ ) {
492
+ if ( switchStates[ i ] ) {
493
+ toggleSwitches( $( $switches[ i ] ), isEnabled );
494
+ $( $switches[ i ] ).removeClass( 'fs-disabled' );
495
+ }
496
+ }
497
+
498
+ toggleGroupOptOut( $optOutButton, isEnabled );
499
+ }
500
+ )
501
+ }
502
+ });
503
+ }
504
+
505
+ if ( 'theme' === type ) {
506
+ /**
507
+ * Add opt-in/out button to the active theme's buttons collection
508
+ * in the theme's extended details overlay.
509
+ *
510
+ * @author Vova Feldman (@svovaf)
511
+ * @since 1.2.2.7
512
+ */
513
+ $( '.theme-overlay' ).contentChange( function () {
514
+ if ( 0 === $( '.theme-overlay.active' ).length ) {
515
+ // Add opt-in/out button only to the currently active theme.
516
+ return;
517
+ }
518
+
519
+ if ( $( '#fs_theme_opt_in_out' ).length > 0 ) {
520
+ // Button already there.
521
+ return;
522
+ }
523
+
524
+ var label = getOptInLabel( ! isTrackingAllowed ),
525
+ href = ( isTrackingAllowed || isRegistered ) ? '' : reconnectUrl,
526
+ $actionLink = $( '<a id="fs_theme_opt_in_out" href="' + encodeURI( href ) + '" class="button">' + label + '</a>' );
527
+
528
+ $( '.theme-wrap .theme-actions .active-theme' ).append( $actionLink );
529
+
530
+ if ( isRegistered && '' === href ) {
531
+ registerActionLinkClick();
532
+ }
533
+ });
534
+ }
535
+
536
+ if ( isRegistered ) {
537
+ if ( 'theme' !== type ) {
538
+ registerActionLinkClick();
539
+ }
540
+
541
+ registerEventHandlers();
542
+ }
543
+
544
+ };
545
+ } )( jQuery );
546
+ </script>
freemius/templates/partials/network-activation.php CHANGED
@@ -58,7 +58,7 @@
58
  <div class="fs-sites-list-container">
59
  <table cellspacing="0">
60
  <tbody>
61
- <?php $site_props = array('uid', 'url', 'title', 'charset', 'language') ?>
62
  <?php foreach ( $sites as $site ) : ?>
63
  <tr<?php if ( ! empty( $site['license_id'] ) ) {
64
  echo ' data-license-id="' . $site['license_id'] . '"';
58
  <div class="fs-sites-list-container">
59
  <table cellspacing="0">
60
  <tbody>
61
+ <?php $site_props = array('uid', 'url', 'title', 'language') ?>
62
  <?php foreach ( $sites as $site ) : ?>
63
  <tr<?php if ( ! empty( $site['license_id'] ) ) {
64
  echo ' data-license-id="' . $site['license_id'] . '"';
freemius/templates/plugin-icon.php CHANGED
@@ -14,7 +14,9 @@
14
  * @var array $VARS
15
  */
16
  $fs = freemius( $VARS['id'] );
 
 
17
  ?>
18
  <div class="fs-plugin-icon">
19
- <img src="<?php echo $fs->get_local_icon_url() ?>" width="80" height="80"/>
20
  </div>
14
  * @var array $VARS
15
  */
16
  $fs = freemius( $VARS['id'] );
17
+
18
+ $size = isset( $VARS['size'] ) ? $VARS['size'] : 80;
19
  ?>
20
  <div class="fs-plugin-icon">
21
+ <img src="<?php echo $fs->get_local_icon_url() ?>" width="<?php echo $size ?>" height="<?php echo $size ?>" />
22
  </div>
freemius/templates/plugin-info/description.php CHANGED
@@ -52,7 +52,7 @@
52
  <?php if ( ! empty( $plugin->info->screenshots ) ) : ?>
53
  <?php $screenshots = $plugin->info->screenshots ?>
54
  <div class="fs-screenshots clearfix">
55
- <h2><?php fs_esc_html_echo_inline( 'Screenshots', 'screenshots', $plugin->slug ) ?></h2>
56
  <ul>
57
  <?php $i = 0;
58
  foreach ( $screenshots as $s => $url ) : ?>
52
  <?php if ( ! empty( $plugin->info->screenshots ) ) : ?>
53
  <?php $screenshots = $plugin->info->screenshots ?>
54
  <div class="fs-screenshots clearfix">
55
+ <h3><?php fs_esc_html_echo_inline( 'Screenshots', 'screenshots', $plugin->slug ) ?></h3>
56
  <ul>
57
  <?php $i = 0;
58
  foreach ( $screenshots as $s => $url ) : ?>
freemius/templates/plugin-info/features.php CHANGED
@@ -43,7 +43,7 @@
43
  $support_feature->title = fs_text_inline( 'Support', $plugin->slug );
44
  $features_plan_map[ $support_feature->id ] = array( 'feature' => $support_feature, 'plans' => array() );
45
  } else {
46
- $support_feature = $features_plan_map['support'];
47
  }
48
 
49
  $features_plan_map[ $support_feature->id ]['plans'][ $plan->id ] = $support_feature;
43
  $support_feature->title = fs_text_inline( 'Support', $plugin->slug );
44
  $features_plan_map[ $support_feature->id ] = array( 'feature' => $support_feature, 'plans' => array() );
45
  } else {
46
+ $support_feature = $features_plan_map['support']['feature'];
47
  }
48
 
49
  $features_plan_map[ $support_feature->id ]['plans'][ $plan->id ] = $support_feature;
freemius/templates/powered-by.php CHANGED
@@ -33,12 +33,12 @@
33
 
34
  $fs = freemius( $VARS['module_id'] );
35
 
36
- wp_enqueue_script( 'jquery' );
37
- wp_enqueue_script( 'json2' );
38
- fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.min.js' );
39
- fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' );
40
- ?>
41
- <?php if ( ! $fs->is_whitelabeled() ) : ?>
42
  <div id="pframe"></div>
43
  <script type="text/javascript">
44
  (function ($) {
@@ -58,4 +58,4 @@
58
  });
59
  })(jQuery);
60
  </script>
61
- <?php endif ?>
33
 
34
  $fs = freemius( $VARS['module_id'] );
35
 
36
+ if ( ! $fs->is_whitelabeled() && ! $fs->apply_filters( 'hide_freemius_powered_by', false ) ) {
37
+ wp_enqueue_script( 'jquery' );
38
+ wp_enqueue_script( 'json2' );
39
+ fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.min.js' );
40
+ fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' );
41
+ ?>
42
  <div id="pframe"></div>
43
  <script type="text/javascript">
44
  (function ($) {
58
  });
59
  })(jQuery);
60
  </script>
61
+ <?php } ?>
freemius/templates/pricing.php CHANGED
@@ -88,7 +88,8 @@
88
  // Billing cycle.
89
  'billing_cycle' => fs_request_get( 'billing_cycle', WP_FS__PERIOD_ANNUALLY ),
90
  'is_network_admin' => fs_is_network_admin() ? 'true' : 'false',
91
- 'currency' => $fs->apply_filters( 'default_currency', 'usd' ),
 
92
  ) );
93
 
94
  $use_external_pricing = $fs->should_use_external_pricing();
@@ -122,21 +123,21 @@
122
  <div id="fs_pricing_wrapper" data-public-url="<?php echo trailingslashit( dirname( $pricing_js_url ) ) ?>"></div>
123
  <?php
124
  $pricing_config = array_merge( array(
125
- 'contact_url' => $fs->contact_url(),
126
- 'is_network_admin' => fs_is_network_admin(),
127
- 'is_production' => ( defined( 'WP_FS__IS_PRODUCTION_MODE' ) ? WP_FS__IS_PRODUCTION_MODE : null ),
128
- 'menu_slug' => $fs->get_menu_slug(),
129
- 'mode' => 'dashboard',
130
- 'fs_wp_endpoint_url' => WP_FS__ADDRESS,
131
- 'request_handler_url' => admin_url(
132
  'admin-ajax.php?' . http_build_query( array(
133
  'module_id' => $fs->get_id(),
134
  'action' => $fs->get_ajax_action( 'pricing_ajax_action' ),
135
  'security' => $fs->get_ajax_security( 'pricing_ajax_action' )
136
  ) )
137
  ),
138
- 'selector' => '#fs_pricing_wrapper',
139
- 'unique_affix' => $fs->get_unique_affix(),
 
140
  ), $query_params );
141
 
142
  wp_add_inline_script( 'freemius-pricing', 'Freemius.pricing.new( ' . json_encode( $pricing_config ) . ' )' );
@@ -206,4 +207,4 @@
206
  'module_slug' => $slug,
207
  'module_version' => $fs->get_plugin_version(),
208
  );
209
- fs_require_template( 'powered-by.php', $params );
88
  // Billing cycle.
89
  'billing_cycle' => fs_request_get( 'billing_cycle', WP_FS__PERIOD_ANNUALLY ),
90
  'is_network_admin' => fs_is_network_admin() ? 'true' : 'false',
91
+ 'currency' => $fs->apply_filters( 'default_currency', 'usd' ),
92
+ 'discounts_model' => $fs->apply_filters( 'pricing/discounts_model', 'absolute' ),
93
  ) );
94
 
95
  $use_external_pricing = $fs->should_use_external_pricing();
123
  <div id="fs_pricing_wrapper" data-public-url="<?php echo trailingslashit( dirname( $pricing_js_url ) ) ?>"></div>
124
  <?php
125
  $pricing_config = array_merge( array(
126
+ 'contact_url' => $fs->contact_url(),
127
+ 'is_production' => ( defined( 'WP_FS__IS_PRODUCTION_MODE' ) ? WP_FS__IS_PRODUCTION_MODE : null ),
128
+ 'menu_slug' => $fs->get_menu_slug(),
129
+ 'mode' => 'dashboard',
130
+ 'fs_wp_endpoint_url' => WP_FS__ADDRESS,
131
+ 'request_handler_url' => admin_url(
 
132
  'admin-ajax.php?' . http_build_query( array(
133
  'module_id' => $fs->get_id(),
134
  'action' => $fs->get_ajax_action( 'pricing_ajax_action' ),
135
  'security' => $fs->get_ajax_security( 'pricing_ajax_action' )
136
  ) )
137
  ),
138
+ 'selector' => '#fs_pricing_wrapper',
139
+ 'unique_affix' => $fs->get_unique_affix(),
140
+ 'show_annual_in_monthly' => $fs->apply_filters( 'pricing/show_annual_in_monthly', true ),
141
  ), $query_params );
142
 
143
  wp_add_inline_script( 'freemius-pricing', 'Freemius.pricing.new( ' . json_encode( $pricing_config ) . ' )' );
207
  'module_slug' => $slug,
208
  'module_version' => $fs->get_plugin_version(),
209
  );
210
+ fs_require_template( 'powered-by.php', $params );
freemius/templates/sticky-admin-notice-js.php CHANGED
@@ -29,8 +29,7 @@
29
  message_id: id
30
  };
31
 
32
- // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
33
- $.post( ajaxurl, data, function( response ) {
34
 
35
  });
36
 
29
  message_id: id
30
  };
31
 
32
+ $.post( <?php echo Freemius::ajax_url() ?>, data, function( response ) {
 
33
 
34
  });
35
 
freemius/templates/tabs-capture-js.php CHANGED
@@ -45,7 +45,7 @@
45
  var tabsHtml = $('.wrap .nav-tab-wrapper').clone().wrap('<div>').parent().html();
46
 
47
  $.ajax({
48
- url : ajaxurl + '?' + $.param({
49
  action : '<?php echo $fs->get_ajax_action( 'store_tabs' ) ?>',
50
  security : '<?php echo $fs->get_ajax_security( 'store_tabs' ) ?>',
51
  module_id: '<?php echo $fs->get_id() ?>'
45
  var tabsHtml = $('.wrap .nav-tab-wrapper').clone().wrap('<div>').parent().html();
46
 
47
  $.ajax({
48
+ url : <?php echo Freemius::ajax_url() ?> + '?' + $.param({
49
  action : '<?php echo $fs->get_ajax_action( 'store_tabs' ) ?>',
50
  security : '<?php echo $fs->get_ajax_security( 'store_tabs' ) ?>',
51
  module_id: '<?php echo $fs->get_id() ?>'
phpunit.xml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <phpunit
2
+ bootstrap="tests/bootstrap.php"
3
+ backupGlobals="false"
4
+ colors="true"
5
+ convertErrorsToExceptions="true"
6
+ convertNoticesToExceptions="true"
7
+ convertWarningsToExceptions="true"
8
+ >
9
+ <testsuites>
10
+ <testsuite>
11
+ <directory prefix="test-" suffix=".php">./tests/</directory>
12
+ </testsuite>
13
+ </testsuites>
14
+ </phpunit>
post-snippets.php CHANGED
@@ -11,7 +11,7 @@
11
  * Plugin Name: Post Snippets (free)
12
  * Plugin URI: https://www.postsnippets.com
13
  * Description: Create a library of reusable content and insert it into your posts and pages. Navigate to "Settings > Post Snippets" to get started.
14
- * Version: 4.0
15
  * Author: Postsnippets
16
  * Author URI: https://www.postsnippets.com
17
  * License: GPL-2.0+
@@ -27,7 +27,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
27
  function postsnippets_fs()
28
  {
29
  global $postsnippets_fs ;
30
-
31
  if ( !isset( $postsnippets_fs ) ) {
32
  // Include Freemius SDK.
33
  require_once dirname( __FILE__ ) . '/freemius/start.php';
@@ -51,12 +51,12 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
51
  'is_live' => true,
52
  ) );
53
  }
54
-
55
  return $postsnippets_fs;
56
  }
57
-
58
 
59
-
 
60
 
61
 
62
 
@@ -68,41 +68,41 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
68
  {
69
  return admin_url( 'admin.php?page=post-snippets' );
70
  }
71
-
72
-
73
  postsnippets_fs()->add_action( 'after_uninstall', 'postsnippets_fs_uninstall_cleanup' );
74
-
75
 
76
  function postsnippets_fs_uninstall_cleanup() {
77
  $settings = get_option("post_snippets");
78
  $complete_uninstall = isset($settings['complete_uninstall']) ? $settings['complete_uninstall'] : false;
79
-
80
  if ($complete_uninstall) {
81
-
82
  global $wpdb;
83
-
84
  $wild = '%';
85
  $find = "post_snippets";
86
  $option_search = $wild . $wpdb->esc_like( $find ) . $wild;
87
-
88
  $option_names = $wpdb->get_results($wpdb->prepare("SELECT option_name FROM {$wpdb->prefix}options WHERE option_name LIKE %s", $option_search), ARRAY_A );
89
-
90
  if( !empty($option_names) ){
91
  $option_names = array_column($option_names, 'option_name');
92
  foreach ($option_names as $option_name) {
93
-
94
  delete_option($option_name);
95
-
96
  // for site options in Multisite
97
  delete_site_option($option_name);
98
-
99
  }
100
  }
101
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}pspro_snippets");
102
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}pspro_groups");
103
  }
104
  }
105
-
106
  // postsnippets_fs()->add_filter( 'connect_url', 'postsnippets_fs_settings_url' );
107
  // postsnippets_fs()->add_filter( 'after_skip_url', 'postsnippets_fs_settings_url' );
108
  // postsnippets_fs()->add_filter( 'after_connect_url', 'postsnippets_fs_settings_url' );
@@ -119,7 +119,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
119
  }
120
  return false;
121
  }
122
-
123
  postsnippets_fs()->add_filter(
124
  'is_submenu_visible',
125
  'postsnippets_show_submenu',
@@ -130,7 +130,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
130
  define( 'PS_MAIN_FILE', basename( __FILE__ ) );
131
  }
132
  if ( !defined( 'PS_VERSION' ) ) {
133
- define( 'PS_VERSION', '4.0' );
134
  }
135
  if ( !defined( 'PS_MAIN_FILE_PATH' ) ) {
136
  define( 'PS_MAIN_FILE_PATH', __FILE__ );
@@ -178,7 +178,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
178
  }
179
  return self::$instance;
180
  }
181
-
182
  /**
183
  * Initializes the plugin.
184
  */
@@ -190,16 +190,16 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
190
  load_plugin_textdomain( 'post-snippets', false, dirname( plugin_basename( __FILE__ ) ) . '/lang/' );
191
  add_action( 'after_setup_theme', array( &$this, 'phpExecState' ) );
192
  // add_action( 'admin_enqueue_scripts', array($this, 'enqueue_hide_seek_search_library') );
193
-
194
  //requiring PS functions file
195
  require_once 'src/PS_functions.php';
196
-
197
  new \PostSnippets\Admin();
198
  new \PostSnippets\WPEditor();
199
  new \PostSnippets\Shortcode();
200
 
201
  if (postsnippets_fs()->can_use_premium_code() && file_exists(plugin_dir_path( self::FILE ).'/src/PostSnippets/PSRestAdmin.php')) {
202
-
203
  new \PostSnippets\PSRestAdmin();
204
 
205
  if ( 'yes' == get_option( self::PS_ENABLE_REST) && file_exists(plugin_dir_path( self::FILE ).'/src/PostSnippets/PSRestFunctions.php')) {
@@ -207,11 +207,11 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
207
  new \PostSnippets\PSRestFunctions();
208
 
209
  }
210
-
211
  }
212
-
213
  }
214
-
215
  /**
216
  * PSR-0 compliant autoloader to load classes as needed.
217
  *
@@ -228,19 +228,19 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
228
  $className = ltrim( $className, '\\' );
229
  $fileName = '';
230
  $namespace = '';
231
-
232
  if ( $lastNsPos = strrpos( $className, '\\' ) ) {
233
  $namespace = substr( $className, 0, $lastNsPos );
234
  $className = substr( $className, $lastNsPos + 1 );
235
  $fileName = str_replace( '\\', DIRECTORY_SEPARATOR, $namespace );
236
  $fileName .= DIRECTORY_SEPARATOR;
237
  }
238
-
239
  $fileName .= str_replace( '_', DIRECTORY_SEPARATOR, $className );
240
-
241
  require 'src' . DIRECTORY_SEPARATOR . $fileName . '.php';
242
  }
243
-
244
  // -------------------------------------------------------------------------
245
  // Helpers
246
  // -------------------------------------------------------------------------
@@ -264,13 +264,13 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
264
 
265
  global $wpdb;
266
  $table_name = $wpdb->prefix . \PostSnippets::TABLE_NAME;
267
-
268
  $wild = '%';
269
  $find = $name;
270
  $snippet_title_like = $wild . $wpdb->esc_like( $find ) . $wild;
271
-
272
  $particular_snippets = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name WHERE snippet_title LIKE %s", $snippet_title_like), ARRAY_A );
273
-
274
  if( empty($particular_snippets) ){
275
  return;
276
  }
@@ -305,7 +305,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
305
 
306
  return do_shortcode( $snippet_content );
307
  }
308
-
309
  // -------------------------------------------------------------------------
310
  // Environment Checks
311
  // -------------------------------------------------------------------------
@@ -315,24 +315,24 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
315
  private function testHost()
316
  {
317
  // Check if PHP is too old
318
-
319
  if ( version_compare( PHP_VERSION, self::MIN_PHP_VERSION, '<' ) ) {
320
  // Display notice
321
  add_action( 'admin_notices', array( &$this, 'phpVersionError' ) );
322
  return false;
323
  }
324
-
325
  // Check if WordPress is too old
326
  global $wp_version ;
327
-
328
  if ( version_compare( $wp_version, self::MIN_WP_VERSION, '<' ) ) {
329
  add_action( 'admin_notices', array( &$this, 'wpVersionError' ) );
330
  return false;
331
  }
332
-
333
  return true;
334
  }
335
-
336
  /**
337
  * Displays a warning when installed on an old PHP version.
338
  */
@@ -347,7 +347,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
347
  );
348
  echo '</strong></p></div>' ;
349
  }
350
-
351
  /**
352
  * Displays a warning when installed in an old WordPress version.
353
  */
@@ -357,7 +357,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
357
  printf( 'Error: %2$s requires WordPress version %1$s or greater.', self::MIN_WP_VERSION, $this->getPluginName() );
358
  echo '</strong></p></div>' ;
359
  }
360
-
361
  /**
362
  * Get the name of this plugin.
363
  *
@@ -368,7 +368,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
368
  $data = get_plugin_data( self::FILE );
369
  return $data['Name'];
370
  }
371
-
372
  // -------------------------------------------------------------------------
373
  // Deprecated methods
374
  // -------------------------------------------------------------------------
@@ -384,12 +384,12 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
384
  public function phpExecState()
385
  {
386
  $filter = apply_filters( 'post_snippets_php_execution_enabled', true );
387
-
388
  if ( $filter == false and !defined( 'POST_SNIPPETS_DISABLE_PHP' ) ) {
389
  _deprecated_function( 'post_snippets_php_execution_enabled', '2.3', 'define(\'POST_SNIPPETS_DISABLE_PHP\', true);' );
390
  define( 'POST_SNIPPETS_DISABLE_PHP', true );
391
  }
392
-
393
  }
394
 
395
  /*
@@ -408,9 +408,9 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
408
  /**Create DB Table, if not already */
409
  new \PostSnippets\DBTable();
410
 
411
-
412
  }
413
-
414
  /**
415
  * routines to run to check whether the plugin is activated .
416
  */
@@ -419,9 +419,9 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
419
  $current_plugin_verion = get_option( self::VERSION_KEY );
420
 
421
  if(!$current_plugin_verion){
422
-
423
  update_option( self::VERSION_KEY, PS_VERSION );
424
-
425
  /**Create DB Table, if not already */
426
  new \PostSnippets\DBTable();
427
  }
@@ -430,7 +430,7 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
430
 
431
 
432
 
433
-
434
  }
435
  add_action( 'plugins_loaded', array( 'PostSnippets', 'getInstance' ) );
436
  add_action( 'plugins_loaded', array( 'PostSnippets', 'post_snippet_pro_update_check' ) ); /**Check For Version and Update Accordingly */
@@ -442,6 +442,6 @@ if ( !function_exists( 'postsnippets_fs' ) ) {
442
 
443
  register_activation_hook( __FILE__, array( 'PostSnippets', 'post_snippet_pro_activated' ) );
444
 
445
-
446
 
447
  }
11
  * Plugin Name: Post Snippets (free)
12
  * Plugin URI: https://www.postsnippets.com
13
  * Description: Create a library of reusable content and insert it into your posts and pages. Navigate to "Settings > Post Snippets" to get started.
14
+ * Version: 4.0.1
15
  * Author: Postsnippets
16
  * Author URI: https://www.postsnippets.com
17
  * License: GPL-2.0+
27
  function postsnippets_fs()
28
  {
29
  global $postsnippets_fs ;
30
+
31
  if ( !isset( $postsnippets_fs ) ) {
32
  // Include Freemius SDK.
33
  require_once dirname( __FILE__ ) . '/freemius/start.php';
51
  'is_live' => true,
52
  ) );
53
  }
54
+
55
  return $postsnippets_fs;
56
  }
 
57
 
58
+
59
+
60
 
61
 
62
 
68
  {
69
  return admin_url( 'admin.php?page=post-snippets' );
70
  }
71
+
72
+
73
  postsnippets_fs()->add_action( 'after_uninstall', 'postsnippets_fs_uninstall_cleanup' );
74
+
75
 
76
  function postsnippets_fs_uninstall_cleanup() {
77
  $settings = get_option("post_snippets");
78
  $complete_uninstall = isset($settings['complete_uninstall']) ? $settings['complete_uninstall'] : false;
79
+
80
  if ($complete_uninstall) {
81
+
82
  global $wpdb;
83
+
84
  $wild = '%';
85
  $find = "post_snippets";
86
  $option_search = $wild . $wpdb->esc_like( $find ) . $wild;
87
+
88
  $option_names = $wpdb->get_results($wpdb->prepare("SELECT option_name FROM {$wpdb->prefix}options WHERE option_name LIKE %s", $option_search), ARRAY_A );
89
+
90
  if( !empty($option_names) ){
91
  $option_names = array_column($option_names, 'option_name');
92
  foreach ($option_names as $option_name) {
93
+
94
  delete_option($option_name);
95
+
96
  // for site options in Multisite
97
  delete_site_option($option_name);
98
+
99
  }
100
  }
101
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}pspro_snippets");
102
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}pspro_groups");
103
  }
104
  }
105
+
106
  // postsnippets_fs()->add_filter( 'connect_url', 'postsnippets_fs_settings_url' );
107
  // postsnippets_fs()->add_filter( 'after_skip_url', 'postsnippets_fs_settings_url' );
108
  // postsnippets_fs()->add_filter( 'after_connect_url', 'postsnippets_fs_settings_url' );
119
  }
120
  return false;
121
  }
122
+
123
  postsnippets_fs()->add_filter(
124
  'is_submenu_visible',
125
  'postsnippets_show_submenu',
130
  define( 'PS_MAIN_FILE', basename( __FILE__ ) );
131
  }
132
  if ( !defined( 'PS_VERSION' ) ) {
133
+ define( 'PS_VERSION', '4.0.1' );
134
  }
135
  if ( !defined( 'PS_MAIN_FILE_PATH' ) ) {
136
  define( 'PS_MAIN_FILE_PATH', __FILE__ );
178
  }
179
  return self::$instance;
180
  }
181
+
182
  /**
183
  * Initializes the plugin.
184
  */
190
  load_plugin_textdomain( 'post-snippets', false, dirname( plugin_basename( __FILE__ ) ) . '/lang/' );
191
  add_action( 'after_setup_theme', array( &$this, 'phpExecState' ) );
192
  // add_action( 'admin_enqueue_scripts', array($this, 'enqueue_hide_seek_search_library') );
193
+
194
  //requiring PS functions file
195
  require_once 'src/PS_functions.php';
196
+
197
  new \PostSnippets\Admin();
198
  new \PostSnippets\WPEditor();
199
  new \PostSnippets\Shortcode();
200
 
201
  if (postsnippets_fs()->can_use_premium_code() && file_exists(plugin_dir_path( self::FILE ).'/src/PostSnippets/PSRestAdmin.php')) {
202
+
203
  new \PostSnippets\PSRestAdmin();
204
 
205
  if ( 'yes' == get_option( self::PS_ENABLE_REST) && file_exists(plugin_dir_path( self::FILE ).'/src/PostSnippets/PSRestFunctions.php')) {
207
  new \PostSnippets\PSRestFunctions();
208
 
209
  }
210
+
211
  }
212
+
213
  }
214
+
215
  /**
216
  * PSR-0 compliant autoloader to load classes as needed.
217
  *
228
  $className = ltrim( $className, '\\' );
229
  $fileName = '';
230
  $namespace = '';
231
+
232
  if ( $lastNsPos = strrpos( $className, '\\' ) ) {
233
  $namespace = substr( $className, 0, $lastNsPos );
234
  $className = substr( $className, $lastNsPos + 1 );
235
  $fileName = str_replace( '\\', DIRECTORY_SEPARATOR, $namespace );
236
  $fileName .= DIRECTORY_SEPARATOR;
237
  }
238
+
239
  $fileName .= str_replace( '_', DIRECTORY_SEPARATOR, $className );
240
+
241
  require 'src' . DIRECTORY_SEPARATOR . $fileName . '.php';
242
  }
243
+
244
  // -------------------------------------------------------------------------
245
  // Helpers
246
  // -------------------------------------------------------------------------
264
 
265
  global $wpdb;
266
  $table_name = $wpdb->prefix . \PostSnippets::TABLE_NAME;
267
+
268
  $wild = '%';
269
  $find = $name;
270
  $snippet_title_like = $wild . $wpdb->esc_like( $find ) . $wild;
271
+
272
  $particular_snippets = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name WHERE snippet_title LIKE %s", $snippet_title_like), ARRAY_A );
273
+
274
  if( empty($particular_snippets) ){
275
  return;
276
  }
305
 
306
  return do_shortcode( $snippet_content );
307
  }
308
+
309
  // -------------------------------------------------------------------------
310
  // Environment Checks
311
  // -------------------------------------------------------------------------
315
  private function testHost()
316
  {
317
  // Check if PHP is too old
318
+
319
  if ( version_compare( PHP_VERSION, self::MIN_PHP_VERSION, '<' ) ) {
320
  // Display notice
321
  add_action( 'admin_notices', array( &$this, 'phpVersionError' ) );
322
  return false;
323
  }
324
+
325
  // Check if WordPress is too old
326
  global $wp_version ;
327
+
328
  if ( version_compare( $wp_version, self::MIN_WP_VERSION, '<' ) ) {
329
  add_action( 'admin_notices', array( &$this, 'wpVersionError' ) );
330
  return false;
331
  }
332
+
333
  return true;
334
  }
335
+
336
  /**
337
  * Displays a warning when installed on an old PHP version.
338
  */
347
  );
348
  echo '</strong></p></div>' ;
349
  }
350
+
351
  /**
352
  * Displays a warning when installed in an old WordPress version.
353
  */
357
  printf( 'Error: %2$s requires WordPress version %1$s or greater.', self::MIN_WP_VERSION, $this->getPluginName() );
358
  echo '</strong></p></div>' ;
359
  }
360
+
361
  /**
362
  * Get the name of this plugin.
363
  *
368
  $data = get_plugin_data( self::FILE );
369
  return $data['Name'];
370
  }
371
+
372
  // -------------------------------------------------------------------------
373
  // Deprecated methods
374
  // -------------------------------------------------------------------------
384
  public function phpExecState()
385
  {
386
  $filter = apply_filters( 'post_snippets_php_execution_enabled', true );
387
+
388
  if ( $filter == false and !defined( 'POST_SNIPPETS_DISABLE_PHP' ) ) {
389
  _deprecated_function( 'post_snippets_php_execution_enabled', '2.3', 'define(\'POST_SNIPPETS_DISABLE_PHP\', true);' );
390
  define( 'POST_SNIPPETS_DISABLE_PHP', true );
391
  }
392
+
393
  }
394
 
395
  /*
408
  /**Create DB Table, if not already */
409
  new \PostSnippets\DBTable();
410
 
411
+
412
  }
413
+
414
  /**
415
  * routines to run to check whether the plugin is activated .
416
  */
419
  $current_plugin_verion = get_option( self::VERSION_KEY );
420
 
421
  if(!$current_plugin_verion){
422
+
423
  update_option( self::VERSION_KEY, PS_VERSION );
424
+
425
  /**Create DB Table, if not already */
426
  new \PostSnippets\DBTable();
427
  }
430
 
431
 
432
 
433
+
434
  }
435
  add_action( 'plugins_loaded', array( 'PostSnippets', 'getInstance' ) );
436
  add_action( 'plugins_loaded', array( 'PostSnippets', 'post_snippet_pro_update_check' ) ); /**Check For Version and Update Accordingly */
442
 
443
  register_activation_hook( __FILE__, array( 'PostSnippets', 'post_snippet_pro_activated' ) );
444
 
445
+
446
 
447
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: custom snippet, custom shortcode, snippet, snippets, shortcode, shortcodes
4
  Requires at least: 3.3
5
  Tested up to: 6.1.1
6
  Requires PHP: 5.3
7
- Stable tag: 4.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -134,14 +134,24 @@ your clients to be able to use PHP code in a post snippet.
134
 
135
  == Screenshots ==
136
 
137
- 1. Settings > Post Snippets: the admin page where you create and manage snippets.
138
- 2. The convenient button for Post Snippets in the WordPress editor.
139
- 3. The Post Snippet insert dialog that shows all snippets.
140
- 4. Example of an inserted snippet, with optional variables.
141
- 5. Easy to access inline documentation (top right 'Help' button in WordPress).
 
 
 
 
 
 
142
 
143
  == Changelog ==
144
 
 
 
 
 
145
  = Version 4.0 - 24 Nov 2022 =
146
 
147
  * Added - New Backend UI.
4
  Requires at least: 3.3
5
  Tested up to: 6.1.1
6
  Requires PHP: 5.3
7
+ Stable tag: 4.0.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
134
 
135
  == Screenshots ==
136
 
137
+ 1. All Snippets.
138
+ 2. Shortcode snippet.
139
+ 3. Add snippet in classic editor.
140
+ 4. Post Snippet Gutenberg Block.
141
+ 5. Using Gutenberg Block.
142
+ 6. Php Editor.
143
+ 7. Writing Php snippet.
144
+ 8. Writting CSS.
145
+ 9. Writting JS.
146
+ 10. Setting Page.
147
+ 11. Import/Export Settings.
148
 
149
  == Changelog ==
150
 
151
+ = Version 4.0.1 - 14 Dec 2022 =
152
+
153
+ - Fixed - error when using script tag in snippets
154
+
155
  = Version 4.0 - 24 Nov 2022 =
156
 
157
  * Added - New Backend UI.
src/PostSnippets/Edit.php CHANGED
@@ -20,19 +20,19 @@ class Edit {
20
  function __construct() {
21
 
22
  self::prepare();
23
-
24
  $this->save_data();
25
-
26
  $this->perform_actions_response();
27
-
28
  $this->setup_code_editor();
29
-
30
  $this->edit_snippets_meta_boxes();
31
-
32
  $this->setup_sections();
33
-
34
  $this->editPage();
35
-
36
 
37
  }
38
 
@@ -41,19 +41,19 @@ class Edit {
41
  if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'edit' && isset($_REQUEST['snippet']) ){
42
 
43
  self::$snippet_id = intval($_REQUEST['snippet']);
44
-
45
  check_admin_referer( 'ps-snippets-hover-trigger_'.self::$snippet_id );
46
 
47
  global $wpdb;
48
 
49
  if( !$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM ".$wpdb->prefix.\PostSnippets::TABLE_NAME." WHERE ID = %d", self::$snippet_id) ) ){
50
-
51
  self::$snippet_id = '';
52
 
53
  wp_safe_redirect( admin_url('admin.php?page=post-snippets' ) );
54
-
55
  }
56
-
57
  if( !empty(self::$snippet_id) ){
58
  self::$editSnippetPage = true;
59
  }
@@ -63,7 +63,7 @@ class Edit {
63
 
64
  public static function change_edit_snippet_page_title($admin_title, $title){
65
 
66
-
67
  self::prepare();
68
 
69
  if(self::$editSnippetPage){
@@ -75,16 +75,16 @@ class Edit {
75
  }
76
 
77
  function editPage(){
78
-
79
-
80
  ?>
81
  <div class="wrap">
82
  <div id="icon-users" class="icon32"></div>
83
-
84
- <?php
85
-
86
  $edit_page = '';
87
-
88
  if( self::$editSnippetPage ){
89
  $page = self::get_snippet_page();
90
  if( $page == 'post-snippets-edit-js' ){
@@ -93,9 +93,9 @@ class Edit {
93
  elseif( $page == 'post-snippets-edit-css' ){
94
  $edit_page = "CSS ";
95
  }
96
- ?>
97
  <h1 class="wp-heading-inline"><?php echo $edit_page; esc_html_e( 'Edit', 'post-snippets' ); ?> Snippet</h1>
98
- <a href="<?php echo esc_url("?page=$page") ?>" class="page-title-action"><?php esc_html_e( 'Add New', 'post-snippets' ); ?></a>
99
  <?php
100
  }
101
  else{
@@ -103,26 +103,26 @@ class Edit {
103
  <h1 class="wp-heading-inline"><?php echo $edit_page; esc_html_e( 'Add', 'post-snippets' ); ?> Snippet</h1>
104
  <?php
105
  }
106
-
107
  ?>
108
 
109
  <hr class="wp-header-end">
110
  <form id="ps_edit_snippets" method="post">
111
- <?php
112
-
113
  wp_nonce_field( 'pspro_edit_snippet', 'pspro_edit_snippet_nonce', false );
114
-
115
  // Pro
116
 
117
  // wp_nonce_field('sync_up', 'sync_up');
118
  // wp_nonce_field('sync_down', 'sync_down');
119
-
120
  /* Used to save closed meta boxes and their order */
121
  wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
122
- wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
123
-
124
  ?>
125
-
126
  <div id="poststuff">
127
  <!-- #post-body .metabox-holder goes here -->
128
  <div id="post-body" class="metabox-holder columns-<?php echo 1 == get_current_screen()->get_columns() ? '1' : '2'; ?>">
@@ -131,49 +131,49 @@ class Edit {
131
  <!-- #post-body-content -->
132
  <?php $this->body_content_cb(); ?>
133
  </div>
134
-
135
  <div id="postbox-container-1" class="postbox-container">
136
- <?php
137
  /**Add Meta Box to Side Bar */
138
- do_meta_boxes('','side',null);
139
-
140
  ?>
141
 
142
- <div class ="save-button-area">
143
-
144
  <?php
145
-
146
- submit_button(__('Save','post-snippets'));
147
-
148
  ?>
149
-
150
  </div>
151
 
152
  </div>
153
-
154
  <div id="postbox-container-2" class="postbox-container">
155
  <?php
156
  /**Add Full witdh meta-boxes to bottom of Fixed content */
157
- do_meta_boxes('','normal',null);
158
- // do_meta_boxes('','advanced',null);
159
-
160
  ?>
161
  </div>
162
 
163
 
164
  </div>
165
  </div>
166
-
167
-
168
-
169
  </form>
170
-
171
  </div><!--/wrap -->
172
 
173
  <?php
174
 
175
  if( isset($_REQUEST['message']) && $_REQUEST['message'] == 1 ){
176
-
177
  printf('<div class="notice notice-success is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Added Successfully', 'post-snippets' ) );
178
 
179
  }
@@ -193,7 +193,7 @@ class Edit {
193
  /*
194
  * Actions to be taken prior to page loading. This is after headers have been set.
195
  * call on load-$hook
196
- * This calls the add_meta_boxes hooks, adds screen options and enqueues the postbox.js script.
197
  */
198
  public static function page_actions(){
199
 
@@ -201,9 +201,9 @@ class Edit {
201
  add_screen_option('layout_columns', array('max' => 2, 'default' => 2) );
202
 
203
  /* Enqueue WordPress' script for handling the metaboxes */
204
- wp_enqueue_script('postbox');
205
  }
206
-
207
  public static function edit_snippets_meta_boxes(){
208
 
209
  add_meta_box(
@@ -215,7 +215,7 @@ class Edit {
215
  );
216
 
217
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit-js' || $_REQUEST['page'] === 'post-snippets-edit-css'){
218
-
219
  add_meta_box(
220
  'ps_option_meta_box', //Meta box ID
221
  __('Options','post-snippets'), //Meta box Title
@@ -223,7 +223,7 @@ class Edit {
223
  NULL, // Screen to which to add the meta box, blank means current screen
224
  'side' // Context
225
  );
226
-
227
  add_meta_box(
228
  'ps_preview_meta_box', //Meta box ID
229
  __('Preview','post-snippets'), //Meta box Title
@@ -231,7 +231,7 @@ class Edit {
231
  NULL, // Screen to which to add the meta box, blank means current screen
232
  'normal' // Context
233
  );
234
-
235
  add_meta_box(
236
  'ps_pages_meta_box', //Meta box ID
237
  __('Apply only on these URLs','post-snippets'), //Meta box Title
@@ -241,8 +241,8 @@ class Edit {
241
  );
242
 
243
  }
244
-
245
-
246
  }
247
 
248
  function body_content_cb(){
@@ -295,10 +295,10 @@ class Edit {
295
  <span></span>
296
  </div>
297
 
298
-
299
-
300
  <div class="pspro_edit_duplicate_snippet" <?php echo esc_attr( $disabled_action ) ?>>
301
-
302
  <span class="dashicons dashicons-admin-page" style="width:30px; height:30px; font-size:25px;"></span>
303
  <button <?php echo esc_attr( $disabled_action_cursor ) ?> class="link_style_button" type="<?php echo (self::$editSnippetPage) ? esc_attr( "submit" ) : esc_attr( "button" ) ?>" name="duplicate" value><?php esc_html_e( "Duplicate Snippet", 'post-snippets' ) ?></button>
304
 
@@ -308,21 +308,21 @@ class Edit {
308
 
309
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit'){
310
  ?>
311
- <div class="pspro_edit_copy_shortcode" style="margin-top:1rem; text-align:center;"><?php
312
-
313
  if(self::$editSnippetPage){?>
314
 
315
  <div id="pspro_edit_shortcode_copied">
316
  <span id="pspro_edit_shortcode_copied_text"><?php esc_html_e( "Shortcode Copied", "post-snippets" ) ?></span>
317
  </div>
318
-
319
  <input type="text" name="" id="pspro_edit_shotcode_text" disabled value = "<?php echo (esc_attr( self::generate_shortcode_text() )) ?>" style="width:100%;<?php echo self::get_data('snippet_shortcode')?'':'display:none;'; ?>">
320
-
321
  <?php
322
  }
323
  ?>
324
 
325
- <button type = "button" class="button"
326
  <?php echo self::$editSnippetPage ? ( ( self::get_data('snippet_shortcode') == 0 )?( esc_attr( 'disabled' ) ):'') : ( esc_attr( 'disabled' ) ) ?>>
327
  <?php esc_html_e( "Copy Shortcode", 'post-snippets' ) ?>
328
  </button>
@@ -356,12 +356,12 @@ class Edit {
356
  <ul id="pspro_group-tabs" class="category-tabs">
357
  <li class="tabs"> <?php esc_html_e( 'All Groups', 'post-snippets' ) ?></li>
358
  </ul>
359
-
360
  <div id="pspro-group-all" class="tabs-panel">
361
-
362
  <ul id="pspro_group_checklist" data-wp-lists="list:pspro_group" class="categorychecklist form-no-clear">
363
  <?php
364
-
365
  $checked = ''; /** Preventing Further warning of undefind variable below */
366
 
367
  foreach ($all_groups as $group_key => $group_value) {
@@ -376,19 +376,19 @@ class Edit {
376
  }
377
 
378
  ?>
379
- <li id="pspro_group_<?php echo esc_attr( $group_value['ID'] )?>">
380
- <label class="selectit">
381
- <input value="<?php echo esc_attr( $group_value['ID'] )?>" type="checkbox" name="pspro_group_list[]" id="in_pspro_group_<?php echo esc_attr( $group_value['ID'] )?>"
382
  <?php echo ( esc_attr( $ungrouped_checked?? '' ) );
383
  echo ( esc_attr( $checked ) );
384
- ?>/>
385
- <?php
386
- echo esc_html( $group_value['group_name'] );
387
  ?>
388
  </label>
389
  </li>
390
  <?php
391
-
392
  }
393
 
394
  ?>
@@ -408,7 +408,7 @@ class Edit {
408
  if( empty($jsLocation) ){
409
  $jsLocation = 'header';
410
  }
411
-
412
  ?>
413
 
414
  <div id="pspro_edit_options_where">
@@ -417,10 +417,10 @@ class Edit {
417
 
418
  <input type="radio" name="pspro_edit_js_where" id="pspro_edit_js_header" class="pspro_edit_js_radio" value="header" <?php echo ( $jsLocation == 'header' ) ? ( esc_attr( 'checked' ) ) : '' ?> >
419
  <label for="pspro_edit_js_header" class="pspro_edit_js_label"><?php esc_html_e( "Header", "post-snippets" ) ?></label><br>
420
-
421
  <input type="radio" name="pspro_edit_js_where" id="pspro_edit_js_footer" class="pspro_edit_js_radio" value="footer" <?php echo ( $jsLocation == 'footer' ) ? ( esc_attr( 'checked' ) ) : '' ?> >
422
  <label for="pspro_edit_js_footer" class="pspro_edit_js_label"><?php esc_html_e( "Footer", "post-snippets" ) ?></label><br>
423
-
424
  </div>
425
 
426
 
@@ -434,7 +434,7 @@ class Edit {
434
  $location = 'admin';
435
  }
436
  $available_site_locations = Edit::availableSiteLocations();
437
-
438
  ?>
439
 
440
  <div id="pspro_edit_options_where_in_site">
@@ -447,32 +447,32 @@ class Edit {
447
  foreach ($available_site_locations as $available_site_location_slug => $available_site_location) {
448
  ?>
449
  <input
450
-
451
- type="radio" name="pspro_edit_where_site"
452
- id="pspro_edit_<?php echo esc_attr( $available_site_location_slug ) ?>"
453
- class="pspro_edit_js_radio"
454
- value="<?php echo esc_attr( $available_site_location_slug ) ?>"
455
  <?php echo ( $location == $available_site_location_slug ) ? ( esc_attr( 'checked' ) ) : '' ?>
456
  >
457
-
458
- <label
459
-
460
- for="pspro_edit_<?php echo esc_attr( $available_site_location_slug ) ?>"
461
  class="pspro_edit_js_label"><?php esc_html_e( $available_site_location, "post-snippets" )
462
-
463
  ?></label><br>
464
 
465
  <?php
466
  }
467
 
468
  ?>
469
-
470
  </div>
471
 
472
 
473
  <?php
474
-
475
-
476
  }
477
 
478
  }
@@ -505,7 +505,7 @@ class Edit {
505
  }
506
 
507
  static function pages_metabox_content(){
508
-
509
  $pages = self::getAllPagesAndPosts();
510
  $pageLocation = self::getLocation('page');
511
  $available_locations = Edit::availablePageLocations();
@@ -521,7 +521,7 @@ class Edit {
521
  $pageLocation = 'specific_page';
522
  $disabled = '';
523
  }
524
-
525
  ?>
526
  <div id="pspro_edit_select_location" class="pspro_edit_display_flex">
527
 
@@ -532,7 +532,7 @@ class Edit {
532
  foreach ($available_locations as $available_location_slug => $available_location) {
533
  ?>
534
  <option value="<?php echo esc_attr( $available_location_slug ) ?>" <?php echo ( $available_location_slug == $pageLocation ? 'selected' : '') ?>
535
-
536
  ><?php esc_html_e( $available_location, 'post-snippets' ) ?></option>
537
 
538
  <?php
@@ -549,7 +549,7 @@ class Edit {
549
  ?>
550
  <option value="<?php echo esc_attr( $page->ID ) ?>" data-url = "<?php echo esc_attr( get_permalink($page->ID) ) ?>"
551
  <?php echo ( $selected_page == $page->ID ? 'selected' : '' ) ?>
552
-
553
  ><?php echo esc_attr( ( !empty($page->post_title) ) ? $page->post_title : $page->post_name ) ?></option>
554
 
555
  <?php
@@ -559,7 +559,7 @@ class Edit {
559
  </select>
560
 
561
  <button type="button" id="pspro_edit_add_selection" class="button button-primary" disabled><?php esc_html_e( 'Add', 'post-snippets' ) ?></button>
562
-
563
  </div>
564
 
565
 
@@ -574,13 +574,13 @@ class Edit {
574
  }
575
 
576
  $location = Shortcode::filterVars( self::get_data('snippet_vars') );
577
-
578
  if( $type === 'js' ){
579
 
580
  if( !empty($location) ){
581
-
582
  $location = array_keys($location);
583
-
584
  if( !empty( $location[1] ) ){
585
  $location = $location[1];
586
  if($location !== 'header' && $location !== 'footer'){
@@ -600,9 +600,9 @@ class Edit {
600
  elseif( $type === 'site' ){
601
 
602
  if( !empty($location) ){
603
-
604
  $location = array_keys($location);
605
-
606
  if( !empty( $location[2] ) ){
607
  $location = $location[2];
608
  $available_site_locations = array_keys( Edit::availableSiteLocations() );
@@ -623,9 +623,9 @@ class Edit {
623
  elseif( $type === 'page' ){
624
 
625
  if( !empty($location) ){
626
-
627
  $location_keys = array_keys($location);
628
-
629
  if( !empty( $location_keys[0] ) ){
630
 
631
  $location_type = $location_keys[0];
@@ -701,11 +701,11 @@ class Edit {
701
 
702
  }
703
 
704
-
705
  function snippetTitle(){
706
 
707
  ?>
708
-
709
  <div id="titlediv">
710
  <div id="titlewrap">
711
  <?php
@@ -714,11 +714,11 @@ class Edit {
714
  ?>
715
  <label class="screen-reader-text" id="title-prompt-text" for="title"><?php echo esc_html($title_placeholder) ; ?></label>
716
  <input type="text" name="snippet_title" size="30" value="<?php echo esc_attr( $snippet_title ); ?>" id="title" spellcheck="true" autocomplete="off" placeholder = "<?php echo esc_html($title_placeholder); ?>"/>
717
-
718
  </div>
719
-
720
  </div><!-- /titlediv -->
721
-
722
  <?php
723
 
724
  }
@@ -752,35 +752,35 @@ class Edit {
752
  // 'markerGutter' => true,
753
  'lint' => true
754
  // 'htmllint' => true
755
-
756
  )
757
  ));
758
 
759
  wp_localize_script('post-snippets', 'cm_settings', $cm_settings);
760
-
761
  wp_enqueue_script('wp-theme-plugin-editor');
762
  wp_enqueue_style('wp-codemirror');
763
  wp_enqueue_style( 'post-snippets-edit', plugin_dir_url( PS_MAIN_FILE_PATH ) . "assets/edit.css");
764
- wp_enqueue_script( 'post-snippets-edit-menu', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/edit.js');
765
 
766
  return;
767
  }
768
 
769
  wp_enqueue_style( 'post-snippets-editor', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/editor.css' );
770
  wp_enqueue_script( 'post-snippets-editor', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/editor.js' );
771
-
772
  wp_enqueue_style( 'post-snippets-edit', plugin_dir_url( PS_MAIN_FILE_PATH ) . "assets/edit.css");
773
- wp_enqueue_script( 'post-snippets-edit-menu', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/edit.js');
774
- }
775
 
776
  function codeEditor(){
777
-
778
  $snippet_content = self::get_data('snippet_content');
779
 
780
  ?>
781
  <style>
782
  #ps_snippet_code_editor textarea{
783
-
784
  width: 100%;
785
  min-height: 60vh;
786
  height: calc(100vh - 295px);
@@ -797,7 +797,7 @@ class Edit {
797
  }
798
 
799
  .pspro_codeEditor_heading{
800
-
801
  margin-top: 1.5rem !important;
802
  margin-bottom: 1rem !important;
803
 
@@ -809,7 +809,7 @@ class Edit {
809
  <input type="hidden" name="pspro-edit-code-editor-page" value="<?php echo esc_attr( self::get_snippet_page() ) ?>">
810
  <div id="ps_snippet_code_editor">
811
  <h1 class="pspro_codeEditor_heading wp-heading"> <?php esc_html_e( 'Code Editor:', 'post-snippets' ) ?> </h1>
812
- <textarea name="snippet_content" id="ps_code_editor"><?php echo esc_html( stripslashes( $snippet_content ) ) ?></textarea>
813
  </div>
814
  <?php
815
 
@@ -817,10 +817,10 @@ class Edit {
817
 
818
  public static function setup_sections(){
819
 
820
-
821
- add_settings_section( 'pspro_edit_snippet',
 
822
  '',
823
- '',
824
  'post-snippets-edit' );
825
 
826
  add_settings_field( 'snippet_vars', //unique_name
@@ -833,9 +833,9 @@ class Edit {
833
  'id' => 'snippet_vars',
834
  'label_for' => 'pspro_snippet_vars',
835
  'description' => __( 'Enter Variables For This Snippet', 'post-snippets' )
836
- )
837
  ); //value to pass to callback function
838
-
839
  add_settings_field( 'snippet_shortcode',
840
  __('Shortcode:','post-snippets'),
841
  array( \PostSnippets::EDIT_CLASS, 'add_section_fields' ),
@@ -848,7 +848,7 @@ class Edit {
848
  'description' => __( 'Enable Shortcode For This Snippet', 'post-snippets' )
849
  )
850
  );
851
-
852
  add_settings_field( 'snippet_php',
853
  __('PHP Code:','post-snippets'),
854
  array( \PostSnippets::EDIT_CLASS, 'add_section_fields' ),
@@ -861,8 +861,8 @@ class Edit {
861
  'description' => __( 'Enable PHP Rendering For This Snippet', 'post-snippets' )
862
  )
863
  );
864
-
865
-
866
  add_settings_field( 'snippet_wptexturize',
867
  __('WPtexturize:','post-snippets'),
868
  array( \PostSnippets::EDIT_CLASS, 'add_section_fields' ),
@@ -876,41 +876,63 @@ class Edit {
876
  )
877
  );
878
  }
879
-
880
  public static function add_section_fields( $arguments ) {
881
 
882
  if($arguments['id'] == 'snippet_vars'){
883
-
884
  $snippet_vars = self::get_data($arguments['id']);
885
 
886
  ?>
887
-
888
- <textarea
889
- name="<?php echo esc_attr( $arguments['id'] ) ?>"
890
- id="pspro_<?php echo esc_attr( $arguments['id'] ) ?>"
891
  cols="55" rows="4"
892
  ><?php echo esc_html( $snippet_vars ) ?></textarea>
893
-
894
  <?php
895
 
896
  return;
897
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
898
 
899
  $is_checked = self::get_data($arguments['id']);
900
-
901
  ?>
902
 
903
- <input type="checkbox"
904
- name="<?php echo esc_attr( $arguments['id'] ) ?>"
905
  id="pspro_<?php echo esc_attr( $arguments['id'] ) ?>"
906
  value = "1"
907
  <?php echo ( ($is_checked)?esc_attr( 'checked' ):'' ) ?>
908
- />
909
 
 
910
 
911
  <?php
912
-
913
-
 
914
  }
915
 
916
 
@@ -920,18 +942,18 @@ class Edit {
920
 
921
  ?>
922
  <h1 class="ps_description_heading wp-heading" style="margin-top:1.5rem; margin-bottom:1.5rem"> <?php esc_html_e( 'Description:', 'post-snippets' ) ?> </h1>
923
-
924
  <?php
925
 
926
  wp_editor(stripslashes($snippet_desc), 'ps_snippet_description', array(
927
-
928
  'textarea_name' => 'snippet_desc',
929
-
930
  // 'wpautop' => true,
931
  // 'media_buttons' => false,
932
  // 'editor_class' => 'my_custom_class',
933
  // 'textarea_rows' => 10
934
-
935
  ) );
936
 
937
 
@@ -942,12 +964,12 @@ class Edit {
942
  $column = sanitize_key( $column );
943
 
944
  if(self::$editSnippetPage){
945
-
946
  global $wpdb;
947
 
948
  $data = $wpdb->get_var($wpdb->prepare("SELECT $column FROM ".$wpdb->prefix.\PostSnippets::TABLE_NAME." WHERE ID = %d", self::$snippet_id));
949
  }
950
-
951
  else{
952
 
953
  if($column == "snippet_title"){
@@ -958,7 +980,7 @@ class Edit {
958
  }
959
 
960
  $data = $_REQUEST[$column]?? '';
961
-
962
  }
963
 
964
  return $data;
@@ -973,16 +995,16 @@ class Edit {
973
  * @param String $method - Whether to Increment or Decrement
974
  *
975
  * @return Void
976
- *
977
  */
978
  public static function change_group_count($group_list, $method){
979
 
980
  global $wpdb;
981
 
982
  if( !strcmp($method, 'increment') ){
983
-
984
  foreach ($group_list as $key => $group_id) {
985
-
986
  $group_count = $wpdb->get_var( $wpdb->prepare("SELECT group_count FROM ".$wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME." WHERE ID = %d", $group_id) );
987
  $group_count = $group_count + 1;
988
 
@@ -1006,13 +1028,13 @@ class Edit {
1006
  }
1007
 
1008
  elseif( !strcmp($method, 'decrement') ){
1009
-
1010
  foreach ($group_list as $key => $group_id) {
1011
-
1012
  $group_count = $wpdb->get_var( $wpdb->prepare("SELECT group_count FROM ".$wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME." WHERE ID = %d", $group_id) );
1013
-
1014
  if($group_count == 0){ /**if there is no snippet in a group but a snippet is deleted, this condition would never met but just in case*/
1015
-
1016
  continue;
1017
  }
1018
 
@@ -1041,7 +1063,7 @@ class Edit {
1041
 
1042
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit-js' /*&& isset($_REQUEST['pspro_edit_location_select'])*/ ){
1043
 
1044
- return true;
1045
  }
1046
  else{
1047
  return false;
@@ -1052,7 +1074,7 @@ class Edit {
1052
 
1053
  if( $this->is_JS() && isset( $_REQUEST['pspro_edit_js_where'] ) && ( $_REQUEST['pspro_edit_js_where'] === 'header' || $_REQUEST['pspro_edit_js_where'] === 'footer' ) ){
1054
 
1055
- return $_REQUEST['pspro_edit_js_where'];
1056
  }
1057
  elseif( $this->is_JS() ){
1058
  return 'header';
@@ -1070,7 +1092,7 @@ class Edit {
1070
 
1071
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit-css' /*&& isset($_REQUEST['pspro_edit_location_select'])*/ ){
1072
 
1073
- return true;
1074
  }
1075
  else{
1076
  return false;
@@ -1078,7 +1100,7 @@ class Edit {
1078
  }
1079
 
1080
  function codeLocation(){
1081
-
1082
  if( ( $this->is_JS() || $this->is_CSS() ) && isset( $_REQUEST['pspro_edit_location_select'] ) && $this->siteLocation() != 'admin'){
1083
 
1084
  $selected_location = $_REQUEST['pspro_edit_location_select'];
@@ -1088,10 +1110,10 @@ class Edit {
1088
  if( $selected_location === 'specific_page'){
1089
 
1090
  if( isset( $_REQUEST['pspro_edit_page_select'] ) && is_numeric( $_REQUEST['pspro_edit_page_select'] ) ){
1091
-
1092
  $specific_page_exist = Edit::isPageOrPost( $_REQUEST['pspro_edit_page_select'] );
1093
 
1094
- if( $specific_page_exist ){
1095
  return 'specific_page='.$_REQUEST['pspro_edit_page_select'];
1096
  }
1097
  else{
@@ -1102,13 +1124,13 @@ class Edit {
1102
  return '';
1103
 
1104
  }
1105
-
1106
  elseif( in_array($selected_location, $available_locations_slugs, true) ){
1107
 
1108
  return $selected_location;
1109
  }
1110
 
1111
- return '';
1112
  }
1113
  elseif( $this->siteLocation() == 'admin' ){
1114
  return 'all_website';
@@ -1131,7 +1153,7 @@ class Edit {
1131
  return 'admin';
1132
  }
1133
  return '';
1134
- }
1135
 
1136
  static function availablePageLocations(){
1137
 
@@ -1145,7 +1167,7 @@ class Edit {
1145
  }
1146
 
1147
  static function availableSiteLocations(){
1148
-
1149
  return array(
1150
  'frontend' => 'In Front-End',
1151
  'admin' => 'In Admin',
@@ -1158,20 +1180,20 @@ class Edit {
1158
  if( isset($_REQUEST['submit']) ){
1159
 
1160
  check_admin_referer( 'pspro_edit_snippet', 'pspro_edit_snippet_nonce' );
1161
-
1162
  global $wpdb;
1163
  $table_name = $wpdb->prefix.\PostSnippets::TABLE_NAME;
1164
-
1165
-
1166
-
1167
  //******************** Preparing Group List ************************* */
1168
 
1169
  $upgrouped_id = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM ".$wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME." WHERE group_slug = %s", __('ungrouped','post-snippets') ) );
1170
  $group_list = $_REQUEST['pspro_group_list'] ?? array($upgrouped_id); /**If no group is selected */
1171
-
1172
  //*************************** END ****************************** */
1173
 
1174
-
1175
 
1176
  //******************** Preparing Snippet Title ************************* */
1177
 
@@ -1179,19 +1201,19 @@ class Edit {
1179
 
1180
 
1181
  //****************************** END *********************************** */
1182
-
1183
-
1184
  //******************** Preparing Snippet vars ************************* */
1185
 
1186
  if( isset($_REQUEST['snippet_vars']) && !empty( $_REQUEST['snippet_vars'] ) ){
1187
 
1188
  $snippet_vars = self::filter_snippet_vars( $_REQUEST['snippet_vars'] );
1189
-
1190
  }
1191
  elseif( isset($_REQUEST['snippet_vars']) && empty( $_REQUEST['snippet_vars'] ) ){
1192
 
1193
  $snippet_vars = '';
1194
-
1195
  }
1196
  elseif( !isset($_REQUEST['snippet_vars']) && ( $this->is_JS() || $this->is_CSS() ) ){
1197
 
@@ -1212,8 +1234,8 @@ class Edit {
1212
 
1213
  //****************************** END *********************************** */
1214
 
1215
-
1216
-
1217
  //******************** Preparing Snippet Content ************************* */
1218
 
1219
  $snippet_content = $_REQUEST['snippet_content'];
@@ -1222,7 +1244,7 @@ class Edit {
1222
 
1223
  // $start = '<script type=\"text/javascript\">';
1224
  // $end = '</script>';
1225
-
1226
  // $snippet_content = $start.'\n'.$snippet_content.'\n'.$end;
1227
 
1228
  // }
@@ -1236,17 +1258,17 @@ class Edit {
1236
  $snippet_desc = sanitize_textarea_field( $_REQUEST['snippet_desc'] ?? '' );
1237
 
1238
  //****************************** END *********************************** */
1239
-
1240
-
1241
-
1242
  //******************** Preparing Snippet PHP ************************* */
1243
 
1244
  if( isset($_REQUEST['snippet_php']) ){
1245
  $snippet_php = ( ($_REQUEST['snippet_php']?? 0 ) == 1) ? 1 : 0;
1246
  }
1247
  else{
1248
- /**As this variable is not used in JS and CSS pages, they
1249
- * can be used to save those pages. so new DB entry
1250
  * doesn't need to be created.
1251
  * 2 for JS and 3 for CSS.
1252
  */
@@ -1264,10 +1286,10 @@ class Edit {
1264
  }
1265
 
1266
  //****************************** END *********************************** */
1267
-
1268
-
1269
  if( !empty($snippet_title) ){
1270
-
1271
  if(self::$editSnippetPage){ /**This is a edit page of Previous Snippet */
1272
 
1273
  $current_snippet_title = $wpdb->get_var( $wpdb->prepare("SELECT snippet_title FROM $table_name WHERE ID = %d", self::$snippet_id) );
@@ -1313,13 +1335,13 @@ class Edit {
1313
  )
1314
  );
1315
 
1316
-
1317
  if($snippet_updated){
1318
 
1319
  printf('<div class="notice notice-success is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Updated', 'post-snippets' ) );
1320
  // return;
1321
  }
1322
-
1323
  elseif(!$snippet_updated){
1324
 
1325
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Update Failed', 'post-snippets' ) );
@@ -1330,13 +1352,13 @@ class Edit {
1330
 
1331
  //change group count here
1332
  if( !empty($increment_count) ){ /**Increment Count of these groups */
1333
-
1334
  self::change_group_count($increment_count, 'increment');
1335
 
1336
  }
1337
 
1338
  if( !empty($decrement_count) ){ /**Decrement Count of these groups */
1339
-
1340
  self::change_group_count($decrement_count, 'decrement');
1341
 
1342
  }
@@ -1346,8 +1368,8 @@ class Edit {
1346
  }
1347
 
1348
  elseif( !self::$editSnippetPage && !$this->duplicate_title_check($snippet_title) ){ /**Adding New Snippet here */
1349
-
1350
- $snippet_added = $wpdb->insert(
1351
  $table_name,
1352
  array(
1353
  'snippet_group' => maybe_serialize( $group_list ),
@@ -1360,7 +1382,7 @@ class Edit {
1360
  'snippet_shortcode' => ( ($_REQUEST['snippet_shortcode']?? 0 ) == 1) ? 1 : 0,
1361
  'snippet_php' => $snippet_php,
1362
  'snippet_wptexturize' => ( ($_REQUEST['snippet_wptexturize']?? 0 ) == 1) ? 1 : 0,
1363
- )
1364
  );
1365
 
1366
  if( $snippet_added ){
@@ -1373,18 +1395,18 @@ class Edit {
1373
  ?> <script> <?php echo("location.href = '".$url."';"); ?> </script> <?php
1374
 
1375
  }
1376
-
1377
  elseif(!$snippet_added){
1378
 
1379
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Not Added', 'post-snippets' ) );
1380
-
1381
  }
1382
  }
1383
 
1384
  else{
1385
 
1386
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'Duplicate Titles are Not Allowed', 'post-snippets' ) );
1387
-
1388
  }
1389
  }
1390
 
@@ -1403,7 +1425,7 @@ class Edit {
1403
  if( isset($_REQUEST['page']) && ( $_REQUEST['page'] === 'post-snippets-edit' || $_REQUEST['page'] === 'post-snippets-edit-js' || $_REQUEST['page'] === 'post-snippets-edit-css' ) ){
1404
  return $_REQUEST['page'];
1405
  }
1406
-
1407
  return 'post-snippets-edit';
1408
  }else{
1409
 
@@ -1435,11 +1457,11 @@ class Edit {
1435
 
1436
  $duplicate_title_exist = in_array($snippet_title, $all_snippets_titles);
1437
 
1438
- if( $duplicate_title_exist ){
1439
- /**Cause This function is also used to Increment
1440
  * Title Count by number_duplicate_snippet_title,
1441
  * otherwise, above written code would have to be
1442
- * repeated.
1443
  **/
1444
 
1445
  return $all_snippets_titles;
@@ -1510,7 +1532,7 @@ class Edit {
1510
  if(self::$editSnippetPage){
1511
 
1512
  $snippet_vars = Shortcode::filterVars( self::get_data('snippet_vars') );
1513
-
1514
  $snippet_vars_text = '';
1515
  foreach ($snippet_vars as $var => $value) {
1516
  $snippet_vars_text .= ' '. $var . (!empty($value)?'=':'') . $value;
@@ -1539,7 +1561,7 @@ class Edit {
1539
 
1540
  if($download_succeed){
1541
  printf('<div class="notice notice-success is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippets Downloaded', 'post-snippets' ) );
1542
-
1543
  }
1544
  else{
1545
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'There has been an Error', 'post-snippets' ) );
@@ -1552,7 +1574,7 @@ class Edit {
1552
 
1553
  check_admin_referer( 'pspro_edit_snippet', 'pspro_edit_snippet_nonce' );
1554
 
1555
-
1556
  }
1557
 
1558
 
@@ -1567,7 +1589,7 @@ class Edit {
1567
  else{
1568
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'There has been an Error', 'post-snippets' ) );
1569
  }
1570
-
1571
  }
1572
 
1573
 
@@ -1599,7 +1621,7 @@ class Edit {
1599
  'snippet_shortcode' => (($imported_snippet['snippet_shortcode']?? 0) == 1) ? : 0,
1600
  'snippet_php' => $snippet_php,
1601
  'snippet_wptexturize' => (($imported_snippet['snippet_wptexturize']?? 0) == 1) ? : 0,
1602
- )
1603
  );
1604
 
1605
  if($snippet_added){
20
  function __construct() {
21
 
22
  self::prepare();
23
+
24
  $this->save_data();
25
+
26
  $this->perform_actions_response();
27
+
28
  $this->setup_code_editor();
29
+
30
  $this->edit_snippets_meta_boxes();
31
+
32
  $this->setup_sections();
33
+
34
  $this->editPage();
35
+
36
 
37
  }
38
 
41
  if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'edit' && isset($_REQUEST['snippet']) ){
42
 
43
  self::$snippet_id = intval($_REQUEST['snippet']);
44
+
45
  check_admin_referer( 'ps-snippets-hover-trigger_'.self::$snippet_id );
46
 
47
  global $wpdb;
48
 
49
  if( !$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM ".$wpdb->prefix.\PostSnippets::TABLE_NAME." WHERE ID = %d", self::$snippet_id) ) ){
50
+
51
  self::$snippet_id = '';
52
 
53
  wp_safe_redirect( admin_url('admin.php?page=post-snippets' ) );
54
+
55
  }
56
+
57
  if( !empty(self::$snippet_id) ){
58
  self::$editSnippetPage = true;
59
  }
63
 
64
  public static function change_edit_snippet_page_title($admin_title, $title){
65
 
66
+
67
  self::prepare();
68
 
69
  if(self::$editSnippetPage){
75
  }
76
 
77
  function editPage(){
78
+
79
+
80
  ?>
81
  <div class="wrap">
82
  <div id="icon-users" class="icon32"></div>
83
+
84
+ <?php
85
+
86
  $edit_page = '';
87
+
88
  if( self::$editSnippetPage ){
89
  $page = self::get_snippet_page();
90
  if( $page == 'post-snippets-edit-js' ){
93
  elseif( $page == 'post-snippets-edit-css' ){
94
  $edit_page = "CSS ";
95
  }
96
+ ?>
97
  <h1 class="wp-heading-inline"><?php echo $edit_page; esc_html_e( 'Edit', 'post-snippets' ); ?> Snippet</h1>
98
+ <a href="<?php echo esc_url("?page=$page") ?>" class="page-title-action"><?php esc_html_e( 'Add New', 'post-snippets' ); ?></a>
99
  <?php
100
  }
101
  else{
103
  <h1 class="wp-heading-inline"><?php echo $edit_page; esc_html_e( 'Add', 'post-snippets' ); ?> Snippet</h1>
104
  <?php
105
  }
106
+
107
  ?>
108
 
109
  <hr class="wp-header-end">
110
  <form id="ps_edit_snippets" method="post">
111
+ <?php
112
+
113
  wp_nonce_field( 'pspro_edit_snippet', 'pspro_edit_snippet_nonce', false );
114
+
115
  // Pro
116
 
117
  // wp_nonce_field('sync_up', 'sync_up');
118
  // wp_nonce_field('sync_down', 'sync_down');
119
+
120
  /* Used to save closed meta boxes and their order */
121
  wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
122
+ wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
123
+
124
  ?>
125
+
126
  <div id="poststuff">
127
  <!-- #post-body .metabox-holder goes here -->
128
  <div id="post-body" class="metabox-holder columns-<?php echo 1 == get_current_screen()->get_columns() ? '1' : '2'; ?>">
131
  <!-- #post-body-content -->
132
  <?php $this->body_content_cb(); ?>
133
  </div>
134
+
135
  <div id="postbox-container-1" class="postbox-container">
136
+ <?php
137
  /**Add Meta Box to Side Bar */
138
+ do_meta_boxes('','side',null);
139
+
140
  ?>
141
 
142
+ <div class ="save-button-area">
143
+
144
  <?php
145
+
146
+ submit_button(__('Save','post-snippets'));
147
+
148
  ?>
149
+
150
  </div>
151
 
152
  </div>
153
+
154
  <div id="postbox-container-2" class="postbox-container">
155
  <?php
156
  /**Add Full witdh meta-boxes to bottom of Fixed content */
157
+ do_meta_boxes('','normal',null);
158
+ // do_meta_boxes('','advanced',null);
159
+
160
  ?>
161
  </div>
162
 
163
 
164
  </div>
165
  </div>
166
+
167
+
168
+
169
  </form>
170
+
171
  </div><!--/wrap -->
172
 
173
  <?php
174
 
175
  if( isset($_REQUEST['message']) && $_REQUEST['message'] == 1 ){
176
+
177
  printf('<div class="notice notice-success is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Added Successfully', 'post-snippets' ) );
178
 
179
  }
193
  /*
194
  * Actions to be taken prior to page loading. This is after headers have been set.
195
  * call on load-$hook
196
+ * This calls the add_meta_boxes hooks, adds screen options and enqueues the postbox.js script.
197
  */
198
  public static function page_actions(){
199
 
201
  add_screen_option('layout_columns', array('max' => 2, 'default' => 2) );
202
 
203
  /* Enqueue WordPress' script for handling the metaboxes */
204
+ wp_enqueue_script('postbox');
205
  }
206
+
207
  public static function edit_snippets_meta_boxes(){
208
 
209
  add_meta_box(
215
  );
216
 
217
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit-js' || $_REQUEST['page'] === 'post-snippets-edit-css'){
218
+
219
  add_meta_box(
220
  'ps_option_meta_box', //Meta box ID
221
  __('Options','post-snippets'), //Meta box Title
223
  NULL, // Screen to which to add the meta box, blank means current screen
224
  'side' // Context
225
  );
226
+
227
  add_meta_box(
228
  'ps_preview_meta_box', //Meta box ID
229
  __('Preview','post-snippets'), //Meta box Title
231
  NULL, // Screen to which to add the meta box, blank means current screen
232
  'normal' // Context
233
  );
234
+
235
  add_meta_box(
236
  'ps_pages_meta_box', //Meta box ID
237
  __('Apply only on these URLs','post-snippets'), //Meta box Title
241
  );
242
 
243
  }
244
+
245
+
246
  }
247
 
248
  function body_content_cb(){
295
  <span></span>
296
  </div>
297
 
298
+
299
+
300
  <div class="pspro_edit_duplicate_snippet" <?php echo esc_attr( $disabled_action ) ?>>
301
+
302
  <span class="dashicons dashicons-admin-page" style="width:30px; height:30px; font-size:25px;"></span>
303
  <button <?php echo esc_attr( $disabled_action_cursor ) ?> class="link_style_button" type="<?php echo (self::$editSnippetPage) ? esc_attr( "submit" ) : esc_attr( "button" ) ?>" name="duplicate" value><?php esc_html_e( "Duplicate Snippet", 'post-snippets' ) ?></button>
304
 
308
 
309
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit'){
310
  ?>
311
+ <div class="pspro_edit_copy_shortcode" style="margin-top:1rem; text-align:center;"><?php
312
+
313
  if(self::$editSnippetPage){?>
314
 
315
  <div id="pspro_edit_shortcode_copied">
316
  <span id="pspro_edit_shortcode_copied_text"><?php esc_html_e( "Shortcode Copied", "post-snippets" ) ?></span>
317
  </div>
318
+
319
  <input type="text" name="" id="pspro_edit_shotcode_text" disabled value = "<?php echo (esc_attr( self::generate_shortcode_text() )) ?>" style="width:100%;<?php echo self::get_data('snippet_shortcode')?'':'display:none;'; ?>">
320
+
321
  <?php
322
  }
323
  ?>
324
 
325
+ <button type = "button" class="button"
326
  <?php echo self::$editSnippetPage ? ( ( self::get_data('snippet_shortcode') == 0 )?( esc_attr( 'disabled' ) ):'') : ( esc_attr( 'disabled' ) ) ?>>
327
  <?php esc_html_e( "Copy Shortcode", 'post-snippets' ) ?>
328
  </button>
356
  <ul id="pspro_group-tabs" class="category-tabs">
357
  <li class="tabs"> <?php esc_html_e( 'All Groups', 'post-snippets' ) ?></li>
358
  </ul>
359
+
360
  <div id="pspro-group-all" class="tabs-panel">
361
+
362
  <ul id="pspro_group_checklist" data-wp-lists="list:pspro_group" class="categorychecklist form-no-clear">
363
  <?php
364
+
365
  $checked = ''; /** Preventing Further warning of undefind variable below */
366
 
367
  foreach ($all_groups as $group_key => $group_value) {
376
  }
377
 
378
  ?>
379
+ <li id="pspro_group_<?php echo esc_attr( $group_value['ID'] )?>">
380
+ <label class="selectit">
381
+ <input value="<?php echo esc_attr( $group_value['ID'] )?>" type="checkbox" name="pspro_group_list[]" id="in_pspro_group_<?php echo esc_attr( $group_value['ID'] )?>"
382
  <?php echo ( esc_attr( $ungrouped_checked?? '' ) );
383
  echo ( esc_attr( $checked ) );
384
+ ?>/>
385
+ <?php
386
+ echo esc_html( $group_value['group_name'] );
387
  ?>
388
  </label>
389
  </li>
390
  <?php
391
+
392
  }
393
 
394
  ?>
408
  if( empty($jsLocation) ){
409
  $jsLocation = 'header';
410
  }
411
+
412
  ?>
413
 
414
  <div id="pspro_edit_options_where">
417
 
418
  <input type="radio" name="pspro_edit_js_where" id="pspro_edit_js_header" class="pspro_edit_js_radio" value="header" <?php echo ( $jsLocation == 'header' ) ? ( esc_attr( 'checked' ) ) : '' ?> >
419
  <label for="pspro_edit_js_header" class="pspro_edit_js_label"><?php esc_html_e( "Header", "post-snippets" ) ?></label><br>
420
+
421
  <input type="radio" name="pspro_edit_js_where" id="pspro_edit_js_footer" class="pspro_edit_js_radio" value="footer" <?php echo ( $jsLocation == 'footer' ) ? ( esc_attr( 'checked' ) ) : '' ?> >
422
  <label for="pspro_edit_js_footer" class="pspro_edit_js_label"><?php esc_html_e( "Footer", "post-snippets" ) ?></label><br>
423
+
424
  </div>
425
 
426
 
434
  $location = 'admin';
435
  }
436
  $available_site_locations = Edit::availableSiteLocations();
437
+
438
  ?>
439
 
440
  <div id="pspro_edit_options_where_in_site">
447
  foreach ($available_site_locations as $available_site_location_slug => $available_site_location) {
448
  ?>
449
  <input
450
+
451
+ type="radio" name="pspro_edit_where_site"
452
+ id="pspro_edit_<?php echo esc_attr( $available_site_location_slug ) ?>"
453
+ class="pspro_edit_js_radio"
454
+ value="<?php echo esc_attr( $available_site_location_slug ) ?>"
455
  <?php echo ( $location == $available_site_location_slug ) ? ( esc_attr( 'checked' ) ) : '' ?>
456
  >
457
+
458
+ <label
459
+
460
+ for="pspro_edit_<?php echo esc_attr( $available_site_location_slug ) ?>"
461
  class="pspro_edit_js_label"><?php esc_html_e( $available_site_location, "post-snippets" )
462
+
463
  ?></label><br>
464
 
465
  <?php
466
  }
467
 
468
  ?>
469
+
470
  </div>
471
 
472
 
473
  <?php
474
+
475
+
476
  }
477
 
478
  }
505
  }
506
 
507
  static function pages_metabox_content(){
508
+
509
  $pages = self::getAllPagesAndPosts();
510
  $pageLocation = self::getLocation('page');
511
  $available_locations = Edit::availablePageLocations();
521
  $pageLocation = 'specific_page';
522
  $disabled = '';
523
  }
524
+
525
  ?>
526
  <div id="pspro_edit_select_location" class="pspro_edit_display_flex">
527
 
532
  foreach ($available_locations as $available_location_slug => $available_location) {
533
  ?>
534
  <option value="<?php echo esc_attr( $available_location_slug ) ?>" <?php echo ( $available_location_slug == $pageLocation ? 'selected' : '') ?>
535
+
536
  ><?php esc_html_e( $available_location, 'post-snippets' ) ?></option>
537
 
538
  <?php
549
  ?>
550
  <option value="<?php echo esc_attr( $page->ID ) ?>" data-url = "<?php echo esc_attr( get_permalink($page->ID) ) ?>"
551
  <?php echo ( $selected_page == $page->ID ? 'selected' : '' ) ?>
552
+
553
  ><?php echo esc_attr( ( !empty($page->post_title) ) ? $page->post_title : $page->post_name ) ?></option>
554
 
555
  <?php
559
  </select>
560
 
561
  <button type="button" id="pspro_edit_add_selection" class="button button-primary" disabled><?php esc_html_e( 'Add', 'post-snippets' ) ?></button>
562
+
563
  </div>
564
 
565
 
574
  }
575
 
576
  $location = Shortcode::filterVars( self::get_data('snippet_vars') );
577
+
578
  if( $type === 'js' ){
579
 
580
  if( !empty($location) ){
581
+
582
  $location = array_keys($location);
583
+
584
  if( !empty( $location[1] ) ){
585
  $location = $location[1];
586
  if($location !== 'header' && $location !== 'footer'){
600
  elseif( $type === 'site' ){
601
 
602
  if( !empty($location) ){
603
+
604
  $location = array_keys($location);
605
+
606
  if( !empty( $location[2] ) ){
607
  $location = $location[2];
608
  $available_site_locations = array_keys( Edit::availableSiteLocations() );
623
  elseif( $type === 'page' ){
624
 
625
  if( !empty($location) ){
626
+
627
  $location_keys = array_keys($location);
628
+
629
  if( !empty( $location_keys[0] ) ){
630
 
631
  $location_type = $location_keys[0];
701
 
702
  }
703
 
704
+
705
  function snippetTitle(){
706
 
707
  ?>
708
+
709
  <div id="titlediv">
710
  <div id="titlewrap">
711
  <?php
714
  ?>
715
  <label class="screen-reader-text" id="title-prompt-text" for="title"><?php echo esc_html($title_placeholder) ; ?></label>
716
  <input type="text" name="snippet_title" size="30" value="<?php echo esc_attr( $snippet_title ); ?>" id="title" spellcheck="true" autocomplete="off" placeholder = "<?php echo esc_html($title_placeholder); ?>"/>
717
+
718
  </div>
719
+
720
  </div><!-- /titlediv -->
721
+
722
  <?php
723
 
724
  }
752
  // 'markerGutter' => true,
753
  'lint' => true
754
  // 'htmllint' => true
755
+
756
  )
757
  ));
758
 
759
  wp_localize_script('post-snippets', 'cm_settings', $cm_settings);
760
+
761
  wp_enqueue_script('wp-theme-plugin-editor');
762
  wp_enqueue_style('wp-codemirror');
763
  wp_enqueue_style( 'post-snippets-edit', plugin_dir_url( PS_MAIN_FILE_PATH ) . "assets/edit.css");
764
+ wp_enqueue_script( 'post-snippets-edit-menu', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/edit.js');
765
 
766
  return;
767
  }
768
 
769
  wp_enqueue_style( 'post-snippets-editor', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/editor.css' );
770
  wp_enqueue_script( 'post-snippets-editor', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/editor.js' );
771
+
772
  wp_enqueue_style( 'post-snippets-edit', plugin_dir_url( PS_MAIN_FILE_PATH ) . "assets/edit.css");
773
+ wp_enqueue_script( 'post-snippets-edit-menu', plugin_dir_url( PS_MAIN_FILE_PATH ) . 'assets/edit.js');
774
+ }
775
 
776
  function codeEditor(){
777
+
778
  $snippet_content = self::get_data('snippet_content');
779
 
780
  ?>
781
  <style>
782
  #ps_snippet_code_editor textarea{
783
+
784
  width: 100%;
785
  min-height: 60vh;
786
  height: calc(100vh - 295px);
797
  }
798
 
799
  .pspro_codeEditor_heading{
800
+
801
  margin-top: 1.5rem !important;
802
  margin-bottom: 1rem !important;
803
 
809
  <input type="hidden" name="pspro-edit-code-editor-page" value="<?php echo esc_attr( self::get_snippet_page() ) ?>">
810
  <div id="ps_snippet_code_editor">
811
  <h1 class="pspro_codeEditor_heading wp-heading"> <?php esc_html_e( 'Code Editor:', 'post-snippets' ) ?> </h1>
812
+ <textarea name="snippet_content" id="ps_code_editor"><?php echo esc_html( stripslashes( $snippet_content ) ) ?></textarea>
813
  </div>
814
  <?php
815
 
817
 
818
  public static function setup_sections(){
819
 
820
+
821
+ add_settings_section( 'pspro_edit_snippet',
822
+ '',
823
  '',
 
824
  'post-snippets-edit' );
825
 
826
  add_settings_field( 'snippet_vars', //unique_name
833
  'id' => 'snippet_vars',
834
  'label_for' => 'pspro_snippet_vars',
835
  'description' => __( 'Enter Variables For This Snippet', 'post-snippets' )
836
+ )
837
  ); //value to pass to callback function
838
+
839
  add_settings_field( 'snippet_shortcode',
840
  __('Shortcode:','post-snippets'),
841
  array( \PostSnippets::EDIT_CLASS, 'add_section_fields' ),
848
  'description' => __( 'Enable Shortcode For This Snippet', 'post-snippets' )
849
  )
850
  );
851
+
852
  add_settings_field( 'snippet_php',
853
  __('PHP Code:','post-snippets'),
854
  array( \PostSnippets::EDIT_CLASS, 'add_section_fields' ),
861
  'description' => __( 'Enable PHP Rendering For This Snippet', 'post-snippets' )
862
  )
863
  );
864
+
865
+
866
  add_settings_field( 'snippet_wptexturize',
867
  __('WPtexturize:','post-snippets'),
868
  array( \PostSnippets::EDIT_CLASS, 'add_section_fields' ),
876
  )
877
  );
878
  }
879
+
880
  public static function add_section_fields( $arguments ) {
881
 
882
  if($arguments['id'] == 'snippet_vars'){
883
+
884
  $snippet_vars = self::get_data($arguments['id']);
885
 
886
  ?>
887
+
888
+ <textarea
889
+ name="<?php echo esc_attr( $arguments['id'] ) ?>"
890
+ id="pspro_<?php echo esc_attr( $arguments['id'] ) ?>"
891
  cols="55" rows="4"
892
  ><?php echo esc_html( $snippet_vars ) ?></textarea>
893
+
894
  <?php
895
 
896
  return;
897
  }
898
+ if($arguments['id'] == 'snippet_wptexturize'){
899
+ $is_checked = self::get_data($arguments['id']);
900
+
901
+ ?>
902
+
903
+ <input type="checkbox"
904
+ name="<?php echo esc_attr( $arguments['id'] ) ?>"
905
+ id="pspro_<?php echo esc_attr( $arguments['id'] ) ?>"
906
+ value = "1"
907
+ <?php echo ( ($is_checked)?esc_attr( 'checked' ):'' ) ?>
908
+
909
+ />
910
+
911
+ <?php
912
+ echo '<label>';
913
+ echo esc_html('Text enclosed in the tags <pre> <code>, <kbd>, <style>, <script>, and <tt> will be skipped.');
914
+ echo 'For more details please visit <a href="https://developer.wordpress.org/reference/functions/wptexturize/" target="_blank">WPtexturize</a>.';
915
+ echo '</label>';
916
+ }
917
+ else
918
+ {
919
 
920
  $is_checked = self::get_data($arguments['id']);
921
+
922
  ?>
923
 
924
+ <input type="checkbox"
925
+ name="<?php echo esc_attr( $arguments['id'] ) ?>"
926
  id="pspro_<?php echo esc_attr( $arguments['id'] ) ?>"
927
  value = "1"
928
  <?php echo ( ($is_checked)?esc_attr( 'checked' ):'' ) ?>
 
929
 
930
+ />
931
 
932
  <?php
933
+ }
934
+
935
+
936
  }
937
 
938
 
942
 
943
  ?>
944
  <h1 class="ps_description_heading wp-heading" style="margin-top:1.5rem; margin-bottom:1.5rem"> <?php esc_html_e( 'Description:', 'post-snippets' ) ?> </h1>
945
+
946
  <?php
947
 
948
  wp_editor(stripslashes($snippet_desc), 'ps_snippet_description', array(
949
+
950
  'textarea_name' => 'snippet_desc',
951
+
952
  // 'wpautop' => true,
953
  // 'media_buttons' => false,
954
  // 'editor_class' => 'my_custom_class',
955
  // 'textarea_rows' => 10
956
+
957
  ) );
958
 
959
 
964
  $column = sanitize_key( $column );
965
 
966
  if(self::$editSnippetPage){
967
+
968
  global $wpdb;
969
 
970
  $data = $wpdb->get_var($wpdb->prepare("SELECT $column FROM ".$wpdb->prefix.\PostSnippets::TABLE_NAME." WHERE ID = %d", self::$snippet_id));
971
  }
972
+
973
  else{
974
 
975
  if($column == "snippet_title"){
980
  }
981
 
982
  $data = $_REQUEST[$column]?? '';
983
+
984
  }
985
 
986
  return $data;
995
  * @param String $method - Whether to Increment or Decrement
996
  *
997
  * @return Void
998
+ *
999
  */
1000
  public static function change_group_count($group_list, $method){
1001
 
1002
  global $wpdb;
1003
 
1004
  if( !strcmp($method, 'increment') ){
1005
+
1006
  foreach ($group_list as $key => $group_id) {
1007
+
1008
  $group_count = $wpdb->get_var( $wpdb->prepare("SELECT group_count FROM ".$wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME." WHERE ID = %d", $group_id) );
1009
  $group_count = $group_count + 1;
1010
 
1028
  }
1029
 
1030
  elseif( !strcmp($method, 'decrement') ){
1031
+
1032
  foreach ($group_list as $key => $group_id) {
1033
+
1034
  $group_count = $wpdb->get_var( $wpdb->prepare("SELECT group_count FROM ".$wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME." WHERE ID = %d", $group_id) );
1035
+
1036
  if($group_count == 0){ /**if there is no snippet in a group but a snippet is deleted, this condition would never met but just in case*/
1037
+
1038
  continue;
1039
  }
1040
 
1063
 
1064
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit-js' /*&& isset($_REQUEST['pspro_edit_location_select'])*/ ){
1065
 
1066
+ return true;
1067
  }
1068
  else{
1069
  return false;
1074
 
1075
  if( $this->is_JS() && isset( $_REQUEST['pspro_edit_js_where'] ) && ( $_REQUEST['pspro_edit_js_where'] === 'header' || $_REQUEST['pspro_edit_js_where'] === 'footer' ) ){
1076
 
1077
+ return $_REQUEST['pspro_edit_js_where'];
1078
  }
1079
  elseif( $this->is_JS() ){
1080
  return 'header';
1092
 
1093
  if( isset($_REQUEST['page']) && $_REQUEST['page'] === 'post-snippets-edit-css' /*&& isset($_REQUEST['pspro_edit_location_select'])*/ ){
1094
 
1095
+ return true;
1096
  }
1097
  else{
1098
  return false;
1100
  }
1101
 
1102
  function codeLocation(){
1103
+
1104
  if( ( $this->is_JS() || $this->is_CSS() ) && isset( $_REQUEST['pspro_edit_location_select'] ) && $this->siteLocation() != 'admin'){
1105
 
1106
  $selected_location = $_REQUEST['pspro_edit_location_select'];
1110
  if( $selected_location === 'specific_page'){
1111
 
1112
  if( isset( $_REQUEST['pspro_edit_page_select'] ) && is_numeric( $_REQUEST['pspro_edit_page_select'] ) ){
1113
+
1114
  $specific_page_exist = Edit::isPageOrPost( $_REQUEST['pspro_edit_page_select'] );
1115
 
1116
+ if( $specific_page_exist ){
1117
  return 'specific_page='.$_REQUEST['pspro_edit_page_select'];
1118
  }
1119
  else{
1124
  return '';
1125
 
1126
  }
1127
+
1128
  elseif( in_array($selected_location, $available_locations_slugs, true) ){
1129
 
1130
  return $selected_location;
1131
  }
1132
 
1133
+ return '';
1134
  }
1135
  elseif( $this->siteLocation() == 'admin' ){
1136
  return 'all_website';
1153
  return 'admin';
1154
  }
1155
  return '';
1156
+ }
1157
 
1158
  static function availablePageLocations(){
1159
 
1167
  }
1168
 
1169
  static function availableSiteLocations(){
1170
+
1171
  return array(
1172
  'frontend' => 'In Front-End',
1173
  'admin' => 'In Admin',
1180
  if( isset($_REQUEST['submit']) ){
1181
 
1182
  check_admin_referer( 'pspro_edit_snippet', 'pspro_edit_snippet_nonce' );
1183
+
1184
  global $wpdb;
1185
  $table_name = $wpdb->prefix.\PostSnippets::TABLE_NAME;
1186
+
1187
+
1188
+
1189
  //******************** Preparing Group List ************************* */
1190
 
1191
  $upgrouped_id = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM ".$wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME." WHERE group_slug = %s", __('ungrouped','post-snippets') ) );
1192
  $group_list = $_REQUEST['pspro_group_list'] ?? array($upgrouped_id); /**If no group is selected */
1193
+
1194
  //*************************** END ****************************** */
1195
 
1196
+
1197
 
1198
  //******************** Preparing Snippet Title ************************* */
1199
 
1201
 
1202
 
1203
  //****************************** END *********************************** */
1204
+
1205
+
1206
  //******************** Preparing Snippet vars ************************* */
1207
 
1208
  if( isset($_REQUEST['snippet_vars']) && !empty( $_REQUEST['snippet_vars'] ) ){
1209
 
1210
  $snippet_vars = self::filter_snippet_vars( $_REQUEST['snippet_vars'] );
1211
+
1212
  }
1213
  elseif( isset($_REQUEST['snippet_vars']) && empty( $_REQUEST['snippet_vars'] ) ){
1214
 
1215
  $snippet_vars = '';
1216
+
1217
  }
1218
  elseif( !isset($_REQUEST['snippet_vars']) && ( $this->is_JS() || $this->is_CSS() ) ){
1219
 
1234
 
1235
  //****************************** END *********************************** */
1236
 
1237
+
1238
+
1239
  //******************** Preparing Snippet Content ************************* */
1240
 
1241
  $snippet_content = $_REQUEST['snippet_content'];
1244
 
1245
  // $start = '<script type=\"text/javascript\">';
1246
  // $end = '</script>';
1247
+
1248
  // $snippet_content = $start.'\n'.$snippet_content.'\n'.$end;
1249
 
1250
  // }
1258
  $snippet_desc = sanitize_textarea_field( $_REQUEST['snippet_desc'] ?? '' );
1259
 
1260
  //****************************** END *********************************** */
1261
+
1262
+
1263
+
1264
  //******************** Preparing Snippet PHP ************************* */
1265
 
1266
  if( isset($_REQUEST['snippet_php']) ){
1267
  $snippet_php = ( ($_REQUEST['snippet_php']?? 0 ) == 1) ? 1 : 0;
1268
  }
1269
  else{
1270
+ /**As this variable is not used in JS and CSS pages, they
1271
+ * can be used to save those pages. so new DB entry
1272
  * doesn't need to be created.
1273
  * 2 for JS and 3 for CSS.
1274
  */
1286
  }
1287
 
1288
  //****************************** END *********************************** */
1289
+
1290
+
1291
  if( !empty($snippet_title) ){
1292
+
1293
  if(self::$editSnippetPage){ /**This is a edit page of Previous Snippet */
1294
 
1295
  $current_snippet_title = $wpdb->get_var( $wpdb->prepare("SELECT snippet_title FROM $table_name WHERE ID = %d", self::$snippet_id) );
1335
  )
1336
  );
1337
 
1338
+
1339
  if($snippet_updated){
1340
 
1341
  printf('<div class="notice notice-success is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Updated', 'post-snippets' ) );
1342
  // return;
1343
  }
1344
+
1345
  elseif(!$snippet_updated){
1346
 
1347
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Update Failed', 'post-snippets' ) );
1352
 
1353
  //change group count here
1354
  if( !empty($increment_count) ){ /**Increment Count of these groups */
1355
+
1356
  self::change_group_count($increment_count, 'increment');
1357
 
1358
  }
1359
 
1360
  if( !empty($decrement_count) ){ /**Decrement Count of these groups */
1361
+
1362
  self::change_group_count($decrement_count, 'decrement');
1363
 
1364
  }
1368
  }
1369
 
1370
  elseif( !self::$editSnippetPage && !$this->duplicate_title_check($snippet_title) ){ /**Adding New Snippet here */
1371
+
1372
+ $snippet_added = $wpdb->insert(
1373
  $table_name,
1374
  array(
1375
  'snippet_group' => maybe_serialize( $group_list ),
1382
  'snippet_shortcode' => ( ($_REQUEST['snippet_shortcode']?? 0 ) == 1) ? 1 : 0,
1383
  'snippet_php' => $snippet_php,
1384
  'snippet_wptexturize' => ( ($_REQUEST['snippet_wptexturize']?? 0 ) == 1) ? 1 : 0,
1385
+ )
1386
  );
1387
 
1388
  if( $snippet_added ){
1395
  ?> <script> <?php echo("location.href = '".$url."';"); ?> </script> <?php
1396
 
1397
  }
1398
+
1399
  elseif(!$snippet_added){
1400
 
1401
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippet Not Added', 'post-snippets' ) );
1402
+
1403
  }
1404
  }
1405
 
1406
  else{
1407
 
1408
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'Duplicate Titles are Not Allowed', 'post-snippets' ) );
1409
+
1410
  }
1411
  }
1412
 
1425
  if( isset($_REQUEST['page']) && ( $_REQUEST['page'] === 'post-snippets-edit' || $_REQUEST['page'] === 'post-snippets-edit-js' || $_REQUEST['page'] === 'post-snippets-edit-css' ) ){
1426
  return $_REQUEST['page'];
1427
  }
1428
+
1429
  return 'post-snippets-edit';
1430
  }else{
1431
 
1457
 
1458
  $duplicate_title_exist = in_array($snippet_title, $all_snippets_titles);
1459
 
1460
+ if( $duplicate_title_exist ){
1461
+ /**Cause This function is also used to Increment
1462
  * Title Count by number_duplicate_snippet_title,
1463
  * otherwise, above written code would have to be
1464
+ * repeated.
1465
  **/
1466
 
1467
  return $all_snippets_titles;
1532
  if(self::$editSnippetPage){
1533
 
1534
  $snippet_vars = Shortcode::filterVars( self::get_data('snippet_vars') );
1535
+
1536
  $snippet_vars_text = '';
1537
  foreach ($snippet_vars as $var => $value) {
1538
  $snippet_vars_text .= ' '. $var . (!empty($value)?'=':'') . $value;
1561
 
1562
  if($download_succeed){
1563
  printf('<div class="notice notice-success is-dismissible"><p>%s..</p></div>', esc_html__( 'Snippets Downloaded', 'post-snippets' ) );
1564
+
1565
  }
1566
  else{
1567
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'There has been an Error', 'post-snippets' ) );
1574
 
1575
  check_admin_referer( 'pspro_edit_snippet', 'pspro_edit_snippet_nonce' );
1576
 
1577
+
1578
  }
1579
 
1580
 
1589
  else{
1590
  printf('<div class="notice notice-error is-dismissible"><p>%s..</p></div>', esc_html__( 'There has been an Error', 'post-snippets' ) );
1591
  }
1592
+
1593
  }
1594
 
1595
 
1621
  'snippet_shortcode' => (($imported_snippet['snippet_shortcode']?? 0) == 1) ? : 0,
1622
  'snippet_php' => $snippet_php,
1623
  'snippet_wptexturize' => (($imported_snippet['snippet_wptexturize']?? 0) == 1) ? : 0,
1624
+ )
1625
  );
1626
 
1627
  if($snippet_added){
src/PostSnippets/ImportExport.php CHANGED
@@ -13,7 +13,6 @@ class ImportExport
13
  const FILE_CFG = 'post-snippets-export.cfg';
14
 
15
  private $downloadUrl;
16
- private $snippet_count;
17
 
18
  /**
19
  * Export Snippets.
@@ -42,8 +41,8 @@ class ImportExport
42
  return true;
43
  } else {
44
  return false;
45
- }
46
-
47
  }
48
 
49
  /**
@@ -153,7 +152,7 @@ class ImportExport
153
  'post_snippets_import',
154
  $snippets
155
  );
156
-
157
  $imported_snippets = maybe_unserialize($snippets);
158
 
159
  if( !empty($imported_snippets) ){
@@ -164,7 +163,7 @@ class ImportExport
164
  $import .=
165
  '<p><strong>'.
166
  __('Empty Snippets.', 'post-snippets').
167
- '</strong></p>';
168
  }
169
  }
170
 
@@ -206,7 +205,6 @@ class ImportExport
206
  $table_name = $wpdb->prefix.\PostSnippets::TABLE_NAME;
207
  $group_table_name = $wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME;
208
  $snippet_added = false;
209
- $this->snippet_count=0;
210
 
211
  if( empty( array_column($imported_snippets, 'ID') ) ){ /**Old Exported Files, Backward Compatibility */
212
 
@@ -216,7 +214,7 @@ class ImportExport
216
  if( !empty($current_snippets_titles) ){ /**If there are any Existing Snippets */
217
 
218
  foreach ($imported_snippets as $imported_snippet) {
219
-
220
  if( in_array($imported_snippet['title'], $current_snippets_titles) ){
221
  //duplicate according to settings
222
  $snippet_added = $this->duplicateSnippet($imported_snippet);
@@ -225,26 +223,22 @@ class ImportExport
225
  else{ /**No Duplicate */
226
  DBTable::update_db_table_previous(array($imported_snippet), true);
227
  $snippet_added = true;
228
- $this->snippet_count++;
229
  }
230
  }
231
-
232
  }
233
  else{ /**There aren't any Existing Snippets */
234
-
235
  DBTable::update_db_table_previous($imported_snippets, true);
236
  $snippet_added = true;
237
- $this->snippet_count++;
238
 
239
  }
240
 
241
- if($snippet_added && $this->snippet_count>0){
242
  return
243
  '<p><strong>'.
244
- sprintf(
245
- __('%s Snippets Added Successfully.', 'post-snippets'),
246
- $this->snippet_count++).
247
- '</strong></p>';
248
  }else{
249
  return
250
  '<p><strong>'.
@@ -263,11 +257,11 @@ class ImportExport
263
  if( !empty($group_details) ){
264
  unset($imported_snippets['group_details']);
265
  }
266
-
267
  if( !empty($current_snippets_titles) ){ /**If there are any Existing Snippets */
268
 
269
  foreach ($imported_snippets as $imported_snippet) {
270
-
271
  if( in_array($imported_snippet['snippet_title'], $current_snippets_titles) ){
272
  //duplicate according to settings
273
  $imported_snippet = $this->updateGroupDetails($imported_snippet, $group_details);
@@ -278,9 +272,6 @@ class ImportExport
278
 
279
  $imported_snippet = $this->updateGroupDetails($imported_snippet, $group_details);
280
  $snippet_added = Edit::addImportedSnippet($imported_snippet);
281
- if($snippet_added){
282
- $this->snippet_count++;
283
- }
284
 
285
  }
286
  }
@@ -292,25 +283,20 @@ class ImportExport
292
 
293
  $imported_snippet = $this->updateGroupDetails($imported_snippet, $group_details);
294
  $snippet_added = Edit::addImportedSnippet($imported_snippet);
295
- if($snippet_added){
296
- $this->snippet_count++;
297
- }
298
 
299
- }
300
  }
301
 
302
- if($snippet_added && $this->snippet_count>0){
303
 
304
- return
305
- '<p><strong>'.
306
- sprintf(
307
- __('%s Snippets Added Successfully.', 'post-snippets'),
308
- $this->snippet_count++).
309
- '</strong></p>';
310
 
311
  }
312
  else{
313
- return
314
  '<p><strong>'.
315
  __('Snippets Not Added.', 'post-snippets').
316
  '</strong></p>';
@@ -330,27 +316,21 @@ class ImportExport
330
 
331
  if( isset($imported_snippet['ID']) ){ /**Revamped Version */
332
  $imported_snippet['snippet_title'] = Edit::number_duplicate_snippet_title( $imported_snippet['snippet_title'] );
333
- $snippet_added= Edit::addImportedSnippet($imported_snippet);
334
- if($snippet_added)
335
- {
336
- $this->snippet_count++;
337
- }
338
- return $snippet_added ;
339
  }
340
  else{ /**Old Version, without ID */
341
  $imported_snippet["title"] = Edit::number_duplicate_snippet_title( $imported_snippet["title"] );
342
  DBTable::update_db_table_previous(array($imported_snippet), true);
343
- $this->snippet_count++;
344
  return true;
345
  }
346
  }
347
-
348
  if( $duplicate_option['duplicate_option'] == 'replace_duplicate' ){
349
 
350
  return $this->replaceSnippet($imported_snippet);
351
 
352
  }
353
-
354
  if( $duplicate_option['duplicate_option'] == 'skip_duplicate' ){
355
  return true;
356
  }
@@ -387,7 +367,7 @@ class ImportExport
387
  if( isset( $imported_snippet['snippet_group'] ) ){
388
 
389
  /**
390
- * change group ids, if they are different
391
  * from imported
392
  */
393
 
@@ -396,7 +376,7 @@ class ImportExport
396
  $snippet_group = $imported_snippet['snippet_group'];
397
 
398
  $group_ids_to_increase_count = array_diff( maybe_unserialize( $imported_snippet['snippet_group'] ), maybe_unserialize( $current_snippet_group_ids ) );
399
-
400
  $group_ids_to_decrease_count = array_diff( maybe_unserialize( $current_snippet_group_ids ), maybe_unserialize( $imported_snippet['snippet_group'] ) );
401
 
402
  }
@@ -440,7 +420,7 @@ class ImportExport
440
 
441
  Edit::change_group_count($group_ids_to_increase_count, 'increment');
442
  }
443
-
444
  if( isset($group_ids_to_decrease_count) && !empty($group_ids_to_decrease_count) ){
445
 
446
  Edit::change_group_count($group_ids_to_decrease_count, 'decrement');
@@ -474,13 +454,13 @@ class ImportExport
474
  if($group_id == NULL){
475
 
476
  $group_added_status = $wpdb->insert(
477
- $group_table_name,
478
- array(
479
  'group_name' => $group['group_name'],
480
  'group_slug' => $group['group_slug'],
481
  'group_desc' => $group['group_desc'],
482
  'group_count' => 0
483
- )
484
  );
485
 
486
  $imported_snippet = $this->changeGroupId($imported_snippet, $group['ID'], $wpdb->insert_id);
@@ -490,15 +470,15 @@ class ImportExport
490
  $imported_snippet = $this->changeGroupId($imported_snippet, $group['ID'], $group_id);
491
 
492
  }
493
- }
494
-
495
  return $imported_snippet;
496
  }
497
 
498
  private function changeGroupId($imported_snippet, $old_id, $new_id){
499
 
500
  $imported_snippet_groups = maybe_unserialize( $imported_snippet['snippet_group'] );
501
-
502
  if( in_array($old_id, $imported_snippet_groups, true) && ( $old_id != $new_id) ){
503
 
504
  $imported_snippet_groups[ array_search($old_id, $imported_snippet_groups, true) ] = strval( $new_id );
@@ -519,7 +499,7 @@ class ImportExport
519
  {
520
  $dir = wp_upload_dir();
521
  $upload_dir = $dir['basedir'] . '/';
522
- $upload_url = $dir['baseurl'] . '/';
523
 
524
  global $wpdb;
525
 
@@ -530,14 +510,14 @@ class ImportExport
530
  $snippets = $wpdb->get_results($wpdb->prepare("SELECT * from %1s", $table_name ), ARRAY_A );
531
 
532
  }else{
533
-
534
  $snippets = $wpdb->get_results($wpdb->prepare("SELECT * from $table_name WHERE ID IN %1s", $snippets_ids ), ARRAY_A );
535
  }
536
 
537
  $snippets = $this->addGroupDetails($snippets);
538
 
539
  $snippets = maybe_serialize( $snippets );
540
-
541
  $snippets = apply_filters('post_snippets_export', $snippets);
542
 
543
 
@@ -576,7 +556,7 @@ class ImportExport
576
 
577
  $snippets['group_details'] = $group_details;
578
 
579
- return $snippets;
580
  }
581
 
582
  /**
13
  const FILE_CFG = 'post-snippets-export.cfg';
14
 
15
  private $downloadUrl;
 
16
 
17
  /**
18
  * Export Snippets.
41
  return true;
42
  } else {
43
  return false;
44
+ }
45
+
46
  }
47
 
48
  /**
152
  'post_snippets_import',
153
  $snippets
154
  );
155
+
156
  $imported_snippets = maybe_unserialize($snippets);
157
 
158
  if( !empty($imported_snippets) ){
163
  $import .=
164
  '<p><strong>'.
165
  __('Empty Snippets.', 'post-snippets').
166
+ '</strong></p>';
167
  }
168
  }
169
 
205
  $table_name = $wpdb->prefix.\PostSnippets::TABLE_NAME;
206
  $group_table_name = $wpdb->prefix.\PostSnippets::GROUP_TABLE_NAME;
207
  $snippet_added = false;
 
208
 
209
  if( empty( array_column($imported_snippets, 'ID') ) ){ /**Old Exported Files, Backward Compatibility */
210
 
214
  if( !empty($current_snippets_titles) ){ /**If there are any Existing Snippets */
215
 
216
  foreach ($imported_snippets as $imported_snippet) {
217
+
218
  if( in_array($imported_snippet['title'], $current_snippets_titles) ){
219
  //duplicate according to settings
220
  $snippet_added = $this->duplicateSnippet($imported_snippet);
223
  else{ /**No Duplicate */
224
  DBTable::update_db_table_previous(array($imported_snippet), true);
225
  $snippet_added = true;
 
226
  }
227
  }
228
+
229
  }
230
  else{ /**There aren't any Existing Snippets */
231
+
232
  DBTable::update_db_table_previous($imported_snippets, true);
233
  $snippet_added = true;
 
234
 
235
  }
236
 
237
+ if($snippet_added){
238
  return
239
  '<p><strong>'.
240
+ __('Snippets Added Successfully.', 'post-snippets').
241
+ '</strong></p>';
 
 
242
  }else{
243
  return
244
  '<p><strong>'.
257
  if( !empty($group_details) ){
258
  unset($imported_snippets['group_details']);
259
  }
260
+
261
  if( !empty($current_snippets_titles) ){ /**If there are any Existing Snippets */
262
 
263
  foreach ($imported_snippets as $imported_snippet) {
264
+
265
  if( in_array($imported_snippet['snippet_title'], $current_snippets_titles) ){
266
  //duplicate according to settings
267
  $imported_snippet = $this->updateGroupDetails($imported_snippet, $group_details);
272
 
273
  $imported_snippet = $this->updateGroupDetails($imported_snippet, $group_details);
274
  $snippet_added = Edit::addImportedSnippet($imported_snippet);
 
 
 
275
 
276
  }
277
  }
283
 
284
  $imported_snippet = $this->updateGroupDetails($imported_snippet, $group_details);
285
  $snippet_added = Edit::addImportedSnippet($imported_snippet);
 
 
 
286
 
287
+ }
288
  }
289
 
290
+ if($snippet_added){
291
 
292
+ return
293
+ '<p><strong>'.
294
+ __('Snippets Added Successfully.', 'post-snippets').
295
+ '</strong></p>';
 
 
296
 
297
  }
298
  else{
299
+ return
300
  '<p><strong>'.
301
  __('Snippets Not Added.', 'post-snippets').
302
  '</strong></p>';
316
 
317
  if( isset($imported_snippet['ID']) ){ /**Revamped Version */
318
  $imported_snippet['snippet_title'] = Edit::number_duplicate_snippet_title( $imported_snippet['snippet_title'] );
319
+ return Edit::addImportedSnippet($imported_snippet);
 
 
 
 
 
320
  }
321
  else{ /**Old Version, without ID */
322
  $imported_snippet["title"] = Edit::number_duplicate_snippet_title( $imported_snippet["title"] );
323
  DBTable::update_db_table_previous(array($imported_snippet), true);
 
324
  return true;
325
  }
326
  }
327
+
328
  if( $duplicate_option['duplicate_option'] == 'replace_duplicate' ){
329
 
330
  return $this->replaceSnippet($imported_snippet);
331
 
332
  }
333
+
334
  if( $duplicate_option['duplicate_option'] == 'skip_duplicate' ){
335
  return true;
336
  }
367
  if( isset( $imported_snippet['snippet_group'] ) ){
368
 
369
  /**
370
+ * change group ids, if they are different
371
  * from imported
372
  */
373
 
376
  $snippet_group = $imported_snippet['snippet_group'];
377
 
378
  $group_ids_to_increase_count = array_diff( maybe_unserialize( $imported_snippet['snippet_group'] ), maybe_unserialize( $current_snippet_group_ids ) );
379
+
380
  $group_ids_to_decrease_count = array_diff( maybe_unserialize( $current_snippet_group_ids ), maybe_unserialize( $imported_snippet['snippet_group'] ) );
381
 
382
  }
420
 
421
  Edit::change_group_count($group_ids_to_increase_count, 'increment');
422
  }
423
+
424
  if( isset($group_ids_to_decrease_count) && !empty($group_ids_to_decrease_count) ){
425
 
426
  Edit::change_group_count($group_ids_to_decrease_count, 'decrement');
454
  if($group_id == NULL){
455
 
456
  $group_added_status = $wpdb->insert(
457
+ $group_table_name,
458
+ array(
459
  'group_name' => $group['group_name'],
460
  'group_slug' => $group['group_slug'],
461
  'group_desc' => $group['group_desc'],
462
  'group_count' => 0
463
+ )
464
  );
465
 
466
  $imported_snippet = $this->changeGroupId($imported_snippet, $group['ID'], $wpdb->insert_id);
470
  $imported_snippet = $this->changeGroupId($imported_snippet, $group['ID'], $group_id);
471
 
472
  }
473
+ }
474
+
475
  return $imported_snippet;
476
  }
477
 
478
  private function changeGroupId($imported_snippet, $old_id, $new_id){
479
 
480
  $imported_snippet_groups = maybe_unserialize( $imported_snippet['snippet_group'] );
481
+
482
  if( in_array($old_id, $imported_snippet_groups, true) && ( $old_id != $new_id) ){
483
 
484
  $imported_snippet_groups[ array_search($old_id, $imported_snippet_groups, true) ] = strval( $new_id );
499
  {
500
  $dir = wp_upload_dir();
501
  $upload_dir = $dir['basedir'] . '/';
502
+ $upload_url = $dir['baseurl'] . '/';
503
 
504
  global $wpdb;
505
 
510
  $snippets = $wpdb->get_results($wpdb->prepare("SELECT * from %1s", $table_name ), ARRAY_A );
511
 
512
  }else{
513
+
514
  $snippets = $wpdb->get_results($wpdb->prepare("SELECT * from $table_name WHERE ID IN %1s", $snippets_ids ), ARRAY_A );
515
  }
516
 
517
  $snippets = $this->addGroupDetails($snippets);
518
 
519
  $snippets = maybe_serialize( $snippets );
520
+
521
  $snippets = apply_filters('post_snippets_export', $snippets);
522
 
523
 
556
 
557
  $snippets['group_details'] = $group_details;
558
 
559
+ return $snippets;
560
  }
561
 
562
  /**
src/PostSnippets/WPEditor.php CHANGED
@@ -203,7 +203,7 @@ class WPEditor
203
  $snippet = $snippet['snippet_content'];
204
  # Fixes for potential collisions:
205
  /* Replace <> with char codes, otherwise </script> in a snippet will break it */
206
- // $snippet = str_replace( '<', '\x3C', str_replace( '>', '\x3E', $snippet ) );
207
  /* Escape " with \" */
208
  // $snippet = str_replace( '"', '\"', $snippet );
209
  // $snippet = htmlspecialchars( stripslashes( $snippet ) );
203
  $snippet = $snippet['snippet_content'];
204
  # Fixes for potential collisions:
205
  /* Replace <> with char codes, otherwise </script> in a snippet will break it */
206
+ $snippet = str_replace( '<', '\x3C', str_replace( '>', '\x3E', $snippet ) );
207
  /* Escape " with \" */
208
  // $snippet = str_replace( '"', '\"', $snippet );
209
  // $snippet = htmlspecialchars( stripslashes( $snippet ) );