diff --git a/assets/css/admin/account.css b/assets/css/admin/account.css index 8044ac690..76bdf40b8 100644 --- a/assets/css/admin/account.css +++ b/assets/css/admin/account.css @@ -1 +1 @@ -#fs_account .postbox,#fs_account .widefat{max-width:700px}#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 i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .button i.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}.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 var,.fs-key-value-table code{color:#0073AA;font-size:16px;background:none}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_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#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_account .postbox,#fs_account .widefat{max-width:700px}#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 i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .button i.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}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_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#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} diff --git a/assets/css/admin/deactivation-feedback.css b/assets/css/admin/deactivation-feedback.css index 27a596a27..47fa6df73 100644 --- a/assets/css/admin/deactivation-feedback.css +++ b/assets/css/admin/deactivation-feedback.css @@ -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 .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 .fs-modal-dialog li.reason{margin-bottom:10px}.fs-modal .fs-modal-dialog li.reason .reason-input{margin-left:29px}.fs-modal .fs-modal-dialog li.reason label{display:table}.fs-modal .fs-modal-dialog li.reason label>span{display:table-cell;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-modal-body,.fs-modal .fs-modal-footer{border:0;background:#fefefe;padding:20px}.fs-modal .fs-modal-body{border-bottom:0}.fs-modal .fs-modal-body h2{font-size:20px}.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:not(.active){display:none}.fs-modal .reason-input{margin:3px 0 3px 22px}.fs-modal .reason-input input,.fs-modal .reason-input textarea{width:100%}body.has-fs-modal{overflow:hidden}#the-list .deactivate>.fs-slug{display:none} +.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 .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 .fs-modal-dialog li.reason{margin-bottom:10px}.fs-modal .fs-modal-dialog li.reason .reason-input,.fs-modal .fs-modal-dialog li.reason .internal-message{margin-left:29px}.fs-modal .fs-modal-dialog li.reason label{display:table}.fs-modal .fs-modal-dialog li.reason label>span{display:table-cell;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-modal-body,.fs-modal .fs-modal-footer{border:0;background:#fefefe;padding:20px}.fs-modal .fs-modal-body{border-bottom:0}.fs-modal .fs-modal-body h2{font-size:20px}.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:not(.active){display:none}.fs-modal .reason-input,.fs-modal .internal-message{margin:3px 0 3px 22px}.fs-modal .reason-input input,.fs-modal .reason-input textarea,.fs-modal .internal-message input,.fs-modal .internal-message textarea{width:100%}.fs-modal li.reason.has-internal-message .internal-message{border:1px solid #ccc;padding:7px;display:none}body.has-fs-modal{overflow:hidden}#the-list .deactivate>.fs-slug{display:none} diff --git a/assets/css/admin/dialog-boxes.css b/assets/css/admin/dialog-boxes.css new file mode 100644 index 000000000..cd6448060 --- /dev/null +++ b/assets/css/admin/dialog-boxes.css @@ -0,0 +1,3 @@ +.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 .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-modal-body,.fs-modal .fs-modal-footer{border:0;background:#fefefe;padding:20px}.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}.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}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}} +#the-list .deactivate>.fs-slug{display:none}.fs-modal.fs-modal-license-activation .fs-modal-body input.license_key{width:100%}.fs-modal.fs-modal-license-key-resend .button-close{float:right;margin:5px}.fs-modal.fs-modal-license-key-resend .button-close:hover{cursor:pointer}.fs-modal.fs-modal-license-key-resend .fs-modal-body .input-container>.email-address-container{overflow:hidden;padding-right:2px}.fs-modal.fs-modal-license-key-resend .fs-modal-body input.email-address{width:100%}.fs-modal.fs-modal-license-key-resend .fs-modal-body .button-container{float:right;margin-left:7px}@media (max-width: 650px){.fs-modal.fs-modal-license-key-resend .fs-modal-body .button-container{margin-top:2px}} +a.show-license-resend-modal{margin-top:4px;display:inline-block} diff --git a/assets/css/admin/license-activation.css b/assets/css/admin/license-activation.css index cc0a23e85..6152f363c 100644 --- a/assets/css/admin/license-activation.css +++ b/assets/css/admin/license-activation.css @@ -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 .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-modal-body,.fs-modal .fs-modal-footer{border:0;background:#fefefe;padding:20px}.fs-modal .fs-modal-body{border-bottom:0}.fs-modal .fs-modal-body input.license_key{width:100%}.fs-modal .fs-modal-body p{font-size:14px}.fs-modal .fs-modal-body h2{font-size:20px}.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:not(.active){display:none}body.has-fs-modal{overflow:hidden} +.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 .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-modal-body,.fs-modal .fs-modal-footer{border:0;background:#fefefe;padding:20px}.fs-modal .fs-modal-body{border-bottom:0}.fs-modal .fs-modal-body .license-activation-message{margin:0;display:none}.fs-modal .fs-modal-body input.license_key{width:100%}.fs-modal .fs-modal-body p{font-size:14px}.fs-modal .fs-modal-body h2{font-size:20px}.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:not(.active){display:none}body.has-fs-modal{overflow:hidden} diff --git a/assets/scss/admin/_deactivation-feedback.scss b/assets/scss/admin/_deactivation-feedback.scss new file mode 100644 index 000000000..0e0807e30 --- /dev/null +++ b/assets/scss/admin/_deactivation-feedback.scss @@ -0,0 +1,42 @@ +@import "../colors"; + +.fs-modal.fs-modal-deactivation-feedback { + .reason-input, .internal-message { + margin: 3px 0 3px 22px; + + input, textarea { + width: 100%; + } + } + + li.reason { + &.has-internal-message .internal-message { + border: 1px solid lighten($darkest-color, 80%); + padding: 7px; + display: none; + } + + @media (max-width: 650px) { + li.reason { + margin-bottom: 10px; + + .reason-input, .internal-message { + margin-left: 29px; + } + + label { + display: table; + + > span { + display: table-cell; + font-size: 1.3em; + } + } + } + } + } +} + +#the-list .deactivate > .fs-slug { + display: none; +} \ No newline at end of file diff --git a/assets/scss/admin/_license-activation.scss b/assets/scss/admin/_license-activation.scss new file mode 100644 index 000000000..2eb8accee --- /dev/null +++ b/assets/scss/admin/_license-activation.scss @@ -0,0 +1,7 @@ +.fs-modal.fs-modal-license-activation { + .fs-modal-body { + input.license_key { + width: 100%; + } + } +} \ No newline at end of file diff --git a/assets/scss/admin/_license-key-resend.scss b/assets/scss/admin/_license-key-resend.scss new file mode 100644 index 000000000..1f349c3bd --- /dev/null +++ b/assets/scss/admin/_license-key-resend.scss @@ -0,0 +1,35 @@ +.fs-modal.fs-modal-license-key-resend { + .button-close { + float: right; + margin: 5px; + + &:hover { + cursor: pointer; + } + } + + .fs-modal-body { + .input-container > .email-address-container { + overflow: hidden; + padding-right: 2px; + } + + input.email-address { + width: 100%; + } + + .button-container { + float: right; + margin-left: 7px; + + @media (max-width: 650px) { + margin-top: 2px; + } + } + } +} + +a.show-license-resend-modal { + margin-top: 4px; + display: inline-block; +} diff --git a/assets/scss/admin/license-activation.scss b/assets/scss/admin/_modal-common.scss similarity index 89% rename from assets/scss/admin/license-activation.scss rename to assets/scss/admin/_modal-common.scss index e2386baa7..7438b4c4a 100644 --- a/assets/scss/admin/license-activation.scss +++ b/assets/scss/admin/_modal-common.scss @@ -1,3 +1,5 @@ +@import "../colors"; + .fs-modal { position: fixed; overflow: auto; @@ -17,14 +19,14 @@ top: -100%; z-index: 100001; width: 596px; - + @media (max-width: 650px) { margin-left: -50%; box-sizing: border-box; padding-left: 10px; padding-right: 10px; width: 100%; - + .fs-modal-panel > h3 > strong { font-size: 1.3em; } @@ -53,10 +55,6 @@ .fs-modal-body { border-bottom: 0; - input.license_key { - width: 100%; - } - p { font-size: 14px; } @@ -64,7 +62,7 @@ h2 { font-size: 20px; } - + > div { margin-top: 10px; @@ -79,7 +77,7 @@ .fs-modal-footer { border-top: #eeeeee solid 1px; text-align: right; - + > .button { margin: 0 7px; @@ -89,8 +87,15 @@ } } - .fs-modal-panel:not(.active) { - display: none; + .fs-modal-panel { + > .notice.inline { + margin: 0; + display: none; + } + + &:not(.active) { + display: none; + } } } diff --git a/assets/scss/admin/account.scss b/assets/scss/admin/account.scss index 0b5e38559..f828c5ccb 100755 --- a/assets/scss/admin/account.scss +++ b/assets/scss/admin/account.scss @@ -58,6 +58,10 @@ } } +#fs_account_details .button-group { + float: right; +} + .rtl #fs_account .fs-header-actions { left: 15px; @@ -106,12 +110,21 @@ padding: 10px; } - var, code + code { + line-height: 28px; + } + + var, code, input[type="text"] { color: #0073AA; font-size: 16px; background: none; } + + input[type="text"] { + width: 100%; + font-weight: bold; + } } label.fs-tag diff --git a/assets/scss/admin/deactivation-feedback.scss b/assets/scss/admin/deactivation-feedback.scss deleted file mode 100644 index 03792f1f3..000000000 --- a/assets/scss/admin/deactivation-feedback.scss +++ /dev/null @@ -1,120 +0,0 @@ -.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-dialog { - background: transparent; - position: absolute; - left: 50%; - margin-left: -298px; - padding-bottom: 30px; - top: -100%; - z-index: 100001; - width: 596px; - - @media (max-width: 650px) { - margin-left: -50%; - box-sizing: border-box; - padding-left: 10px; - padding-right: 10px; - width: 100%; - - .fs-modal-panel > h3 > strong { - font-size: 1.3em; - } - - li.reason { - margin-bottom: 10px; - - .reason-input { - margin-left: 29px; - } - - label { - display: table; - - > span { - display: table-cell; - font-size: 1.3em; - } - } - } - } - } - - &.active { - display: block; - - &:before { - display: block; - } - - .fs-modal-dialog { - top: 10%; - } - } - - .fs-modal-body, - .fs-modal-footer { - border: 0; - background: #fefefe; - padding: 20px; - } - - .fs-modal-body { - border-bottom: 0; - - h2 { - font-size: 20px; - } - - > div { - margin-top: 10px; - - h2 { - font-weight: bold; - font-size: 20px; - margin-top: 0; - } - } - } - - .fs-modal-footer { - border-top: #eeeeee solid 1px; - text-align: right; - - > .button { - margin: 0 7px; - - &:first-child { - margin: 0; - } - } - } - - .fs-modal-panel:not(.active) { - display: none; - } - - .reason-input { - margin: 3px 0 3px 22px; - - input, textarea { - width: 100%; - } - } -} - -body.has-fs-modal { - overflow: hidden; -} - -#the-list .deactivate > .fs-slug { - display: none; -} \ No newline at end of file diff --git a/assets/scss/admin/dialog-boxes.scss b/assets/scss/admin/dialog-boxes.scss new file mode 100644 index 000000000..2b90b2f5d --- /dev/null +++ b/assets/scss/admin/dialog-boxes.scss @@ -0,0 +1,4 @@ +@import "modal-common"; +@import "deactivation-feedback"; +@import "license-activation"; +@import "license-key-resend"; \ No newline at end of file diff --git a/includes/class-freemius.php b/includes/class-freemius.php index e963625d6..9036ae22b 100644 --- a/includes/class-freemius.php +++ b/includes/class-freemius.php @@ -444,9 +444,6 @@ private function _register_hooks() { } } - // Hook to plugin uninstall. - register_uninstall_hook( $this->_plugin_main_file_path, array( 'Freemius', '_uninstall_plugin_hook' ) ); - if ( ! $this->is_ajax() ) { if ( ! $this->is_addon() ) { add_action( 'init', array( &$this, '_add_default_submenu_items' ), WP_FS__LOWEST_PRIORITY ); @@ -464,6 +461,31 @@ private function _register_hooks() { $this->add_action( 'sdk_version_update', array( &$this, '_data_migration' ), WP_FS__DEFAULT_PRIORITY, 2 ); } + /** + * Keeping the uninstall hook registered for free or premium plugin version may result to a fatal error that + * could happen when a user tries to uninstall either version while one of them is still active. Uninstalling a + * plugin will trigger inclusion of the free or premium version and if one of them is active during the + * uninstallation, a fatal error may occur in case the plugin's class or functions are already defined. + * + * @author Leo Fajardo (leorw) + * + * @since 1.2.0 + */ + private function unregister_uninstall_hook() { + $uninstallable_plugins = (array) get_option( 'uninstall_plugins' ); + unset( $uninstallable_plugins[ $this->_free_plugin_basename ] ); + unset( $uninstallable_plugins[ $this->premium_plugin_basename() ] ); + + update_option( 'uninstall_plugins', $uninstallable_plugins ); + } + + /** + * @since 1.2.0 Invalidate module's main file cache, otherwise, FS_Plugin_Updater will not fetch updates. + */ + private function clear_module_main_file_cache() { + unset( $this->_storage->plugin_main_file ); + } + /** * @author Vova Feldman (@svovaf) * @since 1.0.9 @@ -495,6 +517,12 @@ private function _register_account_hooks() { add_action( 'admin_footer', array( &$this, '_add_deactivation_feedback_dialog_box' ) ); } } + + if ( ! $this->is_addon() ) { + if ( $this->is_registered() ) { + $this->add_filter( 'after_code_type_change', array( &$this, '_after_code_type_change' ) ); + } + } } } @@ -537,8 +565,6 @@ private function _find_caller_plugin_file() { * @since 1.1.2 */ function _add_deactivation_feedback_dialog_box() { - fs_enqueue_local_style( 'fs_deactivation_feedback', '/admin/deactivation-feedback.css' ); - /* Check the type of user: * 1. Long-term (long-term) * 2. Non-registered and non-anonymous short-term (non-registered-and-non-anonymous-short-term). @@ -582,7 +608,7 @@ function _add_deactivation_feedback_dialog_box() { /** * @todo Deactivation form core functions should be loaded only once! Otherwise, when there are multiple Freemius powered plugins the same code is loaded multiple times. The only thing that should be loaded differently is the various deactivation reasons object based on the state of the plugin. */ - fs_require_template( 'deactivation-feedback-modal.php', $vars ); + fs_require_template( 'forms/deactivation/form.php', $vars ); } /** @@ -594,6 +620,16 @@ function _add_deactivation_feedback_dialog_box() { * @return array The uninstall reasons for the specified user type. */ function _get_uninstall_reasons( $user_type = 'long-term' ) { + $internal_message_template_var = array( + 'slug' => $this->_slug + ); + + if ( $this->is_registered() && false !== $this->get_plan() && $this->get_plan()->has_technical_support() ) { + $contact_support_template = fs_get_template( 'forms/deactivation/contact.php', $internal_message_template_var ); + } else { + $contact_support_template = ''; + } + $reason_found_better_plugin = array( 'id' => 2, 'text' => __fs( 'reason-found-a-better-plugin', $this->_slug ), @@ -633,13 +669,15 @@ function _get_uninstall_reasons( $user_type = 'long-term' ) { 'id' => 4, 'text' => __fs( 'reason-broke-my-site', $this->_slug ), 'input_type' => '', - 'input_placeholder' => '' + 'input_placeholder' => '', + 'internal_message' => $contact_support_template ), array( 'id' => 5, 'text' => __fs( 'reason-suddenly-stopped-working', $this->_slug ), 'input_type' => '', - 'input_placeholder' => '' + 'input_placeholder' => '', + 'internal_message' => $contact_support_template ) ); @@ -652,6 +690,24 @@ function _get_uninstall_reasons( $user_type = 'long-term' ) { ); } + $reason_dont_share_info = array( + 'id' => 9, + 'text' => __fs( 'reason-dont-like-to-share-my-information', $this->_slug ), + 'input_type' => '', + 'input_placeholder' => '' + ); + + /** + * If the current user has selected the "don't share data" reason in the deactivation feedback modal, inform the + * user by showing additional message that he doesn't have to share data and can just choose to skip the opt-in + * (the Skip button is included in the message to show). This message will only be shown if anonymous mode is + * enabled and the user's account is currently not in pending activation state (similar to the way the Skip + * button in the opt-in form is shown/hidden). + */ + if ( $this->is_enable_anonymous() && ! $this->is_pending_activation() ) { + $reason_dont_share_info['internal_message'] = fs_get_template( 'forms/deactivation/retry-skip.php', $internal_message_template_var ); + } + $long_term_user_reasons[] = $reason_temporary_deactivation; $long_term_user_reasons[] = $reason_other; @@ -664,12 +720,7 @@ function _get_uninstall_reasons( $user_type = 'long-term' ) { 'input_type' => '', 'input_placeholder' => '' ), - array( - 'id' => 9, - 'text' => __fs( 'reason-dont-like-to-share-my-information', $this->_slug ), - 'input_type' => '', - 'input_placeholder' => '' - ), + $reason_dont_share_info, $reason_found_better_plugin, $reason_temporary_deactivation, $reason_other @@ -679,7 +730,8 @@ function _get_uninstall_reasons( $user_type = 'long-term' ) { 'id' => 10, 'text' => __fs( 'reason-couldnt-make-it-work', $this->_slug ), 'input_type' => '', - 'input_placeholder' => '' + 'input_placeholder' => '', + 'internal_message' => $contact_support_template ), $reason_found_better_plugin, array( @@ -1108,6 +1160,7 @@ static function _debug_page_render() { $users = self::get_all_users(); $addons = self::get_all_addons(); $account_addons = self::get_all_account_addons(); + $licenses = self::get_all_licenses(); // $plans = self::get_all_plans(); // $licenses = self::get_all_licenses(); @@ -1117,6 +1170,7 @@ static function _debug_page_render() { 'users' => $users, 'addons' => $addons, 'account_addons' => $account_addons, + 'licenses' => $licenses, ); fs_enqueue_local_style( 'fs_account', '/admin/debug.css' ); @@ -2077,23 +2131,25 @@ function dynamic_init( array $plugin_info ) { $this->do_action( 'initiated' ); + if ( $this->_storage->prev_is_premium !== $this->_plugin->is_premium ) { + if ( isset( $this->_storage->prev_is_premium ) ) { + $this->apply_filters( + 'after_code_type_change', + // New code type. + $this->_plugin->is_premium + ); + } else { + // Set for code type for the first time. + $this->_storage->prev_is_premium = $this->_plugin->is_premium; + } + } + if ( ! $this->is_addon() ) { if ( $this->is_registered() ) { // Fix for upgrade from versions < 1.0.9. if ( ! isset( $this->_storage->activation_timestamp ) ) { $this->_storage->activation_timestamp = WP_FS__SCRIPT_START_TIME; } - if ( $this->_storage->prev_is_premium !== $this->_plugin->is_premium ) { - if ( isset( $this->_storage->prev_is_premium ) ) { - add_action( is_admin() ? 'admin_init' : 'init', array( - &$this, - '_plugin_code_type_changed' - ) ); - } else { - // Set for code type for the first time. - $this->_storage->prev_is_premium = $this->_plugin->is_premium; - } - } $this->do_action( 'after_init_plugin_registered' ); } else if ( $this->is_anonymous() ) { @@ -2113,14 +2169,23 @@ function dynamic_init( array $plugin_info ) { // Add license activation link and AJAX request handler. if ( $this->has_paid_plan() ) { - $this->_add_license_action_link(); - global $pagenow; if ( 'plugins.php' === $pagenow ) { - add_action( 'admin_footer', array( &$this, '_add_license_activation_dialog_box' ) ); + /** + * @since 1.2.0 Add license action link only on plugins page. + */ + $this->_add_license_action_link(); + $this->_require_license_activation_dialog(); } - add_action( 'wp_ajax_activate-license', array( &$this, '_activate_license_ajax_action' ) ); + if ( $this->is_ajax_action( array( + 'activate_license', + 'resend_license_key' + ) ) + ) { + // Hook license activation and resend AJAX callbacks. + $this->_require_license_activation_dialog(); + } } } @@ -2311,6 +2376,21 @@ private function should_stop_execution() { return false; } + /** + * Triggered after code type has changed. + * + * @author Vova Feldman (@svovaf) + * @since 1.1.9.1 + */ + function _after_code_type_change() { + $this->_logger->entrance(); + + add_action( is_admin() ? 'admin_init' : 'init', array( + &$this, + '_plugin_code_type_changed' + ) ); + } + /** * Handles plugin's code type change (free <--> premium). * @@ -2318,6 +2398,8 @@ private function should_stop_execution() { * @since 1.0.9 */ function _plugin_code_type_changed() { + $this->_logger->entrance(); + // Schedule code type changes event. // $this->sync_install(); $this->schedule_install_sync(); @@ -2357,6 +2439,19 @@ function _plugin_code_type_changed() { } } + /** + * Unregister the uninstall hook for the other version of the plugin (with different code type) to avoid + * triggering a fatal error when uninstalling that plugin. For example, after deactivating the "free" version + * of a specific plugin, its uninstall hook should be unregistered after the "premium" version has been + * activated. If we don't do that, a fatal error will occur when we try to uninstall the "free" version since + * the main file of the "free" version will be loaded first before calling the hooked callback. Since the + * free and premium versions are almost identical (same class or have same functions), a fatal error like + * "Cannot redeclare class MyClass" or "Cannot redeclare my_function()" will occur. + */ + $this->unregister_uninstall_hook(); + + $this->clear_module_main_file_cache(); + // Update is_premium of latest version. $this->_storage->prev_is_premium = $this->_plugin->is_premium; } @@ -2740,6 +2835,8 @@ function _sync_cron() { // Sync add-ons collection. $this->_sync_addons( true ); } + + $this->do_action( 'after_sync_cron' ); } /** @@ -3302,6 +3399,8 @@ function _activate_plugin_event_hook() { return; } + $this->unregister_uninstall_hook(); + // Clear API cache on activation. FS_Api::clear_cache(); @@ -3445,6 +3544,10 @@ function _deactivate_plugin_hook() { $this->_storage->is_plugin_new_install = false; } + // Hook to plugin uninstall. + register_uninstall_hook( $this->_plugin_main_file_path, array( 'Freemius', '_uninstall_plugin_hook' ) ); + + $this->clear_module_main_file_cache(); $this->clear_sync_cron(); $this->clear_install_sync_cron(); @@ -4087,7 +4190,17 @@ function get_plugin_data() { if ( ! isset( $this->_plugin_data ) ) { self::require_plugin_essentials(); - $this->_plugin_data = get_plugin_data( $this->_plugin_main_file_path ); + /** + * @author Vova Feldman (@svovaf) + * @since 1.2.0 When using get_plugin_data() do NOT translate plugin data. + * + * @link https://github.com/Freemius/wordpress-sdk/issues/77 + */ + $this->_plugin_data = get_plugin_data( + $this->_plugin_main_file_path, + false, + false + ); } return $this->_plugin_data; @@ -4825,10 +4938,12 @@ private function get_plan_by_name( $name ) { * @author Vova Feldman (@svovaf) * @since 1.0.6 * + * @param number|bool $site_license_id + * * @return FS_Plugin_License[]|object */ - function _sync_licenses() { - $licenses = $this->_fetch_licenses(); + function _sync_licenses( $site_license_id = false ) { + $licenses = $this->_fetch_licenses( false, $site_license_id ); if ( ! $this->is_api_error( $licenses ) ) { $this->_licenses = $licenses; $this->_store_licenses(); @@ -4857,7 +4972,7 @@ function _get_license_by_id( $id ) { return false; } - if ( ! is_array( $this->_licenses ) || 0 === count( $this->_licenses ) ) { + if ( ! $this->has_any_license() ) { $this->_sync_licenses(); } @@ -5101,13 +5216,46 @@ function has_free_plan() { * @since 1.1.9 */ function _add_license_activation_dialog_box() { - fs_enqueue_local_style( 'fs_license_action', '/admin/license-activation.css' ); + if ( $this->is_addon() ) { + $sync_license_url = $this->get_parent_instance()->_get_sync_license_url( $this->_plugin->id, true ); + } else { + $sync_license_url = $this->_get_sync_license_url( $this->_plugin->id, true ); + } $vars = array( - 'slug' => $this->_slug + 'slug' => $this->_slug, + // Avoid having HTML entity like "&" in the URL which breaks the redirection to the "Account" page. + 'sync-license-url' => html_entity_decode( $sync_license_url ) ); - fs_require_template( 'license-activation-modal.php', $vars ); + fs_require_template( 'forms/license-activation.php', $vars ); + fs_require_template( 'forms/resend-key.php', $vars ); + } + + /** + * Prepare page to include all required UI and logic for the license activation dialog. + * + * @author Vova Feldman (@svovaf) + * @since 1.2.0 + */ + function _require_license_activation_dialog() { + if ( $this->is_ajax() ) { + if ( fs_request_is_action( 'activate_license' ) ) { + // Add license activation AJAX callback. + add_action( 'wp_ajax_activate_license', array( &$this, '_activate_license_ajax_action' ) ); + } + + if ( fs_request_is_action( 'resend_license_key' ) ) { + // Add resend license AJAX callback. + add_action( 'wp_ajax_resend_license_key', array( + &$this, + '_resend_license_key_ajax_action' + ) ); + } + } else { + // Inject license activation dialog UI and client side code. + add_action( 'admin_footer', array( &$this, '_add_license_activation_dialog_box' ) ); + } } /** @@ -5115,28 +5263,90 @@ function _add_license_activation_dialog_box() { * @since 1.1.9 */ function _activate_license_ajax_action() { - if ( ! isset( $_POST['license-key'] ) ) { - exit; - } + $license_key = trim( fs_request_get('license_key') ); - $license_key = trim( $_POST['license-key'] ); if ( empty( $license_key ) ) { exit; } + $slug = $_POST['slug']; + $fs = ( ( $slug === $this->_slug ) ? $this : self::instance( $slug ) ); + $error = false; + if ( $this->is_registered() ) { - $api = $this->get_api_site_scope(); - $api->call( '/', 'put', + $api = $fs->get_api_site_scope(); + $install = $api->call( '/', 'put', array( 'license_key' => $license_key ) ); + + if ( isset( $install->error ) ) { + $error = $install->error->message; + } } else { $this->opt_in( false, false, false, $license_key ); } - // Print '1' for successful operation. - echo 1; + $result = array( + 'success' => ( false === $error ) + ); + + if ( false !== $error ) { + $result['error'] = $error; + } + + echo json_encode( $result ); + + exit; + } + + /** + * @author Leo Fajardo (@leorw) + * @since 1.2.0 + */ + function _resend_license_key_ajax_action() { + if ( ! isset( $_POST['email'] ) ) { + exit; + } + + $email_address = trim( $_POST['email'] ); + if ( empty( $email_address ) ) { + exit; + } + + $error = false; + + $api = $this->get_api_plugin_scope(); + $result = $api->call( '/licenses/resend.json', 'post', + array( + 'email' => $email_address, + 'is_localhost' => WP_FS__IS_LOCALHOST + ) + ); + + if ( is_object( $result ) && isset( $result->error ) ) { + $error = $result->error; + + if ( in_array( $error->code, array( 'invalid_email', 'no_user' ) ) ) { + $error = __fs( 'email-not-found' ); + } else if ( 'no_license' === $error->code ) { + $error = __fs( 'no-active-licenses' ); + } else { + $error = $error->message; + } + } + + $licenses = array( + 'success' => ( false === $error ) + ); + + if ( false !== $error ) { + $licenses['error'] = sprintf( '%s... %s', __fs( 'oops', $this->_slug ), strtolower( $error ) ); + } + + echo json_encode( $licenses ); + exit; } @@ -5354,6 +5564,45 @@ function is_ajax() { return ( defined( 'DOING_AJAX' ) && DOING_AJAX ); } + /** + * Check if it's an AJAX call targeted for the current module. + * + * @author Vova Feldman (@svovaf) + * @since 1.2.0 + * + * @param array|string $actions Collection of AJAX actions. + * + * @return bool + */ + function is_ajax_action( $actions ) { + // Verify it's an ajax call. + if ( ! $this->is_ajax() ) { + return false; + } + + // Verify the call is relevant for the plugin. + if ( $this->_slug !== fs_request_get( 'slug' ) ) { + return false; + } + + // Verify it's one of the specified actions. + if ( is_string( $actions ) ) { + $actions = explode( ',', $actions ); + } + + if ( is_array( $actions ) && 0 < count( $actions ) ) { + $ajax_action = fs_request_get( 'action' ); + + foreach ( $actions as $action ) { + if ( $ajax_action === $action ) { + return true; + } + } + } + + return false; + } + /** * @author Vova Feldman (@svovaf) * @since 1.1.7 @@ -5430,6 +5679,30 @@ function _get_admin_page_url( $page = '', $params = array() ) { } } + /** + * Plugin's account page + sync license URL. + * + * @author Vova Feldman (@svovaf) + * @since 1.1.9.1 + * + * @param bool|number $plugin_id + * @param bool $add_action_nonce + * + * @return string + */ + function _get_sync_license_url( $plugin_id = false, $add_action_nonce = true ) { + $params = array(); + + if ( is_numeric( $plugin_id ) ) { + $params['plugin_id'] = $plugin_id; + } + + return $this->get_account_url( + $this->_slug . '_sync_license', + $params, + $add_action_nonce + ); + } /** * Plugin's account URL. @@ -5456,6 +5729,25 @@ function get_account_url( $action = false, $params = array(), $add_action_nonce $this->_get_admin_page_url( 'account', $params ); } + /** + * @author Vova Feldman (@svovaf) + * @since 1.2.0 + * + * @param string $tab + * @param bool $action + * @param array $params + * @param bool $add_action_nonce + * + * @return string + * + * @uses get_account_url() + */ + function get_account_tab_url( $tab, $action = false, $params = array(), $add_action_nonce = true ) { + $params['tab'] = $tab; + + return $this->get_account_url( $action, $params, $add_action_nonce ); + } + /** * Plugin's account URL. * @@ -5821,7 +6113,7 @@ function get_opt_in_params( $override_with = array() ) { * @param string|bool $email * @param string|bool $first * @param string|bool $last - * @param string|bool $license_key + * @param string|bool $license_secret_key * * @return bool Is successful opt-in (or set to pending). */ @@ -6000,13 +6292,7 @@ function setup_account( FS_User $user, FS_Site $site, $redirect = true ) { if ( is_numeric( $plugin_id ) ) { if ( $plugin_id != $this->_plugin->id ) { // Add-on was installed - sync license right after install. - if ( $redirect && fs_redirect( fs_nonce_url( $this->_get_admin_page_url( - 'account', - array( - 'fs_action' => $this->_slug . '_sync_license', - 'plugin_id' => $plugin_id - ) - ), $this->_slug . '_sync_license' ) ) + if ( $redirect && fs_redirect( $this->_get_sync_license_url( $plugin_id ) ) ) { exit(); } @@ -6291,6 +6577,8 @@ function _prepare_admin_menu() { if ( ! $this->has_api_connectivity() && ! $this->is_enable_anonymous() ) { $this->_menu->remove_menu_item(); } else { + $this->do_action( 'before_admin_menu_init' ); + $this->add_menu_action(); $this->add_submenu_items(); } @@ -6445,8 +6733,6 @@ private function get_top_level_menu_slug() { private function add_submenu_items() { $this->_logger->entrance(); - $this->do_action( 'before_admin_menu_init' ); - if ( ! $this->is_addon() ) { if ( ! $this->is_activation_mode() ) { if ( $this->is_registered() ) { @@ -7292,10 +7578,11 @@ private function _fetch_plugin_plans() { * @uses FS_Api * * @param number|bool $plugin_id + * @param number|bool $site_license_id * * @return FS_Plugin_License[]|object */ - private function _fetch_licenses( $plugin_id = false ) { + private function _fetch_licenses( $plugin_id = false, $site_license_id = false ) { $this->_logger->entrance(); $api = $this->get_api_user_scope(); @@ -7306,14 +7593,75 @@ private function _fetch_licenses( $plugin_id = false ) { $result = $api->get( "/plugins/{$plugin_id}/licenses.json", true ); + $is_site_license_synced = false; + if ( ! isset( $result->error ) ) { for ( $i = 0, $len = count( $result->licenses ); $i < $len; $i ++ ) { $result->licenses[ $i ] = new FS_Plugin_License( $result->licenses[ $i ] ); + + if ( ( ! $is_site_license_synced ) && is_numeric( $site_license_id ) ) { + $is_site_license_synced = ( $site_license_id == $result->licenses[ $i ]->id ); + } } $result = $result->licenses; } + if ( ! $is_site_license_synced ) { + $api = $this->get_api_site_scope(); + + if ( is_numeric( $site_license_id ) ) { + // Try to retrieve a foreign license that is linked to the install. + $api_result = $api->call( '/licenses.json' ); + + if ( ! isset( $api_result->error ) ) { + $licenses = $api_result->licenses; + + if ( ! empty( $licenses ) ) { + $result[] = new FS_Plugin_License( $licenses[0] ); + } + } + } else if ( is_object( $this->_license ) ) { + // Fetch foreign license by ID and license key. + $license = $api->get( "/licenses/{$this->_license->id}.json?license_key=" . + urlencode( $this->_license->secret_key ) ); + + if ( ! isset( $license->error ) ) { + $result[] = new FS_Plugin_License( $license ); + } + } + } + + return $result; + } + + /** + * @author Vova Feldman (@svovaf) + * @since 1.2.0 + * @uses FS_Api + * + * @param number|bool $plugin_id + * + * @return FS_Payment[]|object + */ + function _fetch_payments( $plugin_id = false ) { + $this->_logger->entrance(); + + $api = $this->get_api_user_scope(); + + if ( ! is_numeric( $plugin_id ) ) { + $plugin_id = $this->_plugin->id; + } + + $result = $api->get( "/plugins/{$plugin_id}/payments.json", true ); + + if ( ! isset( $result->error ) ) { + for ( $i = 0, $len = count( $result->payments ); $i < $len; $i ++ ) { + $result->payments[ $i ] = new FS_Payment( $result->payments[ $i ] ); + } + $result = $result->payments; + } + return $result; } @@ -7623,8 +7971,11 @@ private function _sync_plugin_license( $background = false ) { $this->_enrich_site_plan( true ); $this->_store_site(); } else { - // Sync licenses. - $this->_sync_licenses(); + /** + * Sync licenses. Pass the site's license ID so that the foreign licenses will be fetched if the license + * associated with that ID is not included in the user's licenses collection. + */ + $this->_sync_licenses( $site->license_id ); // Check if plan / license changed. if ( ! FS_Entity::equals( $site->plan, $this->_site->plan ) || @@ -7670,7 +8021,7 @@ private function _sync_plugin_license( $background = false ) { null : $this->_get_license_by_id( $site->license_id ); - if ( $is_free && is_null( $new_license ) && $this->has_license() && $this->_license->is_cancelled ) { + if ( $is_free && is_null( $new_license ) && $this->has_any_license() && $this->_license->is_cancelled ) { // License cancelled. $this->_site = $site; $this->_update_site_license( $new_license ); @@ -7882,8 +8233,14 @@ protected function _activate_license( $background = false ) { return; } + if ( $this->_site->user_id != $premium_license->user_id ) { + $api_request_params = array( 'license_key' => $premium_license->secret_key ); + } else { + $api_request_params = array(); + } + $api = $this->get_api_site_scope(); - $license = $api->call( "/licenses/{$premium_license->id}.json", 'put' ); + $license = $api->call( "/licenses/{$premium_license->id}.json", 'put', $api_request_params ); if ( $this->is_api_error( $license ) ) { if ( ! $background ) { @@ -8385,6 +8742,24 @@ private function _get_latest_download_api_url( $plugin_id = false ) { ); } + /** + * Get payment invoice URL. + * + * @author Vova Feldman (@svovaf) + * @since 1.2.0 + * + * @param bool|number $payment_id + * + * @return string + */ + function _get_invoice_api_url( $payment_id = false ) { + $this->_logger->entrance(); + + return $this->get_api_user_scope()->get_signed_url( + "/payments/{$payment_id}/invoice.pdf" + ); + } + /** * Get latest plugin download link. * @@ -8984,7 +9359,11 @@ function _account_page_render() { $this->_logger->entrance(); $vars = array( 'slug' => $this->_slug ); - fs_require_once_template( 'account.php', $vars ); + if ( 'billing' === fs_request_get( 'tab' ) ) { + fs_require_once_template( 'billing.php', $vars ); + } else { + fs_require_once_template( 'account.php', $vars ); + } } /** @@ -9431,7 +9810,14 @@ function _add_upgrade_action_link() { function _add_license_action_link() { $this->_logger->entrance(); - $link_text = __fs( $this->is_free_plan() ? 'activate-license' : 'change-license', $this->_slug ); + if ( $this->is_free_plan() && $this->is_addon() ) { + return; + } + + $link_text = __fs( + $this->is_free_plan() ? 'activate-license' : 'change-license', + $this->_slug + ); $this->add_plugin_action_link( $link_text, diff --git a/includes/entities/class-fs-payment.php b/includes/entities/class-fs-payment.php new file mode 100755 index 000000000..e3d61868d --- /dev/null +++ b/includes/entities/class-fs-payment.php @@ -0,0 +1,94 @@ +bound_payment_id ) && 0 > $this->gross ); + } + } \ No newline at end of file diff --git a/includes/entities/class-fs-plugin-license.php b/includes/entities/class-fs-plugin-license.php index 655605f34..02cafcb2b 100755 --- a/includes/entities/class-fs-plugin-license.php +++ b/includes/entities/class-fs-plugin-license.php @@ -46,6 +46,10 @@ class FS_Plugin_License extends FS_Entity { * @var string */ public $expiration; + /** + * @var string + */ + public $secret_key; /** * @var bool $is_free_localhost Defaults to true. If true, allow unlimited localhost installs with the same * license. @@ -87,6 +91,10 @@ function left() { return 0; } + if ( $this->is_unlimited() ) { + return 999; + } + return ( $this->quota - $this->activated - ( $this->is_free_localhost ? 0 : $this->activated_local ) ); } @@ -122,6 +130,16 @@ function is_lifetime() { return is_null( $this->expiration ); } + /** + * @author Vova Feldman (@svovaf) + * @since 1.2.0 + * + * @return bool + */ + function is_unlimited() { + return is_null( $this->quota ); + } + /** * Check if license is fully utilized. * @@ -137,6 +155,10 @@ function is_utilized( $is_localhost = null ) { $is_localhost = WP_FS__IS_LOCALHOST_FOR_SERVER; } + if ( $this->is_unlimited() ) { + return false; + } + return ! ( $this->is_free_localhost && $is_localhost ) && ( $this->quota <= $this->activated + ( $this->is_free_localhost ? 0 : $this->activated_local ) ); } diff --git a/includes/entities/class-fs-plugin-plan.php b/includes/entities/class-fs-plugin-plan.php index 1df50eb2a..9161d5746 100755 --- a/includes/entities/class-fs-plugin-plan.php +++ b/includes/entities/class-fs-plugin-plan.php @@ -111,6 +111,22 @@ function is_free() { return ( 'free' === $this->name ); } + /** + * Checks if this plan supports "Technical Support". + * + * @author Leo Fajardo (leorw) + * @since 1.2.0 + * + * @return bool + */ + function has_technical_support() { + return ( ! empty( $this->support_email ) || + ! empty( $this->support_skype ) || + ! empty( $this->support_phone ) || + ! empty( $this->is_success_manager ) + ); + } + /** * @author Vova Feldman (@svovaf) * @since 1.0.9 diff --git a/includes/i18n.php b/includes/i18n.php index 063236d7f..0e543d6ae 100644 --- a/includes/i18n.php +++ b/includes/i18n.php @@ -35,6 +35,7 @@ 'account' => __( 'Account', 'freemius' ), 'addon' => __( 'Add On', 'freemius' ), 'contact-us' => __( 'Contact Us', 'freemius' ), + 'contact-support' => __( 'Contact Support', 'freemius' ), 'change-ownership' => __( 'Change Ownership', 'freemius' ), 'support' => __( 'Support', 'freemius' ), 'support-forum' => __( 'Support Forum', 'freemius' ), @@ -53,6 +54,14 @@ 'details' => __( 'Details', 'freemius' ), 'account-details' => __( 'Account Details', 'freemius' ), 'delete' => _x( 'Delete', 'verb', 'freemius' ), + 'show' => _x( 'Show', 'verb', 'freemius' ), + 'hide' => _x( 'Hide', 'verb', 'freemius' ), + 'edit' => _x( 'Edit', 'verb', 'freemius' ), + 'date' => __( 'Date', 'freemius' ), + 'amount' => __( 'Amount', 'freemius' ), + 'invoice' => __( 'Invoice', 'freemius' ), + 'billing' => __( 'Billing', 'freemius' ), + 'payments' => __( 'Payments', 'freemius' ), 'delete-account' => __( 'Delete Account', 'freemius' ), 'dismiss' => _x( 'Dismiss', 'as close a window', 'freemius' ), 'plan' => _x( 'Plan', 'as product pricing plan', 'freemius' ), @@ -72,6 +81,7 @@ 'license-unlimited' => __( 'Unlimited Licenses', 'freemius' ), 'license-x-sites' => __( 'Up to %s Sites', 'freemius' ), 'renew-license-now' => __( '%sRenew your license now%s to access version %s features and support.', 'freemius' ), + 'ask-for-upgrade-email-address' => __( "Enter the email address you've used for the upgrade below and we will resend you the license key.", 'freemius' ), 'x-plan' => _x( '%s Plan', 'e.g. Professional Plan', 'freemius' ), 'you-are-step-away' => __( 'You are just one step away - %s', 'freemius' ), 'activate-x-now' => _x( 'Complete "%s" Activation Now', '%s - plugin name. As complete "Jetpack" activation now', 'freemius' ), @@ -95,6 +105,7 @@ 'version' => _x( 'Version', 'as plugin version', 'freemius' ), 'name' => __( 'Name', 'freemius' ), 'email' => __( 'Email', 'freemius' ), + 'email-address' => __( 'Email address', 'freemius' ), 'verified' => __( 'Verified', 'freemius' ), 'plugin' => __( 'Plugin', 'freemius' ), 'plugins' => __( 'Plugins', 'freemius' ), @@ -178,6 +189,7 @@ 'placeholder-what-did-you-expect' => __( "What did you expect?", 'freemius' ), 'reason-didnt-work' => __( "The plugin didn't work", 'freemius' ), 'reason-dont-like-to-share-my-information' => __( "I don't like to share my information with you", 'freemius' ), + 'dont-have-to-share-any-data' => __( "You might have missed it, but you don't have to share any data and can just %s the opt-in.", 'freemius' ), #endregion Plugin Deactivation #region Connect @@ -206,10 +218,16 @@ 'opt-in-connect' => _x( 'Allow & Continue', 'button label', 'freemius' ), 'agree-activate-license' => _x( 'Agree & Activate License', 'button label', 'freemius' ), 'skip' => _x( 'Skip', 'verb', 'freemius' ), + 'click-here-to-use-plugin-anonymously' => __( 'Click here to use the plugin anonymously', 'freemius' ), 'resend-activation-email' => __( 'Re-send activation email', 'freemius' ), 'license-key' => __( 'License key', 'freemius' ), + 'send-license-key' => __( 'Send License Key', 'freemius' ), + 'sending-license-key' => __( 'Sending license key', 'freemius' ), 'have-license-key' => __( 'Have a license key?', 'freemius' ), 'dont-have-license-key' => __( 'Don\'t have a license key?', 'freemius' ), + 'cant-find-license-key' => __( "Can't find your license key?", 'freemius' ), + 'email-not-found' => __( "We couldn't find your email address in the system, are you sure it's the right address?" ), + 'no-active-licenses' => __( "We can't see any active licenses associated with that email address, are you sure it's the right address?" ), #endregion Connect #region Screenshots @@ -324,6 +342,7 @@ 'curl-missing-no-clue-desc' => __( 'We\'ll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update.', 'freemius' ), 'curl-missing-sysadmin-desc' => __( 'Great, please install cURL and enable it in your php.ini file. To make sure it was successfully activated, use \'phpinfo()\'. Once activated, deactivate the plugin and reactivate it back again.', 'freemius' ), 'happy-to-resolve-issue-asap' => __( '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.', 'freemius' ), + 'contact-support-before-deactivation' => __( 'Sorry for the inconvenience and we are here to help if you give us a chance.', 'freemius' ), 'fix-issue-title' => __( 'Yes - I\'m giving you a chance to fix it', 'freemius' ), 'fix-issue-desc' => __( '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.', 'freemius' ), 'install-previous-title' => __( 'Let\'s try your previous version', 'freemius' ), diff --git a/includes/managers/class-fs-key-value-storage.php b/includes/managers/class-fs-key-value-storage.php index 17f8d15f4..1dd6c2343 100755 --- a/includes/managers/class-fs-key-value-storage.php +++ b/includes/managers/class-fs-key-value-storage.php @@ -10,6 +10,37 @@ exit; } + /** + * Class FS_Key_Value_Storage + * + * @property int $install_timestamp + * @property int $activation_timestamp + * @property int $sync_timestamp + * @property object $sync_cron + * @property int $install_sync_timestamp + * @property array $connectivity_test + * @property array $is_on + * @property object $trial_plan + * @property bool $has_trial_plan + * @property bool $trial_promotion_shown + * @property string $sdk_version + * @property string $sdk_last_version + * @property bool $sdk_upgrade_mode + * @property bool $sdk_downgrade_mode + * @property bool $plugin_upgrade_mode + * @property bool $plugin_downgrade_mode + * @property string $plugin_version + * @property string $plugin_last_version + * @property bool $is_plugin_new_install + * @property bool $was_plugin_loaded + * @property object $plugin_main_file + * @property bool $prev_is_premium + * @property array $is_anonymous + * @property bool $is_pending_activation + * @property bool $sticky_optin_added + * @property object $uninstall_reason + * @property object $subscription + */ class FS_Key_Value_Storage implements ArrayAccess, Iterator, Countable { /** * @var string diff --git a/require.php b/require.php new file mode 100644 index 000000000..082cd9304 --- /dev/null +++ b/require.php @@ -0,0 +1,43 @@ +get_plan(); $is_active_subscription = ( is_object( $subscription ) && $subscription->is_active() ); $is_paid_trial = $fs->is_paid_trial(); - $show_upgrade = ( ! $is_paying && ! $is_paid_trial ); -?> + $show_upgrade = ( $fs->has_paid_plan() && ! $is_paying && ! $is_paid_trial ); -
+ | + | + | + | + | + | + | + | + |
---|---|---|---|---|---|---|---|---|
id ?> | +plugin_id ?> | +user_id ?> | +plan_id ?> | +is_unlimited() ? 'Unlimited' : ( $license->is_single_site() ? 'Single Site' : $license->quota ) ?> | +activated ?> | +is_block_features ? 'Blocking' : 'Flexible' ?> | +secret_key ) ?> | +expiration ?> | +