diff --git a/assets/css/admin/wlffwc-admin-main.css b/assets/css/admin/wlffwc-admin-main.css new file mode 100755 index 0000000..24c39f0 --- /dev/null +++ b/assets/css/admin/wlffwc-admin-main.css @@ -0,0 +1 @@ +#poststuffIE{background-color:#fff;padding:20px}#wlffwc_global_settings .hr{border-top:solid 1px #ccc;margin:10px 0}.#wlffwc_global_settings input{width:400px} \ No newline at end of file diff --git a/assets/css/dev/admin/wlffwc-admin-main.css b/assets/css/dev/admin/wlffwc-admin-main.css new file mode 100755 index 0000000..d2f9a52 --- /dev/null +++ b/assets/css/dev/admin/wlffwc-admin-main.css @@ -0,0 +1,11 @@ +#poststuffIE { + background-color: #fff; + padding: 20px; +} +#wlffwc_global_settings .hr { + border-top: solid 1px #ccc; + margin: 10px 0; +} +.#wlffwc_global_settings input { + width: 400px; +} \ No newline at end of file diff --git a/assets/css/dev/public/wlffwc-wishlist.css b/assets/css/dev/public/wlffwc-wishlist.css new file mode 100644 index 0000000..53b74d8 --- /dev/null +++ b/assets/css/dev/public/wlffwc-wishlist.css @@ -0,0 +1,101 @@ +/* Hide page title of shortcode page. */ +#main .entry-title { + display: none; +} +/* However, do not hide title of wc account page as we are using shortcode there too. */ +.woocommerce-account #main .entry-title { + display: inherit !important; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl { + border-collapse: collapse; + text-align: left; + width: 100%; + font-size: 0.8rem; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-th { + background: #eee; + color: dimgrey; + display: none; + font-weight: bold; + padding: 5px; + position: absolute; + text-transform: uppercase; + top: 0; + left: 0; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-thumbnail img { + max-width: 50px; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .price-increase { + font-size: 0.75rem; + color: #bb5e5e; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .price-decrease { + font-size: 0.75rem; + color: #306f35; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .date-added { + display: block; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .add_to_cart_inline { + display: inline-block; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .add_to_cart_inline a { + padding: 0.5em 2em; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .remove-from-wishlist { + margin-left: 5px; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .woocommerce-message { + display: none !important; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-display { + display: none; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .no-data-found-row td { + text-align: center; +} +#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .no-data-found-row.hide { + display: none; +} +/* Hide wishlist shortcode title on wc my account page */ +.woocommerce-MyAccount-content #wlffwc-wishlist-wrapper h2 { + display: none; +} +/* Simple CSS for flexbox table on mobile */ +@media(max-width: 800px) { + #wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl thead { + left: -9999px; + position: absolute; + visibility: hidden; + } + #wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl tr { + border-bottom: 0; + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin-bottom: 40px; + } + #wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl td { + border: 1px solid #ccc; + margin: 0 -1px -1px 0; + padding-top: 35px; + padding-bottom: 0.875em; + position: relative; + width: 50%; + text-align: center; + } + #wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .no-data-found-row td { + padding-top: 0.875em; + border: none; + } + #wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-th { + display: block; + } + #wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-display { + display: inherit; + } + #wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .not-mobile-display { + display: none; + } +} \ No newline at end of file diff --git a/assets/css/public/wlffwc-wishlist.css b/assets/css/public/wlffwc-wishlist.css new file mode 100755 index 0000000..9b73f17 --- /dev/null +++ b/assets/css/public/wlffwc-wishlist.css @@ -0,0 +1 @@ +#main .entry-title{display:none}.woocommerce-account #main .entry-title{display:inherit!important}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl{border-collapse:collapse;text-align:left;width:100%;font-size:.8rem}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-th{background:#eee;color:#696969;display:none;font-weight:700;padding:5px;position:absolute;text-transform:uppercase;top:0;left:0}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-thumbnail img{max-width:50px}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .price-increase{font-size:.75rem;color:#bb5e5e}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .price-decrease{font-size:.75rem;color:#306f35}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .date-added{display:block}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .add_to_cart_inline{display:inline-block}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .add_to_cart_inline a{padding:.5em 2em}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .remove-from-wishlist{margin-left:5px}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .product-actions .woocommerce-message{display:none!important}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-display{display:none}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .no-data-found-row td{text-align:center}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .no-data-found-row.hide{display:none}.woocommerce-MyAccount-content #wlffwc-wishlist-wrapper h2{display:none}@media(max-width:800px){#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl thead{left:-9999px;position:absolute;visibility:hidden}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl tr{border-bottom:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-bottom:40px}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl td{border:1px solid #ccc;margin:0 -1px -1px 0;padding-top:35px;padding-bottom:.875em;position:relative;width:50%;text-align:center}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .no-data-found-row td{padding-top:.875em;border:none}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-th{display:block}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .mobile-display{display:inherit}#wlffwc-wishlist-wrapper .wlffwc-wishlist-tbl .not-mobile-display{display:none}} \ No newline at end of file diff --git a/assets/images/ajax-loader.svg b/assets/images/ajax-loader.svg new file mode 100755 index 0000000..f28cffe --- /dev/null +++ b/assets/images/ajax-loader.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/wishlist-icon.png b/assets/images/wishlist-icon.png new file mode 100644 index 0000000..8cfcfff Binary files /dev/null and b/assets/images/wishlist-icon.png differ diff --git a/assets/js/dev/public/wlffwc-wishlist.js b/assets/js/dev/public/wlffwc-wishlist.js new file mode 100644 index 0000000..5d59701 --- /dev/null +++ b/assets/js/dev/public/wlffwc-wishlist.js @@ -0,0 +1,224 @@ +jQuery( function( $ ) { + + // console.log( wlffwc_wishlist ); + + if ( 'undefined' === typeof wlffwc_wishlist ) { + return false; + } + + /** + * WishList. + */ + var WLFFWCWishlist = function() { + + $( document ) + .on( 'click', '.wlffwc-add-to-wishlist-wrap .add-to-wishlist-link', { WLFFWCWishlist: this }, this.addToWishlist ) + .on( 'adding_to_cart', 'body', { WLFFWCWishlist: this }, this.adding_to_cart ) + .on( 'added_to_cart', 'body', { WLFFWCWishlist: this }, this.added_to_cart ) + .on( 'click', '.remove-from-wishlist', { WLFFWCWishlist: this }, this.remove_from_wishlist ) + }; + + /** + * Sends request to add product to wishlist. + * + * @since 1.0.0 + * + * @param jQuery object el Add to wishlist link. + */ + WLFFWCWishlist.prototype.addToWishlist = function( el ) { + + var that = $( this), + product_id = that.attr( 'data-product_id' ), + data = { + product_id: product_id, + wlffwc_add_nonce: wlffwc_wishlist.wlffwc_add_nonce, + action: wlffwc_wishlist.actions.add_to_wishlist_action, + }; + + el.preventDefault(); + + // console.log( data ); + + $( document.body ).trigger( 'before_adding_to_wishlist' ); + + $.ajax({ + type: 'POST', + url: wlffwc_wishlist.ajax_url, + data: data, + dataType: 'json', + beforeSend: function(){ + WLFFWCWishlistObj.block( that ); + }, + complete: function(){ + WLFFWCWishlistObj.unblock( that ); + }, + success: function( response ) { + var response_result = response.success, + response_message = response.message; + + // if( true === response_result ) { + $( '.wlffwc-add-to-wishlist-wrap' ).html( response_message ); + // console.log( 'yeh, true!' ); + // } + if( true === response_result ) { + $( document.body ).trigger('after_adding_to_wishlist', [ that, data ] ); + } + } + }); + + return false; + }; // ! addToWishlist + + /** + * Appends data to send to add to cart ajax request along with WC data. + * + * @since 1.0.0 + * + * @param object ev Event object. + * @param object button jQuery add to cart button object. + * @param object data WC Ajax add to cart data. + * + * @return void. + */ + WLFFWCWishlist.prototype.adding_to_cart = function( ev, button, data ) { + if( button.hasClass( 'wlffwc-wishlist-add-to-cart' ) ){ + data.wlffwc_remove_after_adding_cart = button.attr( 'data-product_id' ); + } + }; // ! adding_to_cart + + /** + * Remove item from the list after adding to the cart. + * + * @since 1.0.0 + * + * @param object ev Event object. + * @param object fragments WC Cart fragements. + * @param string carthash Cart hash value. + * @param object button jQuery add to cart button object. + * + * @return void. + */ + WLFFWCWishlist.prototype.added_to_cart = function( ev, fragments, carthash, button ) { + $( button ).closest('tr').remove(); + var messages = $( '#wlffwc-wishlist-wrapper .wlffwc-wc-alert.woocommerce'); + + if( messages.length === 0 ){ + messages.html( wlffwc_wishlist.labels.added_to_cart_message ).fadeIn(); + } else { + messages.fadeOut( 300, function(){ + messages.html( wlffwc_wishlist.labels.added_to_cart_message ).fadeIn(); + } ); + } + WLFFWCWishlistObj.maybe_show_no_data_message(); + }; // ! added_to_cart + + /** + * Removes item from the wishlist. + * + * @since 1.0.0 + * + * @param jQuery object el Remove element to remove the product from the list. + * + * @return void. + */ + WLFFWCWishlist.prototype.remove_from_wishlist = function( el ) { + var that = $( this ).closest( 'tr' ), + product_id = that.attr( 'data-product_id' ), + data = { + product_id : product_id, + wlffwc_remove_nonce : wlffwc_wishlist.wlffwc_remove_nonce, + action : wlffwc_wishlist.actions.remove_from_wishlist_action, + }; + + el.preventDefault(); + + $( document.body ).trigger( 'before_removing_from_wishlist' ); + + $.ajax({ + type: 'POST', + url: wlffwc_wishlist.ajax_url, + data: data, + dataType: 'json', + beforeSend: function(){ + WLFFWCWishlistObj.block( that ); + }, + complete: function(){ + WLFFWCWishlistObj.unblock( that ); + }, + success: function( response ) { + var response_result = response.success, + response_message = response.message; + + // if( true === response_result ) { + $( '.wlffwc-add-to-wishlist-wrap' ).html( response_message ); + // } + if( true === response_result ) { + that.remove(); + WLFFWCWishlistObj.maybe_show_no_data_message(); + $( document.body ).trigger('after_removing_from_wishlist', [ that, data ] ); + } + } + }); + + return false; + }; // ! remove_from_wishlist + + + /************************ + | | + | UTILITY FUNCTIONS | + | | + ************************/ + + /** + * Checks how many items in the list. If found zero, shows "no data" row. + * + * @since 1.0.0 + * + * @return void. + */ + WLFFWCWishlist.prototype.maybe_show_no_data_message = function() { + if ( $( '#wlffwc-wishlist-wrapper .wishlist-item' ).length == 0 ) { + $( '#wlffwc-wishlist-wrapper .no-data-found-row' ).removeClass('hide'); + } + } + + /** + * Block item if possible. + * + * @since 1.0.0 + * + * @param item jQuery object + * + * @return void + */ + WLFFWCWishlist.prototype.block = function( item ) { + if( typeof $.fn.block != 'undefined' ) { + item.fadeTo( '400', '0.6' ).block( { + message: null, + overlayCSS : { + background : 'transparent url(' + wlffwc_wishlist.ajax_loader + ') no-repeat center', + backgroundSize: '40px 40px', + opacity : 1 + } + } ); + } + } // ! block + + /** + * Unblock item if possible + * + * @since 1.0.0 + * + * @param item jQuery object + * + * @return void + */ + WLFFWCWishlist.prototype.unblock = function( item ) { + if( typeof $.fn.unblock != 'undefined' ) { + item.stop( true ).css( 'opacity', '1' ).unblock(); + } + } // ! unblock + + var WLFFWCWishlistObj = new WLFFWCWishlist(); +}); diff --git a/assets/js/public/wlffwc-wishlist.js b/assets/js/public/wlffwc-wishlist.js new file mode 100755 index 0000000..c6ea0f5 --- /dev/null +++ b/assets/js/public/wlffwc-wishlist.js @@ -0,0 +1 @@ +jQuery(function(a){if("undefined"==typeof wlffwc_wishlist)return!1;function t(){a(document).on("click",".wlffwc-add-to-wishlist-wrap .add-to-wishlist-link",{WLFFWCWishlist:this},this.addToWishlist).on("adding_to_cart","body",{WLFFWCWishlist:this},this.adding_to_cart).on("added_to_cart","body",{WLFFWCWishlist:this},this.added_to_cart).on("click",".remove-from-wishlist",{WLFFWCWishlist:this},this.remove_from_wishlist)}t.prototype.addToWishlist=function(t){var i=a(this),s={product_id:i.attr("data-product_id"),wlffwc_add_nonce:wlffwc_wishlist.wlffwc_add_nonce,action:wlffwc_wishlist.actions.add_to_wishlist_action};return t.preventDefault(),a(document.body).trigger("before_adding_to_wishlist"),a.ajax({type:"POST",url:wlffwc_wishlist.ajax_url,data:s,dataType:"json",beforeSend:function(){c.block(i)},complete:function(){c.unblock(i)},success:function(t){var o=t.success,e=t.message;a(".wlffwc-add-to-wishlist-wrap").html(e),!0===o&&a(document.body).trigger("after_adding_to_wishlist",[i,s])}}),!1},t.prototype.adding_to_cart=function(t,o,e){o.hasClass("wlffwc-wishlist-add-to-cart")&&(e.wlffwc_remove_after_adding_cart=o.attr("data-product_id"))},t.prototype.added_to_cart=function(t,o,e,i){a(i).closest("tr").remove();var s=a("#wlffwc-wishlist-wrapper .wlffwc-wc-alert.woocommerce");0===s.length?s.html(wlffwc_wishlist.labels.added_to_cart_message).fadeIn():s.fadeOut(300,function(){s.html(wlffwc_wishlist.labels.added_to_cart_message).fadeIn()}),c.maybe_show_no_data_message()},t.prototype.remove_from_wishlist=function(t){var i=a(this).closest("tr"),s={product_id:i.attr("data-product_id"),wlffwc_remove_nonce:wlffwc_wishlist.wlffwc_remove_nonce,action:wlffwc_wishlist.actions.remove_from_wishlist_action};return t.preventDefault(),a(document.body).trigger("before_removing_from_wishlist"),a.ajax({type:"POST",url:wlffwc_wishlist.ajax_url,data:s,dataType:"json",beforeSend:function(){c.block(i)},complete:function(){c.unblock(i)},success:function(t){var o=t.success,e=t.message;a(".wlffwc-add-to-wishlist-wrap").html(e),!0===o&&(i.remove(),c.maybe_show_no_data_message(),a(document.body).trigger("after_removing_from_wishlist",[i,s]))}}),!1},t.prototype.maybe_show_no_data_message=function(){0==a("#wlffwc-wishlist-wrapper .wishlist-item").length&&a("#wlffwc-wishlist-wrapper .no-data-found-row").removeClass("hide")},t.prototype.block=function(t){void 0!==a.fn.block&&t.fadeTo("400","0.6").block({message:null,overlayCSS:{background:"transparent url("+wlffwc_wishlist.ajax_loader+") no-repeat center",backgroundSize:"40px 40px",opacity:1}})},t.prototype.unblock=function(t){void 0!==a.fn.unblock&&t.stop(!0).css("opacity","1").unblock()};var c=new t}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..6056443 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,38 @@ +const gulp = require('gulp'); +const cleanCSS = require('gulp-clean-css'); +const autoprefixer = require('gulp-autoprefixer'); +const uglify = require('gulp-uglify'); + +var minify_admin_css = function () { + return gulp.src( 'assets/css/dev/admin/*.css' ) + .pipe(cleanCSS({debug: true}, (details) => { + console.log(`${details.name}: ${details.stats.originalSize}kb => ${details.stats.minifiedSize} kb`); + })) + .pipe(autoprefixer({cascade: false})) + .pipe( gulp.dest( './assets/css/admin/' ) ); +} + +var minify_public_css = function () { + return gulp.src( 'assets/css/dev/public/*.css' ) + .pipe(cleanCSS({debug: true}, (details) => { + console.log(`${details.name}: ${details.stats.originalSize}kb => ${details.stats.minifiedSize} kb`); + })) + .pipe(autoprefixer({cascade: false})) + .pipe( gulp.dest( './assets/css/public/' ) ); +} + +var minify_admin_js = function() { + return gulp.src( 'assets/js/dev/admin/*.js' ) + .pipe(uglify()) + .pipe( gulp.dest( './assets/js/admin/' ) ) +} + +var minify_public_js = function() { + return gulp.src( 'assets/js/dev/public/*.js' ) + .pipe(uglify()) + .pipe( gulp.dest( './assets/js/public/' ) ) +} + +exports.css = gulp.series( minify_admin_css, minify_public_css ); +exports.js = gulp.series( minify_admin_js, minify_public_js ); +exports.default = gulp.series( minify_admin_css, minify_public_css, minify_admin_js, minify_public_js ); \ No newline at end of file diff --git a/includes/admin/class-wlffwc-admin-plugins-links.php b/includes/admin/class-wlffwc-admin-plugins-links.php new file mode 100644 index 0000000..af415a5 --- /dev/null +++ b/includes/admin/class-wlffwc-admin-plugins-links.php @@ -0,0 +1,66 @@ +' . __( 'Settings', 'wishlist-feature-for-woocommerce' ) . ''; + + return $links; + } +} diff --git a/includes/admin/class-wlffwc-admin-settings.php b/includes/admin/class-wlffwc-admin-settings.php new file mode 100644 index 0000000..6647c92 --- /dev/null +++ b/includes/admin/class-wlffwc-admin-settings.php @@ -0,0 +1,179 @@ +version, 'all' ); + + // add_action( 'woocommerce_flush_rewrite_rules', array( $this, 'trigger_flush' ) ); + add_action( 'after_switch_theme', array( $this, 'trigger_flush' ) ); + } + + /** + * Initiates the class functions. + * + * @static + * @return void. + */ + public static function init() { + self::instance(); + } + + /** + * Adds settings menu page. + * + * @return void + */ + public function menu_page() { + add_menu_page( 'WishList', 'WishList', 'manage_options', 'wishlist-ffwc-settings', array( $this, 'process_settings' ), WLFFWC_PLUGIN_URL . 'assets/images/wishlist-icon.png', 56 ); + } // menu_page() + + /** + * Process plugin settings. + * + * @return void + */ + public function process_settings() { + $this->save_settings(); + + $this->show_settings(); + } // process_settings() + + /** + * Display plugin settings. + * + * @return void. + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function show_settings() { + + wp_enqueue_style( 'woocommerce_admin_styles' ); + wp_enqueue_style( 'wlffwc-global-settings-style' ); + ?> +
+
+ ID = 0; + $no_page->post_title = __( '-- Select --', 'wishlist-feature-for-woocommerce' ); + $wp_pages = array( + '-1' => $no_page, + ) + + + get_pages( + array( + 'number' => 50, + ) + ); + + include WLFFWC_INCLUDES_PATH . 'templates/admin/wlffwc-global-settings.template.php'; + ?> +
+
+ define_constants(); + $this->includes(); + $this->init_hooks(); + } + + /** + * Define Constants. + */ + private function define_constants() { + + $this->define( 'WLFFWC_ABSPATH', dirname( WLFFWC_PLUGIN_FILE ) . '/' ); + $this->define( 'WLFFWC_INCLUDES_PATH', dirname( WLFFWC_PLUGIN_FILE ) . '/includes/' ); + $this->define( 'WLFFWC_PLUGIN_BASENAME', plugin_basename( WLFFWC_PLUGIN_FILE ) ); + $this->define( 'WLFFWC_VERSION', $this->version ); + $this->define( 'WLFFWC_NOTICE_MIN_PHP_VERSION', '5.6.20' ); + $this->define( 'WLFFWC_NOTICE_MIN_WP_VERSION', '5.0' ); + $this->define( 'WLFFWC_NOTICE_MIN_WC_VERSION', '3.8' ); + $this->define( 'WLFFWC_PLUGIN_URL', plugin_dir_url( WLFFWC_PLUGIN_FILE ) ); + $this->define( 'WLFFWC_TEMPLATE_PATH', untrailingslashit( WLFFWC_INCLUDES_PATH ) . '/templates' ); + $this->define( 'WLFFWC_LANG_DIR', WLFFWC_ABSPATH . '/languages/' ); + } + + /** + * Include required core files used in admin and on the frontend. + * + * @since 1.0.0 + */ + public function includes() { + /** + * Class autoloader. + */ + include_once WLFFWC_INCLUDES_PATH . 'class-wlffwc-autoloader.php'; + + /** + * Class Helper functions.. + */ + include_once WLFFWC_INCLUDES_PATH . 'class-wlffwc-helper.php'; + + /** + * Class for installation purpose. + */ + include_once WLFFWC_INCLUDES_PATH . 'class-wlffwc-install.php'; + } + + /** + * Hook into actions and filters. + * + * @since 1.0.0 + */ + private function init_hooks() { + register_activation_hook( WLFFWC_PLUGIN_FILE, array( 'WLFFWC_Install', 'install' ) ); + + add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ), -1 ); + + add_action( 'admin_notices', array( $this, 'build_dependencies_notice' ) ); + + add_action( 'init', array( $this, 'init' ), 0 ); + + } + + /** + * Init WishListFFWC when WordPress Initialises. + * + * @since 1.0.0 + */ + public function init() { + // Before init action. + do_action( 'before_wlffwc_init' ); + + // Set up localisation. + $this->load_plugin_textdomain(); + + // We are dependent on WooCommerce. If either of them is not active, do not run our code. + if ( ! defined( 'WC_PLUGIN_FILE' ) ) { + return; + } + + /* + | + | Classes loaded for the dashboard only. + | + */ + if ( $this->is_request( 'admin' ) ) { + $this->admin_init(); + } + + /* + | + | Classes loaded for the frontend and for ajax requests. + | + */ + if ( $this->is_request( 'frontend' ) ) { + + /* + | + | Classes loaded for Shortcodes. + | + */ + WLFFWC_Shortcode_Wishlist::init(); + + $this->frontend_init(); + } + + // Init action. + do_action( 'wlffwc_init' ); + } + + /** + * Initiate classes required for dashboard. + */ + public function admin_init() { + WLFFWC_Admin_Settings::init(); + WLFFWC_Admin_Plugins_Links::init(); + } + + /** + * Initiate classes required for frontend and ajax. + */ + public function frontend_init() { + WLFFWC_Public_Single_Product::init(); + WLFFWC_Public_Wishlist_Wc_Menu_Item::init(); + } + + /** + * When WP has loaded all plugins, trigger the `wlffwc_loaded` hook. + * + * This ensures `wlffwc_loaded` is called only after all other plugins + * are loaded. + */ + public function on_plugins_loaded() { + do_action( 'wlffwc_loaded' ); + } + + /** + * Show admin notices for dependencies. + * + * @return void. + */ + public function build_dependencies_notice() { + + if ( ! version_compare( PHP_VERSION, WLFFWC_NOTICE_MIN_PHP_VERSION, '>=' ) ) { + // Show notice if php version is less than required. + $current = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION; + ?> +
+

+ %1$s requires PHP version %2$s or higher. Because you are using an unsupported version of PHP (%3$s), the %4$s plugin will not initialize. Please contact your hosting company to upgrade to PHP.' + ), + $this->plugin_name, + WLFFWC_NOTICE_MIN_PHP_VERSION, + $current, + $this->plugin_name + ); + ?> +

+
+ +
+

+ %1$s requires WooCommerce to be activated ! Install / Activate WooCommerce' + ), + $this->plugin_name, + $install_wc_url + ); + ?> +

+
+ is_rest_api_request(); + // case 'frontend_only': + // return ( ! is_admin() && ! defined( 'DOING_AJAX' ) && ! defined( 'DOING_CRON' ) && ! $this->is_rest_api_request() ); + // case 'frontend_and_ajax': + // return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! $this->is_rest_api_request(); + } + } + + + + /** + * Load Localisation files. + * Locales found in: + * - WLFFWC_LANG_DIR/wishlist-feature-for-woocommerce-LOCALE.mo + */ + public function load_plugin_textdomain() { + load_plugin_textdomain( 'wishlist-feature-for-woocommerce', false, plugin_basename( dirname( WLFFWC_PLUGIN_FILE ) ) . '/languages' ); + } + + /** + * Returns true if the request is a REST API request. + * + * @return bool + */ + public function is_rest_api_request() { + + // Considering this discussion, using constant here. We are also keeping further logic to check REST request in case REST_REQUEST is not defined. + // https://github.com/WP-API/WP-API/issues/926 + // if ( ! defined( 'REST_REQUEST' ) ) { + // return false; + // } + + if ( empty( $_SERVER['REQUEST_URI'] ) ) { + return false; + } + + $r_prefix = trailingslashit( rest_get_url_prefix() ); + $is_rest_api_request = ( false !== strpos( $_SERVER['REQUEST_URI'], $r_prefix ) ); + + return $is_rest_api_request; + } + + /** + * A global settings for this plugin. + * + * @return array The plugin settings. + */ + public function get_plugin_settings() { + if ( empty( $this->global_settings ) ) { + $this->global_settings = WLFFWC_Helper::get_global_settings(); + } + return $this->global_settings; + } + + /** + * Wishlist handler is one of the important class we have. To access its instance, we have created a function in the plugin class. + * + * @return WLFFWC_Wishlist_Handler WLFFWC_Wishlist_Handler class instance. + */ + public function wishlist_handler() { + return WLFFWC_Wishlist_Handler::instance(); + } +} diff --git a/includes/class-wlffwc-autoloader.php b/includes/class-wlffwc-autoloader.php new file mode 100644 index 0000000..2c87d3c --- /dev/null +++ b/includes/class-wlffwc-autoloader.php @@ -0,0 +1,94 @@ +include_path = untrailingslashit( plugin_dir_path( WLFFWC_PLUGIN_FILE ) ) . '/includes/'; + } + + /** + * Take a class name and turn it into a file name. + * + * @param string $class Class name. + * @return string + */ + private function get_file_name_from_class( $class ) { + return 'class-' . str_replace( '_', '-', $class ) . '.php'; + } + + /** + * Include a class file. + * + * @param string $path File path. + * @return bool Successful or not. + */ + private function load_file( $path ) { + if ( $path && is_readable( $path ) ) { + include_once $path; + return true; + } + return false; + } + + /** + * Auto-load classes on demand to reduce memory consumption. + * + * @param string $class Class name. + */ + public function autoload( $class ) { + $class = strtolower( $class ); + + if ( 0 !== strpos( $class, 'wlffwc_' ) ) { + return; + } + + $file = $this->get_file_name_from_class( $class ); + $path = ''; + + if ( 0 === strpos( $class, 'wlffwc_admin_' ) ) { + $path = $this->include_path . 'admin/'; + } + + if ( 0 === strpos( $class, 'wlffwc_public_' ) ) { + $path = $this->include_path . 'public/'; + } + + if ( 0 === strpos( $class, 'wlffwc_shortcode_' ) ) { + $path = $this->include_path . 'shortcodes/'; + } + + if ( empty( $path ) || ! $this->load_file( $path . $file ) ) { + $this->load_file( $this->include_path . $file ); + } + } +} + +new WLFFWC_Autoloader(); diff --git a/includes/class-wlffwc-helper.php b/includes/class-wlffwc-helper.php new file mode 100644 index 0000000..36761cb --- /dev/null +++ b/includes/class-wlffwc-helper.php @@ -0,0 +1,73 @@ + __( 'wishlist', 'wishlist-feature-for-woocommerce' ), + 'wlffwc_wishlist_page' => 0, + ); + } + + /** + * Updated settings values of the plugin. + * + * @static + * @return array Updated settings. + */ + public static function get_global_settings() { + + $db_settings = get_option( 'wlffwc_global_settings', array() ); + $def_settings = self::get_default_global_settings(); + + $all_settings = array(); + + if ( ! empty( $db_settings ) ) { + + // Let's find out the difference first. + $diff = array_diff( $db_settings, $def_settings ); + + // Merge diff with DB settings to override DB settings over default settings. + $db_diff_settings = array_merge( $diff, $db_settings ); + + // Now, merge db_diff_settings with default settings to add any missed setting from the default settings which is not present in the DB settings. + $all_settings = array_merge( $def_settings, $db_diff_settings ); + + } else { + $all_settings = $def_settings; + } + + return apply_filters( 'wlffwc_global_settings', $all_settings ); + } +} diff --git a/includes/class-wlffwc-install.php b/includes/class-wlffwc-install.php new file mode 100644 index 0000000..5f14dd9 --- /dev/null +++ b/includes/class-wlffwc-install.php @@ -0,0 +1,182 @@ +version ); + } + + /** + * Update DB version to current. + * + * @static + * @since 1.0.0 + * + * @param string|null $version New DB version or null. + */ + private static function update_db_version( $version = null ) { + update_option( 'wlffwc_db_version', is_null( $version ) ? WishListFFWC()->version : $version ); + } + + /** + * Set up the database tables which the plugin needs to function. + * + * @static + * @since 1.0.0 + * + * Tables: + * wlffwc_list - A table for storing list name, privacy and other related meta. + * wlffwc_list_details - A table with all the products added in the wishlist. + */ + private static function create_tables() { + + global $wpdb; + + $collate = self::get_wp_charset_collate(); + require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); + + $wpdb->hide_errors(); + + /* + | + | wlffwc_list table + | + */ + $wlffwc_list = $wpdb->prefix . 'wlffwc_list'; + if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$wlffwc_list';" ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wlffwc_list_tbl_q = " + CREATE TABLE IF NOT EXISTS {$wlffwc_list} ( + ID int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT, + user_id int(11) NOT NULL, + wishlist_name text, + privacy tinyint(1) default 0, + added timestamp default CURRENT_TIMESTAMP, + INDEX user_id (user_id) + ) $collate; + "; + @dbDelta( $wlffwc_list_tbl_q ); + } + + /* + | + | wlffwc_list_details table + | + */ + $wlffwc_list_details = $wpdb->prefix . 'wlffwc_list_details'; + if ( ! $wpdb->get_var( "SHOW TABLES LIKE '$wlffwc_list_details';" ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wlffwc_list_details_tbl_q = " + CREATE TABLE IF NOT EXISTS {$wlffwc_list_details} ( + ID int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT, + product_id int(11) NOT NULL, + wishlist_id int(11) NOT NULL, + price decimal(9,3) default NULL, + added timestamp default CURRENT_TIMESTAMP, + UNIQUE wishlist_product (wishlist_id, product_id) + ) $collate; + "; + @dbDelta( $wlffwc_list_details_tbl_q ); + } + } // create_tables() + + /** + * Gets the default charset and collate for the MySQL database. + * + * @static + * @since 1.0.0 + * + * @return string $charset_collate charset and collate for the MySQL + * database. + */ + protected static function get_wp_charset_collate() { + global $wpdb; + $charset_collate = ''; + + if ( ! empty( $wpdb->charset ) ) { + $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset"; + } + + if ( ! empty( $wpdb->collate ) ) { + $charset_collate .= " COLLATE $wpdb->collate"; + } + + return $charset_collate; + } // get_wp_charset_collate() + + /** + * Creates wishlist listing page. + * + * @static + * @since 1.0.0 + * + * @return void. + */ + protected static function create_pages() { + $plugin_settings = WishListFFWC()->get_plugin_settings(); + + if ( 0 == $plugin_settings['wlffwc_wishlist_page'] ) { + $page_id = wp_insert_post( + array( + 'post_title' => 'Wishlist', + 'post_status' => 'publish', + 'post_type' => 'page', + 'post_content' => '[wlffwc_wishlist]', + ) + ); + $plugin_settings['wlffwc_wishlist_page'] = $page_id; + update_option( 'wlffwc_global_settings', $plugin_settings ); + } + } // create_pages() +} diff --git a/includes/class-wlffwc-wishlist-handler.php b/includes/class-wlffwc-wishlist-handler.php new file mode 100644 index 0000000..bbc99de --- /dev/null +++ b/includes/class-wlffwc-wishlist-handler.php @@ -0,0 +1,514 @@ +version, 'all' ); + + wp_register_script( 'wlffwc-wishlist-script', WLFFWC_PLUGIN_URL . 'assets/js/' . WLFFWC_Helper::dev_script_debug() . 'public/wlffwc-wishlist.js', array( 'jquery' ), WishListFFWC()->version, true ); + } + + /** + * Data to be localized in JS. + * + * @since 1.0.0 + * @return array Localize data. + */ + public function localize_data() { + + $plugin_settings = WishListFFWC()->get_plugin_settings(); + + return apply_filters( + 'wlffwc_localize_wishlist', + array( + 'ajax_url' => admin_url( 'admin-ajax.php', 'relative' ), + 'ajax_loader' => WLFFWC_PLUGIN_URL . 'assets/images/ajax-loader.svg', + 'labels' => array( + 'added_to_cart_message' => sprintf( '', esc_url( wc_get_cart_url() ), __( 'View cart', 'wishlist-feature-for-woocommerce' ), __( 'A product has been added to your cart.', 'wishlist-feature-for-woocommerce' ) ), + ), + 'wlffwc_add_nonce' => wp_create_nonce( 'wlffwc-add-ajax-' . get_current_user_id() ), + 'wlffwc_remove_nonce' => wp_create_nonce( 'wlffwc-remove-ajax-' . get_current_user_id() ), + 'actions' => array( + 'add_to_wishlist_action' => 'wlffwc_add_to_wishlist', + 'remove_from_wishlist_action' => 'wlffwc_remove_from_wishlist', + ), + 'plugin_settings' => array( + 'browse_url' => get_permalink( $plugin_settings['wlffwc_wishlist_page'] ), + 'wishlist_alt_string' => $plugin_settings['wlffwc_wishlist_text'], + ), + ) + ); + } + + + /** + * Makes an entry in the database for given user, if already not exists. + * + * @param int $user_id User ID. + * @since 1.0.0 + * @return int Wishlist ID. Returns an existing one or newly created. + */ + private function create_wishlist( int $user_id = 0 ) { + + if ( 0 === $user_id ) { + $user_id = get_current_user_id(); + } + + /* + | + | Default data. + | + */ + $default_data = apply_filters( + 'wlffwc_default_wishlist_data', + array( + 'user_id' => $user_id, + 'wishlist_name' => __( 'My wishlist', 'wishlist-feature-for-woocommerce' ), + 'privacy' => 0, // 0 is private. 1 for public. Currently, supports only two status. + ) + ); + + /* + | + | Let's validate the data. + | + */ + $error_in_data = false; + if ( ! (int) $default_data['user_id'] ) { + $error_in_data = true; + } + + $default_data['wishlist_name'] = ( ! empty( $default_data['wishlist_name'] ) ) ? strip_tags( $default_data['wishlist_name'] ) : __( 'My wishlist', 'wishlist-feature-for-woocommerce' ); + + $default_data['privacy'] = ( in_array( $default_data['privacy'], array( 0, 1 ) ) ) ? $default_data['privacy'] : 0; + + if ( $error_in_data ) { + return new WP_Error( 'wlffwc', __( 'Wishlist data is not valid.', 'wishlist-feature-for-woocommerce' ) ); + } + + /* + | + | Now, we have validated the data. Time to insert in the database. + | + */ + global $wpdb; + + $res = $wpdb->query( + $wpdb->prepare( + "INSERT INTO + {$wpdb->prefix}wlffwc_list + ( `user_id`, `wishlist_name`, `privacy` ) + VALUES + ( %d, %s, %d )", + $default_data['user_id'], + $default_data['wishlist_name'], + $default_data['privacy'] + ) + ); + + if ( $res ) { + $this->wishlist_id = (int) $wpdb->insert_id; + + do_action( 'wlffwc_wishlist_created', $this->wishlist_id, $default_data ); + } + + return $this->wishlist_id; + } + + /* === ADD METHODS === */ + + /** + * Add a product in the wishlist. Default call is from Ajax request. + * + * @since 1.0.0 + * @param array $atts An array of parameters. + * @return array|int If ajax request, then returns response with the message. For non ajax requests, it is just a inserted row ID. + */ + public function add( $atts = array() ) { + + $row_id = 0; + $ajax_response = array( + 'success' => false, + 'message' => __( 'Something went wrong. Please refresh a page and try again.', 'wishlist-feature-for-woocommerce' ), + ); + + if ( empty( $atts ) ) { + if ( isset( $_POST['wlffwc_add_nonce'] ) + && + wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wlffwc_add_nonce'] ) ), 'wlffwc-add-ajax-' . get_current_user_id() ) + ) { + $atts = $_POST; + } else { + // The request is not authorized. + wp_send_json( + $ajax_response + ); + } + } + + if ( empty( $atts ) ) { + wp_send_json_error(); + } + + if ( ! get_current_user_id() ) { + $ajax_response['message'] = __( 'Can not process the request for non logged users.', 'wishlist-feature-for-woocommerce' ); + } else { + + $product_id = $atts['product_id']; + + /* + | + | Let's check if the user has wishlist created, if not, create a new one. + | + */ + $this->wishlist_id = $this->get_wishlist_id( get_current_user_id() ); + + if ( ! $this->wishlist_id ) { + $this->wishlist_id = $this->create_wishlist( get_current_user_id() ); + } + + /* + | + | Add the product in the wishlist. + | + */ + + $row_id = $this->add_product_to_wishlist( wc_get_product( $product_id ), $this->wishlist_id ); + + if ( $row_id ) { + + $plugin_settings = WishListFFWC()->get_plugin_settings(); + + $ajax_response['success'] = true; + $ajax_response['message'] = sprintf( + __( + 'A product is successfully added! Browse %2$s' + ), + get_permalink( $plugin_settings['wlffwc_wishlist_page'] ), + $plugin_settings['wlffwc_wishlist_text'] + ); + } + } + + // Response to ajax request. + if ( ! empty( $_POST ) ) { + wp_send_json( + $ajax_response + ); + } else { + return $row_id; + } + } + + /** + * Makes an entry of product in the 'wlffwc_list_details' table. + * + * @param WC_Product $product Product ID to be added. Does not support multiple products as of now. + * @param int $wishlist_id Wishlist ID where products to be added. + * + * @return int Row ID. + */ + private function add_product_to_wishlist( WC_Product $product, int $wishlist_id ) { + + global $wpdb; + + $res = $wpdb->query( + $wpdb->prepare( + "INSERT INTO + {$wpdb->prefix}wlffwc_list_details + ( `product_id`, `wishlist_id`, `price` ) + VALUES + ( %d, %d, %d )", + $product->get_id(), + $wishlist_id, + wc_get_price_to_display( $product ) + ) + ); + + if ( $res ) { + do_action( 'wlffwc_wishlist_item_added', $wishlist_id, $product, (int) $wpdb->insert_id ); + return (int) $wpdb->insert_id; + } + return 0; + } + + /* === REMOVE METHODS === */ + + /** + * Remove an entry from the wishlist. + * + * @param array $atts Array of parameters; when not passed, parameters will be retrieved from $_REQUEST. + * + * @since 1.0.0 + * @return boolean True if removed. + */ + public function remove( $atts = array() ) { + $row_id = 0; + $ajax_response = array( + 'success' => false, + 'message' => __( 'Something went wrong. Please refresh a page and try again.', 'wishlist-feature-for-woocommerce' ), + ); + + if ( empty( $atts ) ) { + if ( isset( $_POST['wlffwc_remove_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wlffwc_remove_nonce'] ) ), 'wlffwc-remove-ajax-' . get_current_user_id() ) ) { + $atts = $_POST; + } else { + error_log( 'nonce failed' ); + // The request is not authorized. + wp_send_json( + $ajax_response + ); + } + } + + if ( empty( $atts ) ) { + wp_send_json_error(); + } + + if ( ! get_current_user_id() ) { + $ajax_response['message'] = __( 'Can not process the request for non logged users.', 'wishlist-feature-for-woocommerce' ); + } else { + + $product_id = $atts['product_id']; + + /* + | + | Let's check if the user has wishlist created. + | + */ + $wishlist_id = $this->get_wishlist_id( get_current_user_id() ); + + /* + | + | Remove product from the wishlist. + | + */ + + $res = $this->remove_product_from_wishlist( $product_id, $wishlist_id ); + + if ( $res ) { + + $ajax_response['success'] = true; + $ajax_response['message'] = __( 'A product is removed!' ); + } + } + + // Response to ajax request. + if ( ! empty( $_POST ) ) { + wp_send_json( + $ajax_response + ); + } else { + return $row_id; + } + } + + /** + * Removes product from the wishlist. + * + * @param int $product_id An item to be removed. + * @param int $wishlist_id Wishlist to be updated. + * @return boolean True on success. + */ + private function remove_product_from_wishlist( int $product_id, int $wishlist_id ) { + global $wpdb; + + $res = $wpdb->delete( + $wpdb->prefix . 'wlffwc_list_details', + array( + 'product_id' => $product_id, + 'wishlist_id' => $wishlist_id, + ) + ); + + if ( $res ) { + do_action( 'wlffwc_wishlist_item_removed', $product_id, $wishlist_id ); + return true; + } + return false; + } + + /** + * Removes product from the wishlist after adding it to the cart from the wishlist shortcode. + * + * @return void. + */ + public function remove_from_wishlist_after_add_to_cart() { + + if ( isset( $_REQUEST['wlffwc_remove_after_adding_cart'] ) ) { + + $product_id = sanitize_text_field( wp_unslash( $_REQUEST['wlffwc_remove_after_adding_cart'] ) ); + + // To confirm that we are going to remove the product that is added in the cart. + if ( isset( $_REQUEST['product_id'] ) && $product_id == $_REQUEST['product_id'] ) { + $this->remove( array( 'product_id' => $product_id ) ); + } + } + } + + /* === GENERAL METHODS === */ + + /** + * Returns wishlist ID of the user. + * + * @param int $user_id User ID. + * @return int Wishlist ID from the database, empty if not found. + */ + public function get_wishlist_id( int $user_id = 0 ) { + if ( 0 === $user_id ) { + return 0; + } + global $wpdb; + return $wpdb->get_var( + $wpdb->prepare( + "SELECT + ID + FROM + {$wpdb->prefix}wlffwc_list + WHERE + `user_id` = %d", + $user_id + ) + ); + } + + /** + * Check if the product exists in the wishlist. + * + * @since 1.0.0 + * + * @param int $product_id Product id to check. + * @param int $user_id User ID to check the product against. + * @param int $wishlist_id Wishlist ID of the user. + * + * @return bool + */ + public function is_product_in_wishlist( int $product_id, int $user_id, int $wishlist_id = 0 ) { + + $response = false; + + if ( empty( $wishlist_id ) ) { + $wishlist_id = $this->get_wishlist_id( $user_id ); + } + + if ( ! empty( $wishlist_id ) ) { + global $wpdb; + $res = $wpdb->get_var( + $wpdb->prepare( + "SELECT + ID + FROM + {$wpdb->prefix}wlffwc_list_details + WHERE + `wishlist_id` = %d + AND + `product_id` = %d", + $wishlist_id, + $product_id + ) + ); + + if ( $res ) { + $response = true; + } + } + + return apply_filters( 'wlffwc_is_product_in_wishlist', $response, $product_id, $user_id, $wishlist_id ); + } + + /** + * Items from the wishlist. + * + * @since 1.0.0 + * @param int $wishlist_id Wishlist ID to get the items. + * @return array Items from the wishlist. + */ + public function get_wishlist_items( $wishlist_id ) { + + global $wpdb; + + $products = $wpdb->get_results( + $wpdb->prepare( + "SELECT + product_id, price, added + FROM + {$wpdb->prefix}wlffwc_list_details + WHERE + `wishlist_id` = %d", + $wishlist_id + ), + ARRAY_A + ); + + return apply_filters( 'wlffwc_get_wishlist_items', $products, $wishlist_id ); + } + } +} diff --git a/includes/public/class-wlffwc-public-single-product.php b/includes/public/class-wlffwc-public-single-product.php new file mode 100644 index 0000000..2a57676 --- /dev/null +++ b/includes/public/class-wlffwc-public-single-product.php @@ -0,0 +1,129 @@ +wishlist_handler(); + } + + /** + * Add to wishlist button on a single product page. + * + * @return void. + */ + public function add_to_wishlist_button() { + global $product; + + /* + | + | Currently, we support only 'simple' type products. + | + */ + $supported_product_types = apply_filters( 'wlffwc_supported_product_types', array( 'simple' ), $product ); + + if ( in_array( $product->get_type(), $supported_product_types ) ) { + + $wlffwc_wishlist_handler = WLFFWC_Wishlist_Handler::instance(); + + $is_in_wishlist = $wlffwc_wishlist_handler->is_product_in_wishlist( $product->get_id(), get_current_user_id() ); + + $global_settings = WishListFFWC()->get_plugin_settings(); + + if ( $is_in_wishlist ) { + $browse_label = apply_filters( 'wlffwc_browse_label', __( 'Browse', 'wishlist-feature-for-woocommerce' ) . ' ' . stripslashes( $global_settings['wlffwc_wishlist_text'] ) ); + $browse_link = apply_filters( 'wlffwc_browse_link', get_permalink( $global_settings['wlffwc_wishlist_page'] ) ); + $pre_browse_text = apply_filters( 'wlffwc_pre_browse_text', __( 'This product is already in your', 'wishlist-feature-for-woocommerce' ) . ' ' . stripslashes( $global_settings['wlffwc_wishlist_text'] ) ) . '!'; + + wc_get_template( + '/public/wlffwc-browse-wishlist.template.php', + array( + 'browse_label' => $browse_label, + 'browse_link' => $browse_link, + 'pre_browse_text' => $pre_browse_text, + ), + 'wishlist-feature-for-woocommerce', + WLFFWC_TEMPLATE_PATH + ); + } else { + $link_label = apply_filters( 'wlffwc_add_to_label', __( 'Add to', 'wishlist-feature-for-woocommerce' ) . ' ' . stripslashes( $global_settings['wlffwc_wishlist_text'] ) ); + + wc_get_template( + '/public/wlffwc-add-to-wishlist-single.template.php', + array( + 'product' => $product, + 'link_label' => $link_label, + ), + 'wishlist-feature-for-woocommerce', + WLFFWC_TEMPLATE_PATH + ); + + // add_filter( 'wlffwc_localize_wishlist', array( $this, 'localize_browse_template' ) ); + } + + wp_enqueue_script( 'wlffwc-wishlist-script' ); + + wp_localize_script( 'wlffwc-wishlist-script', 'wlffwc_wishlist', WishListFFWC()->wishlist_handler()->localize_data() ); + } + } + + /** + * Not in use. Initially decided to show browse wishlist template after adding the product in the wishlist. However, browse template has message that "the product is alread in the list", which does not make sense to show after adding a product. + * + * @param array $data Data to localize. + * @return [type] [description] + */ + public function localize_browse_template( $data ) { + $data['browse_wishlist_template'] = 123; + return $data; + } + } +} diff --git a/includes/public/class-wlffwc-public-wishlist-wc-menu-item.php b/includes/public/class-wlffwc-public-wishlist-wc-menu-item.php new file mode 100644 index 0000000..a15bc72 --- /dev/null +++ b/includes/public/class-wlffwc-public-wishlist-wc-menu-item.php @@ -0,0 +1,151 @@ +get_plugin_settings(); + + // Endpoint should consist the string being used as 'wishlist' text. + self::$endpoint .= sanitize_title( $plugin_settings['wlffwc_wishlist_text'] ); + + // Actions used to insert a new endpoint. + add_action( 'init', array( $this, 'add_endpoints' ) ); + add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 ); + + add_filter( 'woocommerce_account_menu_items', array( $this, 'wishlist_menu_item' ), 11, 1 ); + + add_action( 'woocommerce_account_' . self::$endpoint . '_endpoint', array( $this, 'wishlist_item_content' ) ); + + add_action( 'wp_enqueue_scripts', array( $this, 'wishlist_menu_item_icon' ) ); + + } + + /** + * Heart icon to wishlist item menu in Strofront theme. + * + * @since 1.0.0 + * @return void. + */ + public function wishlist_menu_item_icon() { + + $menu_item_icon = 'body.theme-storefront ul li.woocommerce-MyAccount-navigation-link--' . self::$endpoint . ' a:before{ + content: "\f004" + }'; + wp_add_inline_style( 'wlffwc-wishlist-style', $menu_item_icon ); + } + + /** + * Initiates the class functions. + * + * @static + * @return void. + */ + public static function init() { + self::instance(); + } + + /** + * Registers a new endpoint for wishlist content. + * + * @since 1.0.0 + * @return void. + */ + public function add_endpoints() { + add_rewrite_endpoint( self::$endpoint, EP_ROOT | EP_PAGES ); + + // This option is updated on update of global plugin settings. Refer WLFFWC_Admin_Settings class. + if ( get_option( 'wlffwc_settings_changed' ) == true ) { + // We are flushing rules here because 'wishlist' text is being used on wc account dashboard as well. + flush_rewrite_rules(); + update_option( 'wlffwc_settings_changed', false ); + } + } + + /** + * A new query var for wishlist item. + * + * @param array $vars Endpoints. + * @return array + */ + public function add_query_vars( $vars ) { + $vars[] = self::$endpoint; + + return $vars; + } + + /** + * Adds wishlist menu item tab on the WooCommerce account page. + * + * @since 1.0.0 + * @param array $items Array of items. + */ + public function wishlist_menu_item( $items ) { + + $plugin_settings = WishListFFWC()->get_plugin_settings(); + + // Remove the logout menu item. + $logout = $items['customer-logout']; + unset( $items['customer-logout'] ); + + // Insert our endpoint. + $items[ self::$endpoint ] = ucfirst( stripslashes( $plugin_settings['wlffwc_wishlist_text'] ) ); + + // Insert back the logout item. + $items['customer-logout'] = $logout; + + return $items; + } + + /** + * Wishlist item content. Used original wishlist shortcode. + * + * @since 1.0.0 + * @return void. + */ + public function wishlist_item_content() { + echo apply_filters( 'wlffwc_wishlist_item_content', do_shortcode( '[wlffwc_wishlist]' ) ); + } +} diff --git a/includes/shortcodes/class-wlffwc-shortcode-wishlist.php b/includes/shortcodes/class-wlffwc-shortcode-wishlist.php new file mode 100644 index 0000000..d0ac0f3 --- /dev/null +++ b/includes/shortcodes/class-wlffwc-shortcode-wishlist.php @@ -0,0 +1,131 @@ +wishlist_handler(); + add_shortcode( 'wlffwc_wishlist', array( $this, 'wishlist_callback' ) ); + } + + /** + * Initiates the class functions. + * + * @static + * @return void. + */ + public static function init() { + self::instance(); + } + + /** + * Callback function for [wlffwc_wishlist]. + * + * @param array $atts Shortcode attributes. + * @return string Output for [wlffwc_wishlist]. + */ + public function wishlist_callback( $atts ) { + + $atts = shortcode_atts( + array( + 'wishlist_id' => get_query_var( 'wishlist_id', false ), + ), + $atts + ); + + /** + * Shortcode attributes. + * + * @var array. + */ + extract( $atts ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract + + if ( ! $wishlist_id ) { + $wishlist_handler = WLFFWC_Wishlist_Handler::instance(); + $wishlist_id = $wishlist_handler->get_wishlist_id( get_current_user_id() ); + } + + if ( $wishlist_id ) { + $wishlist_items = $wishlist_handler->get_wishlist_items( $wishlist_id ); + + if ( ! empty( $wishlist_items ) ) { + + $supported_post_status = apply_filters( 'wlffwc_supported_post_status', array( 'publish' ) ); + + foreach ( $wishlist_items as $key => $w_item ) { + $product_obj = wc_get_product( $w_item['product_id'] ); + + if ( ! in_array( $product_obj->get_status(), $supported_post_status ) ) { + unset( $wishlist_items[ $key ] ); + } else { + $wishlist_items[ $key ]['product_object'] = $product_obj; + } + } + } + + $global_settings = WishListFFWC()->get_plugin_settings(); + } + + wp_enqueue_style( 'wlffwc-wishlist-style' ); + wp_enqueue_script( 'wlffwc-wishlist-script' ); + + wp_localize_script( 'wlffwc-wishlist-script', 'wlffwc_wishlist', WishListFFWC()->wishlist_handler()->localize_data() ); + + $userdata = get_userdata( get_current_user_id() ); + + $wishlist_shortcode_data = apply_filters( + 'wlffwc_wishlist_shortcode_data', + array( + 'wishlist_title' => ucfirst( $global_settings['wlffwc_wishlist_text'] ) . ' ' . __( 'of' ) . ' ' . $userdata->display_name, + 'wishlist_items' => $wishlist_items, + 'no_products_text' => __( 'No products added to the', 'wishlist-feature-for-woocommerce' ) . ' ' . stripslashes( $global_settings['wlffwc_wishlist_text'] ), + 'hide_no_data_row' => ( count( $wishlist_items ) > 0 ) ? 'hide' : '', + ) + ); + + return wc_get_template( + '/public/wlffwc-wishlist.template.php', + $wishlist_shortcode_data, + 'wishlist-feature-for-woocommerce', + WLFFWC_TEMPLATE_PATH + ); + } + } +} diff --git a/includes/templates/admin/wlffwc-global-settings.template.php b/includes/templates/admin/wlffwc-global-settings.template.php new file mode 100644 index 0000000..d479a60 --- /dev/null +++ b/includes/templates/admin/wlffwc-global-settings.template.php @@ -0,0 +1,72 @@ + +
+
+

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

+
+ + + +

+
+ +

+ + +

+
+
diff --git a/includes/templates/public/wlffwc-add-to-wishlist-single.template.php b/includes/templates/public/wlffwc-add-to-wishlist-single.template.php new file mode 100644 index 0000000..ca4c306 --- /dev/null +++ b/includes/templates/public/wlffwc-add-to-wishlist-single.template.php @@ -0,0 +1,23 @@ + +
+ + + +
diff --git a/includes/templates/public/wlffwc-browse-wishlist.template.php b/includes/templates/public/wlffwc-browse-wishlist.template.php new file mode 100644 index 0000000..c68bfc4 --- /dev/null +++ b/includes/templates/public/wlffwc-browse-wishlist.template.php @@ -0,0 +1,30 @@ + +
+ + + +
diff --git a/includes/templates/public/wlffwc-wishlist.template.php b/includes/templates/public/wlffwc-wishlist.template.php new file mode 100644 index 0000000..cdad8d9 --- /dev/null +++ b/includes/templates/public/wlffwc-wishlist.template.php @@ -0,0 +1,97 @@ + +
+
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ + get_image( 'woocommerce_thumbnail' ); ?> + + + + + + get_title() ); ?> + + + + +
+ get_price() > $w_item['price'] ) : ?> + get_price() - $w_item['price'] ) / $w_item['price'] ) * 100 ) ); ?> + + get_price() < $w_item['price'] ) : ?> + get_price() ) / $w_item['price'] ) * 100 ) ); ?> + +
+ + + + + ' . sprintf( __( 'Added on: %s', 'wishlist-feature-for-woocommerce' ), date_i18n( get_option( 'date_format' ), strtotime( $w_item['added'] ) ) ) . ''; + ?> + is_in_stock() ) : ?> +

