New User Approve - Version 2.5

Version Description

  • Updated localization - pot file updated, French and Italian po mo updated.
  • Fixed - Approve button color issue.
  • Fixed - Duplicate text message on user registration by invitation code.
  • Fixed - Rest API notice.
  • Improvement - When using invitation code user will not be created if invition code is not given or incorrect invitation code is used.
  • Improvement - When user successfully registers via invitation code the welcome email not sent, instead Approve email is sent and success message appears.
Download this release

Release Info

Developer wpexpertsio
Plugin Icon 128x128 New User Approve
Version 2.5
Comparing to
See all releases

Code changes from version 2.4.1 to 2.5

Files changed (99) hide show
  1. assets/css/nua-admin-style.css +54 -3
  2. freemius/assets/css/admin/account.css +1 -1
  3. freemius/assets/css/admin/add-ons.css +1 -2
  4. freemius/assets/css/admin/affiliation.css +1 -1
  5. freemius/assets/css/admin/clone-resolution.css +1 -0
  6. freemius/assets/css/admin/common.css +1 -2
  7. freemius/assets/css/admin/connect.css +1 -1
  8. freemius/assets/css/admin/debug.css +1 -1
  9. freemius/assets/css/admin/dialog-boxes.css +1 -2
  10. freemius/assets/css/admin/gdpr-optin-notice.css +1 -1
  11. freemius/assets/css/admin/optout.css +1 -0
  12. freemius/assets/css/customizer.css +1 -1
  13. freemius/config.php +5 -5
  14. freemius/includes/class-freemius-abstract.php +4 -63
  15. freemius/includes/class-freemius.php +2226 -1050
  16. freemius/includes/class-fs-admin-notices.php +56 -24
  17. freemius/includes/class-fs-api.php +27 -3
  18. freemius/includes/class-fs-lock.php +110 -0
  19. freemius/includes/class-fs-logger.php +2 -1
  20. freemius/includes/class-fs-plugin-updater.php +9 -7
  21. freemius/includes/class-fs-storage.php +106 -80
  22. freemius/includes/class-fs-user-lock.php +10 -46
  23. freemius/includes/customizer/class-fs-customizer-upsell-control.php +2 -2
  24. freemius/includes/entities/class-fs-affiliate-terms.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 +1 -1
  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 +794 -568
  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 +151 -142
  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/features.php +1 -1
  85. freemius/templates/powered-by.php +7 -7
  86. freemius/templates/pricing.php +12 -11
  87. freemius/templates/sticky-admin-notice-js.php +1 -2
  88. freemius/templates/tabs-capture-js.php +1 -1
  89. includes/admin-approve.php +14 -10
  90. includes/invitation-code.php +111 -4
  91. includes/messages.php +18 -0
  92. includes/zapier/includes/rest-api.php +17 -14
  93. localization/new-user-approve-fr_FR.mo +0 -0
  94. localization/new-user-approve-fr_FR.po +473 -218
  95. localization/new-user-approve-it_IT.mo +0 -0
  96. localization/new-user-approve-it_IT.po +450 -207
  97. localization/new-user-approve.pot +371 -127
  98. new-user-approve.php +16 -13
  99. readme.txt +11 -3
assets/css/nua-admin-style.css CHANGED
@@ -68,16 +68,67 @@ body.new-users-approve_page_nua-invitation-code {
68
  float: right;
69
  }
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  #pw_denied_users .button-primary {
72
  float: none;
73
  }
74
 
75
- #pw_pending_users .button,
76
- #pw_approved_users .button,
77
- #pw_denied_users .button {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  background: #eb5a46;
79
  color: #FFF;
80
  border-color: #f3463a;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
 
83
  #nua-approve-admin .postbox-header {
68
  float: right;
69
  }
70
 
71
+
72
+ #pw_pending_users table.widefat thead {
73
+
74
+ width:606px;
75
+
76
+ }
77
+
78
+
79
+ #pw_pending_users table.widefat thead th {
80
+
81
+ width:151px;
82
+
83
+ }
84
+
85
+ .users_edit_links {
86
+ display:block;
87
+ margin-top:-33px;
88
+ margin-left:38px;
89
+ }
90
+
91
+
92
  #pw_denied_users .button-primary {
93
  float: none;
94
  }
95
 
96
+ #pw_approved_users .button {
97
+ background: #eb5a46;
98
+ color: #FFF;
99
+ border-color: #f3463a;
100
+ }
101
+
102
+
103
+
104
+
105
+ #pw_pending_users .approve-btn {
106
+
107
+ background: #61bd4f;
108
+ color: #FFF;
109
+ border-color: #73e831;
110
+ }
111
+
112
+ #pw_pending_users .deny-btn {
113
+
114
  background: #eb5a46;
115
  color: #FFF;
116
  border-color: #f3463a;
117
+ margin-left:20px;
118
+ }
119
+
120
+ #pw_pending_users .deny-btn, #pw_pending_users .approve-btn {
121
+
122
+ width:81px;
123
+ text-align:center;
124
+ align-self: center;
125
+ margin-left:20px;
126
+ }
127
+
128
+ #pw_denied_users .button {
129
+ background: #61bd4f;
130
+ color: #FFF;
131
+ border-color: #73e831;
132
  }
133
 
134
  #nua-approve-admin .postbox-header {
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}#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
 
@@ -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;
@@ -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
  *
@@ -7635,12 +7817,21 @@
7635
 
7636
  /**
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
@@ -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
 
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;
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
  *
7817
 
7818
  /**
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
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;
@@ -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
 
@@ -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
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;
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
 
650
  }
651
 
652
  /**
653
+ * Get module's required data for the updates' mechanism.
654
  *
655
  * @author Vova Feldman (@svovaf)
656
  * @since 2.0.0
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-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
@@ -1306,8 +1306,8 @@
1306
  <?php endif ?>
1307
  </div>
1308
  </div>
1309
- </div>
1310
  <?php endforeach ?>
 
1311
  <?php endif ?>
1312
  <?php endif ?>
1313
  <div>
1306
  <?php endif ?>
1307
  </div>
1308
  </div>
 
1309
  <?php endforeach ?>
1310
+ </div>
1311
  <?php endif ?>
1312
  <?php endif ?>
1313
  <div>
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
+