In our Magento 2.2.2 installation we have removed all "Recently Viewed" and "Compared" elements from our site (removed via XML files in our custom theme). We do not need these functionalities at all and removing all elements worked fine so far. However:
The functionalitites itselves are obviously still enabled and create unnecessary output. Most important issue is that still COOKIES / HTML Local Storage are being set:
recently_viewed_product | recently_viewed_product_previous | recently_compared_product | recently_compared_product_previous
Is there a way to COMPLETELY disable these features so that there will be not output at all / no cookies will be set any longer? Or another way to just stop Magento to set these cookies?
Thanks for your help!
Alex
We also had that issue and decided to develop two open source modules to fix this:
CustomGento_RemoveProductComparison removes the product comparison from Magento completely - it also takes care of the cookies.
CustomGento_RemoveViewedProducts removes the viewed products feature from Magento completely - it also takes care of the cookies.
Technically, both modules add a mixin for Magento_Catalog/js/storage-manager. In the mixin, we extend the function prepareStoragesConfig and remove the respective configuration from the storagesConfiguration:
define([], function () {
'use strict';
return function (storageManager) {
return storageManager.extend({
prepareStoragesConfig: function () {
if (typeof this.storagesConfiguration === 'object') {
delete this.storagesConfiguration.recently_compared_product;
}
return this._super();
}
});
};
});
One thing to watch out for is that there is still the cookie product_data_storage. I am not totally sure if this is only used for the product comparison / viewed products feature and can therefore also be removed or if this needs to be kept... I tend to ignore this issue for the time being and claim that product_data_storage is a technically necessary cookie ;-)
Related
I'm making a website right now and need to use django-tracking2 for analytics. Everything works but I would like to allow users to opt out and I haven't seen any options for that. I was thinking modifying the middleware portion may work but honestly, I don't know how to go about that yet since I haven't written middleware before.
I tried writing a script to check a cookie called no_track and if it wasn't set, I would set it to false for default tracking and if they reject, it sets no_track to True but I had no idea where to implement it (other than the middle ware, when I tried that the server told me to contact the administrator). I was thinking maybe I could use signals to prevent the user being tracked but then that would slow down the webpage since it would have to deal with preventing a new Visitor instance on each page (because it would likely keep making new instances since it would seem like a new user). Could I subclass the Visitor class and modify __init__ to do a check for the cookie and either let it save or don't.
Thanks for any answers, if I find a solution I'll edit the post or post and accept the answer just in case someone else needs this.
I made a function in my tools file (holds all functions used throughout the project to make my life easier) to get and set a session key. Inside the VisitorTrackingMiddleware I used the function _should_track() and placed a check that looks for the session key (after _should_track() checks that sessions is installed and before all other checks), with the check_session() function in my tools file, if it doesn't exist, the function creates it with the default of True (Track the user until they accept or reject) and returns an HttpResponse (left over from trying the cookie method).
When I used the cookie method, the firefox console said the cookie will expire so I just switched to sessions another reason is that django-tracking2 runs on it.
It seems to work very well and it didn't have a very large impact on load times, every time a request is made, that function runs and my debug tells me if it's tracking me or not and all the buttons work through AJAX. I want to run some tests to see if this does indeed work and if so, maybe I'll submit a pull request to django-tracking2 just in case someone else wants to use it.
A Big advantage to this is that you can allow users to change their minds if they want or you can reprompt at user sign up depending on if they accepted or not. with the way check_session() is set up, I can use it in template tags and class methods as well.
Is there a common pattern for using Backbone with a service that filters a collection on the server? I haven't been able to find anything in Google and Stack Overflow searches, which is surprising, given the number of Backbone apps in production.
Suppose I'm building a new front end for Stack Overflow using Backbone.
On the search screen, I need to pass the following information to the server and get back a page worth of results.
filter criteria
sort criteria
results per page
page number
Backbone doesn't seem to have much interest in offloading filtering to the server. It expects the server to return the entire list of questions and perform filtering on the client side.
I'm guessing that in order to make this work I need to subclass Collection and override the fetch method so that rather than always GETting data from the same RESTful URL, it passes the above parameters.
I don't want to reinvent the wheel. Am I missing a feature in Backbone that would make this process simpler or more compatible with existing components? Is there already a well-established pattern to solve this problem?
If you just want to pass GET parameters on a request, you should just be able to specify them in the fetch call itself.
collection.fetch( {
data: {
sortDir: "ASC",
totalResults: 100
}
} );
The options passed into fetch should directly translate to a jQuery.ajax call, and a data property should automatically get parsed. Of course overriding the fetch method is fine too, especially if you want to standardize portions of the logic.
You're right, creating your own Collection is the way to go, as there are not standards about server pagination except OData.
Instead of overriding 'fetch', what I usually do in these cases is create a collection.url property as a function, an return the proper URL based on the collection state.
In order to do pagination, however, the server must return to you the total number of items so you can calculate how many pages based on X items per page. Nowadays some APIs are using things like HAL or HATEOAS, which are basically HTTP response headers. To get that information, I normally add a listener to the sync event, which is raised after any AJAX operation. If you need to notify external components (normally the view) of the number of available items/pages, use an event.
Simple example: your server returns X-ItemTotalCount in the response headers, and expects parameters page and items in the request querystring.
var PagedCollection = Backbone.Collection.extend({
initialize: function(models,options){
this.listenTo(this, "sync", this._parseHeaders);
this.currentPage = 0;
this.pageSize = 10;
this.itemCount = 0;
},
url: function() {
return this.baseUrl + "?page=" + this.currentPage + "&items=" + this.pageSize;
},
_parseHeaders: function(collection,response){
var totalItems = response.xhr.getResponseHeader("X-ItemTotalCount");
if(totalItems){
this.itemCount = parseInt(totalItems);
//trigger an event with arguments (collection, totalItems)
this.trigger("pages:itemcount", this, this.itemCount);
}
}
});
var PostCollection = PagedCollection.extend({
baseUrl: "/posts"
});
Notice we use another own property, baseUrl to simplify extending the PagedCollection. If you need to add your own initialize, call the parent's prototype one like this, or you won't parse the headers:
PagedCollection.protoype.initialize.apply(this,arguments)
You can even add fetchNext and fetchPrevious methods to the collection, where you simply modify this.currentPage and fetch. Remember to add {reset:true} as fetch options if you want to replace one page with the other instead of appending.
Now if your backend for the project is consistent, any resource that allows pagination on the server may be represented using one PagedCollection-based collection on the client, given the same parameters/responses are used.
I have two sites both served by one Sitecore solution, and am having a problem where the following code is forcing language embedding in the URLs of one of the two sites because the double-asterisk-enclosed piece of code below from the base LinkProvider is returning true (the "lang" cookie key returns null).
private bool EmbedLanguage()
{
if (this._options.LanguageEmbedding == LanguageEmbedding.Always)
{
return true;
}
if (this._options.LanguageEmbedding == LanguageEmbedding.Never)
{
return false;
}
SiteContext site = Context.Site;
return ((site == null) || **((WebUtil.GetOriginalCookieValue(site.GetCookieKey("lang")) == null)** || this._options.EmbedLanguage(Context.Language)));
}
This only does this for one of the two sites, and not the other. The languageEmbedding value in the linkManager in Web.config is "asNeeded." The first (behaving) site is multi-language, and the other (not behaving) is not.
It's not clear why the behaving one is getting its "lang" cookie set, and the other is not. They're both sharing quite a bit of code, and the problematic site hasn't deviated much from the original one beyond anything other than a layout, new sublayouts, etc. The guts are pretty much the same.
Any ideas would be welcomed. Perhaps it's something really obvious I'm missing?
First of all let me clarify if i have understood your question correctly.
You have 2 Sites running on same Code. (Multi-Site)
One is Multi-Language, It behaves fine (Sets the Lang cookie)
Another is Single Language, It behaves odd and does not set the Lang cookie.
How you have implemented multi-site?
It looks like you are using the mentioned function to embed language in URL in some class.
So suggesting a workaround we have implemented.
Sometime IE8 Deletes the cookie and that's the reason we have moved our multisite solution from languageEmbedding= asNeeded to Never.
We have override the LinkManager Class and when we are generating the URLs for the site we check the setting placed on the Site Home Page if the site is multi-language then we set the languageEmbedding= always. else it is already never.
This way we never face issue with language cookies.
Yes, It will not set the cookie when it is a single language site and languageEmbedding is set to never.
The way we ended up resolving this is by adding a custom "embedLanguageInUrl" attribute to the problem site in web.config, and setting it to "never." We then used a SiteContextExtensions class to expose that value (Sitecore.Context.Site.EmbedLanguageInUrl()), and then adjust the urlOptions.LanguageEmbedding value appropriately, if needed. Seems to work great. :)
i'm trying to migrate to the new Router in Ember. the use case is this: user is not logged in but requests a URL that requires login. he is redirected to a login route, after successful login he is redirected to his original destination.
i achieved this with the prior Router by overriding Router.route(path) and intercepting path requests when the app was in unauthorized state.
the new Router doesn't have a route() function, also, i don't know how to override it now that the Router instance is created automatically by Ember. i probably shouldn't do that anyway.
there is a Route.redirect() hook that looks useful. however, Route no longer extends Path in the v2 Router, so there is no Root.path, and no path information is passed into Route.redirect(), so i don't know how to save the path info for calling transitionTo() later.
i've supplied my general approach below. how can i accomplish this? it seems like a very common use case for many application.
// i imagine something like this should happen
App.AuthRequiredRoute = Ember.Route.extend({
redirect: function() {
if(!App.controllerFor('login').get('isLoggedIn')) {
var pathToSave = ????
App.controllerFor('login').set('pathAfterLogin',pathToSave);
this.transitionTo('login');
}
}
}
// and then after login, the LoginController would call App.router.transitionTo(this.pathAfterLogin)
I have done a lot of research into this myself in the last day or two. I can share with you what I have discovered, and a couple of thoughts.
First of all, you can some information regarding the current path and contexts like so:
this.router.router.currentHandlerInfos
This returns an array. Each object has both a name and a context property. The names correspond to the name of the router you would call in transitionTo.
In my opinion, although you could work with something like this, it would be messy. The API docs don't refer to this and it may not be the intention to use this as a public API.
I see issues with the above solution for deeper nested dynamic segments too. Given how new the router v2 is, I think it will continue to evolve and a better solution is likely to present itself. It's a fairly common thing to want to save the current location and return to it at a later date.
In the meantime, rather than a redirect, perhaps use a conditional block in your template that presents a login rather than an outlet if the authenticated flag is not set on the ApplicationController? I know it's not as "right" but it is "right now".
I use both Google Analytics and Google Website Optimizer on www.britely.com, and want to limit their cookies to that domain only. (To avoid cookie overhead in requests for static assets loaded from other subdomains of britely.com that should get some CDN love.)
An example page that uses both (and that currently sets the __utma, __utmb, __utmc and __utmz cookies on .britely.com, instead of on the wanted www.britely.com), is http://www.britely.com/ninjamom/s-t-dogs-think
As far as Google's docs go, it seems that a _gaq.push(['_setDomainName', 'none']); call (or ditto 'www.britely.com' instead of 'none') at the top of the page should be enough to achieve this goal.
Somehow, it isn't. I think we used to have even more cookies set on .britely.com before I read through the source of GWO's siteopt.js, which doesn't seem to know _setDomainName. It's responsive to a page-global constant _udn declaring the cookie domain it should use though - so leading in the page with this, at least GWO's __utmx and __utmxx cookies are handled correctly:
<script>
var _gaq = _gaq || [], _udn = 'www.britely.com';
_gaq.push(['_setDomainName', 'none']);
</script>
I know the common way of fixing the cookie overhead issue is to serve static content from some domain entirely different from the one using GA and GWO. That is not the solution I seek.
Besides the above tweaks, the Google Website Optimizer control script also needs its own _gaq.push(['gwo._setDomainName', 'none']); call - similar to the GA one, which only seems to be system global, but isn't.
With the above setup, all cookies get scoped to www.britely.com except for the __utmx and __utmxx ones, which end up scoped to .www.britely.com for some reason. Good enough for me.