get_id() . '"]' ) ); ?>" data-quantity="1" class="button product_type_simple add_to_cart_button ajax_add_to_cart wlffwc-wishlist-add-to-cart" data-product_id="get_id(); ?>" data-product_sku="woo-belt" aria-label="Add Belt to your cart" rel="nofollow">Add to cart

+ + +
+
diff --git a/languages/wishlist-feature-for-woocommerce.pot b/languages/wishlist-feature-for-woocommerce.pot new file mode 100644 index 0000000..5f79b9c --- /dev/null +++ b/languages/wishlist-feature-for-woocommerce.pot @@ -0,0 +1,173 @@ +msgid "" +msgstr "" +"Project-Id-Version: Wishlist Feature For WooCommerce\n" +"POT-Creation-Date: 2020-03-09 16:06+0530\n" +"PO-Revision-Date: 2020-03-09 16:06+0530\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.2.3\n" +"X-Poedit-Basepath: ..\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-KeywordsList: __;_e;esc_html_e;esc_html__\n" +"X-Poedit-SearchPath-0: .\n" +"X-Poedit-SearchPathExcluded-0: node_modules\n" + +#: includes/admin/class-wlffwc-admin-plugins-links.php:62 +msgid "Settings" +msgstr "" + +#: includes/admin/class-wlffwc-admin-settings.php:103 +msgid "-- Select --" +msgstr "" + +#: includes/class-wishlist-feature-for-woocommerce.php:68 +msgid "Cloning is forbidden." +msgstr "" + +#: includes/class-wishlist-feature-for-woocommerce.php:75 +msgid "Unserializing instances of this class is forbidden." +msgstr "" + +#: includes/class-wishlist-feature-for-woocommerce.php:231 +#, php-format +msgid "" +"The %1$s requires PHP version %2$s or higher. Because you " +"are using an unsupported version of PHP (%3$s), the %4$s " +"plugin will not initialize. Please contact your hosting company to upgrade " +"to PHP." +msgstr "" + +#: includes/class-wishlist-feature-for-woocommerce.php:252 +#, php-format +msgid "" +"The %1$s requires WooCommerce to be activated ! Install / Activate WooCommerce" +msgstr "" + +#: includes/class-wlffwc-helper.php:38 +msgid "wishlist" +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:95 +msgid "View cart" +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:95 +msgid "A product has been added to your cart." +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:134 +#: includes/class-wlffwc-wishlist-handler.php:149 +msgid "My wishlist" +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:154 +msgid "Wishlist data is not valid." +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:200 +#: includes/class-wlffwc-wishlist-handler.php:317 +msgid "Something went wrong. Please refresh a page and try again." +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:222 +#: includes/class-wlffwc-wishlist-handler.php:337 +msgid "Can not process the request for non logged users." +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:253 +#, php-format +msgid "A product is successfully added! Browse %2$s" +msgstr "" + +#: includes/class-wlffwc-wishlist-handler.php:360 +msgid "A product is removed!" +msgstr "" + +#: includes/public/class-wlffwc-public-single-product.php:82 +msgid "Browse" +msgstr "" + +#: includes/public/class-wlffwc-public-single-product.php:84 +msgid "This product is already in your" +msgstr "" + +#: includes/public/class-wlffwc-public-single-product.php:97 +msgid "Add to" +msgstr "" + +#: includes/shortcodes/class-wlffwc-shortcode-wishlist.php:116 +msgid "of" +msgstr "" + +#: includes/shortcodes/class-wlffwc-shortcode-wishlist.php:118 +msgid "No products added to the" +msgstr "" + +#: includes/templates/admin/wlffwc-global-settings.template.php:22 +msgid "Wishlist Feature for WooCommerce" +msgstr "" + +#: includes/templates/admin/wlffwc-global-settings.template.php:28 +msgid "Wishlist text" +msgstr "" + +#: includes/templates/admin/wlffwc-global-settings.template.php:39 +msgid "" +"A \"wishlist\" text. Changing this text will take effect on frontend only." +msgstr "" + +#: includes/templates/admin/wlffwc-global-settings.template.php:45 +msgid "Wishlist page" +msgstr "" + +#: includes/templates/admin/wlffwc-global-settings.template.php:59 +msgid "A page with [wlffwc_wishlist] shortcode inserted." +msgstr "" + +#: includes/templates/admin/wlffwc-global-settings.template.php:66 +msgid "Save changes" +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:28 +#: includes/templates/public/wlffwc-wishlist.template.php:48 +msgid "Product" +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:29 +#: includes/templates/public/wlffwc-wishlist.template.php:60 +msgid "Unit Price" +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:30 +#: includes/templates/public/wlffwc-wishlist.template.php:73 +msgid "Stock Status" +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:31 +#: includes/templates/public/wlffwc-wishlist.template.php:78 +msgid "Actions" +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:64 +#, php-format +msgid "Price increased by %s%% since added." +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:67 +#, php-format +msgid "Price dropped by %s%% since added." +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:80 +#, php-format +msgid "Added on: %s" +msgstr "" + +#: includes/templates/public/wlffwc-wishlist.template.php:85 +msgid "Remove" +msgstr "" diff --git a/package.json b/package.json new file mode 100644 index 0000000..2286c7c --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "wishlist-feature-for-woocommerce", + "version": "1.0.0", + "description": "WishList feature for WooCommerce.", + "main": "gulpfile.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Uday", + "license": "GPL-3.0", + "devDependencies": { + "gulp": "^4.0.2", + "gulp-autoprefixer": "^7.0.1", + "gulp-clean-css": "^4.2.0", + "gulp-uglify": "^3.0.2" + }, + "browserslist": ["last 2 versions"] +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..0a64a50 --- /dev/null +++ b/readme.md @@ -0,0 +1,57 @@ +# Wishlist Feature For WooCommerce + +The "wishlist" is the popular tool in the e-commerce market. + + - Save the products in the list to quickly find them later. + - Easy to add favorite products in to the cart. + - Easy to keep watch on price change of favorite products. + +# Features! + + - Select a page for your wishlist. + - Add to wishlist button on the single product page. + - A shortcode to show wishlist on any page. + - Hooks to customize the columns in the wishlist. + - Directly add to cart from the wishlist. + - Price change notice in the wishlist. + - Wishlist in the WooCommerce My Account page. + +### Screenshots +Wishlist +![Wishlist shortcode](https://i.imgur.com/YBveh8H.png) + +Plugin Settings +![Plugin Settings](https://i.imgur.com/8j1VI7O.png) + +Add to wishlist +![Add to wishlist](https://i.imgur.com/YxvoDur.png) + +Added to wishlist +![Added to wishlist](https://i.imgur.com/4JRq0HE.png) + +My Account Wishlist +![My Account Wishlist](https://i.imgur.com/BiX6cIP.png) + +### Tech + +* [Gulp] - the task runner. +* "dev" version of each asset in "dev" directory under "assets/--" + +### Installation + +Follow these [steps](https://wordpress.org/support/article/managing-plugins/#installing-plugins) to install a plugin. + +### Todos + + - Multiple wishlists per user. + - Support to variable products. + - Privacy and unique wishlist URL to share with others. + - Write unit tests. + - Analytics to the admin about wishlisted products. + +License +---- + +GPL 3.0 + + [Gulp]: \ No newline at end of file diff --git a/screenshots/my-account-wishlist.png b/screenshots/my-account-wishlist.png new file mode 100755 index 0000000..e1a5bae Binary files /dev/null and b/screenshots/my-account-wishlist.png differ diff --git a/screenshots/plugin-settings.png b/screenshots/plugin-settings.png new file mode 100755 index 0000000..b4d8817 Binary files /dev/null and b/screenshots/plugin-settings.png differ diff --git a/screenshots/single-product-after-wishlist.png b/screenshots/single-product-after-wishlist.png new file mode 100755 index 0000000..0beda99 Binary files /dev/null and b/screenshots/single-product-after-wishlist.png differ diff --git a/screenshots/single-product-before-wishlist.png b/screenshots/single-product-before-wishlist.png new file mode 100755 index 0000000..0c34e8e Binary files /dev/null and b/screenshots/single-product-before-wishlist.png differ diff --git a/screenshots/wishlist-shortcode.png b/screenshots/wishlist-shortcode.png new file mode 100755 index 0000000..fb9c9b2 Binary files /dev/null and b/screenshots/wishlist-shortcode.png differ diff --git a/wishlist-feature-for-woocommerce.php b/wishlist-feature-for-woocommerce.php new file mode 100644 index 0000000..7a5c816 --- /dev/null +++ b/wishlist-feature-for-woocommerce.php @@ -0,0 +1,61 @@ +=' ) && function_exists( 'spl_autoload_register' ) ) { + require_once __DIR__ . '/includes/class-wlffwc-autoloader.php'; + + // Let's start. Who let the dogs out. Woof, woof, woof, woof, woof. + $GLOBALS['wlffwc_instance'] = WishListFFWC(); +}