commit 69a2269c530a0c90b3231838bebb7e51190ff51f
Author: stephaneDiot <stephane.diot@ez.no>
Date:   Thu Nov 3 09:52:18 2016 +0100

    EZP-26325: Use query and filter in PlatformUI
    
    Conflicts:
    	Tests/js/views/services/plugins/assets/ez-searchplugin-tests.js

diff --git a/Resources/public/js/models/ez-contentmodel.js b/Resources/public/js/models/ez-contentmodel.js
index cc8cf26..de9e869 100644
--- a/Resources/public/js/models/ez-contentmodel.js
+++ b/Resources/public/js/models/ez-contentmodel.js
@@ -282,9 +282,9 @@ YUI.add('ez-contentmodel', function (Y) {
                     'LocationQuery'
                 );
 
-            query.body.ViewInput.LocationQuery.Criteria = {
+            query.setFilter({
                 ContentIdCriterion: this.get('contentId')
-            };
+            });
 
             contentService.createView(
                 query,
diff --git a/Resources/public/js/models/ez-locationmodel.js b/Resources/public/js/models/ez-locationmodel.js
index 7da90b4..6ebac98 100644
--- a/Resources/public/js/models/ez-locationmodel.js
+++ b/Resources/public/js/models/ez-locationmodel.js
@@ -311,9 +311,9 @@ YUI.add('ez-locationmodel', function (Y) {
             var contentService = options.api.getContentService(),
                 query = contentService.newViewCreateStruct('ancestors-' + this.get('locationId'), 'LocationQuery');
 
-            query.body.ViewInput.LocationQuery.Criteria = {
+            query.setFilter({
                 AncestorCriterion: this.get('pathString')
-            };
+            });
 
             contentService.createView(
                 query,
diff --git a/Resources/public/js/views/dashboard/ez-dashboardblockallcontentview.js b/Resources/public/js/views/dashboard/ez-dashboardblockallcontentview.js
index a73bd9a..11e35a2 100644
--- a/Resources/public/js/views/dashboard/ez-dashboardblockallcontentview.js
+++ b/Resources/public/js/views/dashboard/ez-dashboardblockallcontentview.js
@@ -70,7 +70,7 @@ YUI.add('ez-dashboardblockallcontentview', function (Y) {
                 resultAttribute: 'items',
                 loadContentType: true,
                 search: {
-                    criteria: {SubtreeCriterion: rootLocation.get('pathString')},
+                    filter: {SubtreeCriterion: rootLocation.get('pathString')},
                     sortClauses: {DateModified: 'descending'},
                     limit: 10
                 }
diff --git a/Resources/public/js/views/dashboard/ez-dashboardblockmycontentview.js b/Resources/public/js/views/dashboard/ez-dashboardblockmycontentview.js
index 18470c8..2185175 100644
--- a/Resources/public/js/views/dashboard/ez-dashboardblockmycontentview.js
+++ b/Resources/public/js/views/dashboard/ez-dashboardblockmycontentview.js
@@ -75,7 +75,7 @@ YUI.add('ez-dashboardblockmycontentview', function (Y) {
                 resultAttribute: 'items',
                 loadContentType: true,
                 search: {
-                    criteria: { UserMetadataCriterion: {
+                    filter: { UserMetadataCriterion: {
                         Target: "modifier",
                         Value: user.get('userId'),
                     }},
diff --git a/Resources/public/js/views/fields/richtext/ez-richtext-resolveembed.js b/Resources/public/js/views/fields/richtext/ez-richtext-resolveembed.js
index 27801e2..b4f3860 100644
--- a/Resources/public/js/views/fields/richtext/ez-richtext-resolveembed.js
+++ b/Resources/public/js/views/fields/richtext/ez-richtext-resolveembed.js
@@ -150,7 +150,7 @@ YUI.add('ez-richtext-resolveembed', function (Y) {
         view.fire('contentSearch', {
             viewName: 'resolveembed-field-' + view.get('field').id,
             search: {
-                criteria: {'ContentIdCriterion': Object.keys(mapNode).join(',')},
+                filter: {'ContentIdCriterion': Object.keys(mapNode).join(',')},
                 offset: 0,
             },
             callback: Y.bind(this._renderEmbed, this, mapNode),
diff --git a/Resources/public/js/views/fields/richtext/ez-richtext-resolveimage.js b/Resources/public/js/views/fields/richtext/ez-richtext-resolveimage.js
index a06dbaa..8f423bc 100644
--- a/Resources/public/js/views/fields/richtext/ez-richtext-resolveimage.js
+++ b/Resources/public/js/views/fields/richtext/ez-richtext-resolveimage.js
@@ -60,7 +60,7 @@ YUI.add('ez-richtext-resolveimage', function (Y) {
         view.fire('contentSearch', {
             viewName: 'resolveimage-field-' + view.get('field').id,
             search: {
-                criteria: {'ContentIdCriterion': Object.keys(mapNode).join(',')},
+                filter: {'ContentIdCriterion': Object.keys(mapNode).join(',')},
                 offset: 0,
             },
             loadContent: true,
diff --git a/Resources/public/js/views/services/ez-searchviewservice.js b/Resources/public/js/views/services/ez-searchviewservice.js
index 457e893..f4a0116 100644
--- a/Resources/public/js/views/services/ez-searchviewservice.js
+++ b/Resources/public/js/views/services/ez-searchviewservice.js
@@ -43,11 +43,11 @@ YUI.add('ez-searchviewservice', function (Y) {
                     'limit',
                     this.get('request').params.limit ?  Number(this.get('request').params.limit) : this.get('loadMoreAddingNumber')
                 );
-                this.search.findLocations({
+                this.search.findContent({
                     viewName: 'search-' + this.get('searchString'),
-                    loadContent: true,
+                    loadLocation: true,
                     loadContentType: true,
-                    criteria: {
+                    query: {
                         "FullTextCriterion": this.get('searchString'),
                     },
                     limit: this.get('limit'),
diff --git a/Resources/public/js/views/services/plugins/ez-contenttreeplugin.js b/Resources/public/js/views/services/plugins/ez-contenttreeplugin.js
index 38c04b5..4367462 100644
--- a/Resources/public/js/views/services/plugins/ez-contenttreeplugin.js
+++ b/Resources/public/js/views/services/plugins/ez-contenttreeplugin.js
@@ -52,7 +52,7 @@ YUI.add('ez-contenttreeplugin', function (Y) {
 
             this.get('host').search.findLocations({
                 viewName: 'children_' + levelLocation.get('locationId'),
-                criteria: {
+                filter: {
                     "ParentLocationIdCriterion": levelLocation.get('locationId'),
                 },
                 sortLocation: levelLocation,
@@ -168,9 +168,9 @@ YUI.add('ez-contenttreeplugin', function (Y) {
             console.log('[DEPRECATED] `_loadContents` method is deprecated');
             console.log('[DEPRECATED] it will be removed from PlatformUI 2.0');
             query = contentService.newViewCreateStruct('children_content' + levelLocation.get('locationId'), 'ContentQuery');
-            query.body.ViewInput.ContentQuery.Criteria = {
+            query.setFilter({
                 "ParentLocationIdCriterion": levelLocation.get('locationId'),
-            };
+            });
 
             contentService.createView(query, function (err, response) {
                 if ( err ) {
diff --git a/Resources/public/js/views/services/plugins/ez-searchplugin.js b/Resources/public/js/views/services/plugins/ez-searchplugin.js
index f2f0328..eccbaf9 100644
--- a/Resources/public/js/views/services/plugins/ez-searchplugin.js
+++ b/Resources/public/js/views/services/plugins/ez-searchplugin.js
@@ -46,7 +46,9 @@ YUI.add('ez-searchplugin', function (Y) {
          * @param {String} name
          * @param {String} type
          * @param {Object} search
-         * @param {Object} search.criteria
+         * @param {Object} [search.criteria]
+         * @param {Object} [search.query]
+         * @param {Object} [search.filter]
          * @param {Object} search.sortClauses
          * @param {Number} [search.limit]
          * @param {Number} [search.offset]
@@ -59,13 +61,24 @@ YUI.add('ez-searchplugin', function (Y) {
             query = contentService.newViewCreateStruct(name, type);
             // TODO ViewCreateStruct should expose an API
             // see https://jira.ez.no/browse/EZP-24808
-            query.body.ViewInput[type].Criteria = search.criteria;
-            query.body.ViewInput[type].offset = search.offset;
-            query.body.ViewInput[type].limit = search.limit;
+            if (search.query) {
+                query.setQuery(search.query);
+            }
+            if (search.filter) {
+                query.setFilter(search.filter);
+            }
+            if (search.criteria) {
+                query.setCriteria(search.criteria);
+                console.log('[DEPRECATED] Criteria property is deprecated');
+                console.log('[DEPRECATED] it will be removed from PlatformUI 2.0');
+                console.log('[DEPRECATED] Please use Query or Filter instead');
+            }
+
+            query.setLimitAndOffset(search.limit, search.offset);
             if ( search.sortClauses ) {
-                query.body.ViewInput[type].SortClauses = search.sortClauses;
+                query.setSortClauses(search.sortClauses);
             } else if ( search.sortLocation ) {
-                query.body.ViewInput[type].SortClauses = search.sortLocation.getSortClause();
+                query.setSortClauses(search.sortLocation.getSortClause());
             }
 
             return query;
@@ -94,27 +107,61 @@ YUI.add('ez-searchplugin', function (Y) {
          * @param {Number} e.callback.count the total number of search result
          */
         _doContentSearch: function (e) {
-            var query = this._createNewCreateViewStruct(e.viewName, 'ContentQuery', e.search),
+            var search = Y.merge(e.search);
+
+            search.viewName = e.viewName;
+            search.loadContentType = e.loadContentType;
+            search.loadLocation = e.loadLocation;
+            delete search.callback;
+            this.findContent(search, e.callback);
+        },
+
+        /**
+         * Executes a Content search based on the provided `search` object.
+         *
+         * @method findContent
+         * @param {Object} search
+         * @param {String} search.viewName the name of the REST view to use
+         * @param {Object} search.criteria (deprecated) the search criteria used as Criteria in ContentQuery
+         * @param {Object} search.query  the search query used as Query in ContentQuery
+         * @param {Object} search.filter the search filter used as Filter in ContentQuery
+         * @param {Object} [search.sortClauses] the sort clauses
+         * @param {Number} [search.offset]
+         * @param {Number} [search.limit]
+         * @param {Boolean} [search.loadContentType] flag indicating whether the
+         * Content Type of each result has to be loaded in addition
+         * @param {Function} callback
+         * @param {Error|null} callback.error
+         * @param {Response|Array} callback.result the Response object in case
+         * of error or an array of Content struct. A Content struct is object
+         * containing the Content item and the Content
+         * Type depending on the `loadContentType` flag.
+         * @param {Number} callback.resultCount the total result number of the
+         * search
+         */
+        findContent: function (search, callback) {
+            var query = this._createNewCreateViewStruct(search.viewName, 'ContentQuery', search),
                 contentService = this._getContentService();
 
             contentService.createView(query, Y.bind(function (error, result) {
-                var parsedResult,
-                    endContentTypeLoad;
+                var parsedResult;
 
                 if ( error ) {
-                    return e.callback(error);
+                    return callback(error);
                 }
                 parsedResult = this._parseSearchResult(result, 'content', '_createContent');
-                if ( e.loadContentType ) {
-                    endContentTypeLoad = function (error, response) {
-                        e.callback(error, parsedResult, result.document.View.Result.count);
-                    };
-
-                    this._loadContentTypeForStruct(parsedResult, function (struct) {
-                        return struct.content.get('resources').ContentType;
-                    }, endContentTypeLoad);
+                if (parsedResult.length && (search.loadLocation || search.loadContentType)) {
+                    this._loadContentResources(
+                        search.viewName,
+                        search.loadContentType,
+                        search.loadLocation,
+                        parsedResult,
+                        function (error, structs) {
+                            callback(error, structs, result.document.View.Result.count);
+                        }
+                    );
                 } else {
-                    e.callback(error, parsedResult, result.document.View.Result.count);
+                    callback(error, parsedResult, result.document.View.Result.count);
                 }
             }, this));
         },
@@ -125,7 +172,9 @@ YUI.add('ez-searchplugin', function (Y) {
          * @method findLocations
          * @param {Object} search
          * @param {String} search.viewName the name of the REST view to use
-         * @param {Object} search.criteria the search criteria used as Criteria in LocationQuery
+         * @param {Object} search.criteria (deprecated) the search criteria used as Criteria in LocationQuery
+         * @param {Object} search.query  the search query used as Query in LocationQuery
+         * @param {Object} search.filter the search filter used as Filter in LocationQuery
          * @param {Object} [search.sortClauses] the sort clauses
          * @param {eZ.Location} [search.sortLocation] the Location to use to
          * generate the sort clauses
@@ -157,7 +206,7 @@ YUI.add('ez-searchplugin', function (Y) {
                 }
                 parsedResult = this._parseSearchResult(result, 'location', '_createLocation');
                 if (parsedResult.length && (search.loadContentType || search.loadContent)) {
-                    this._loadResources(
+                    this._loadLocationResources(
                         search.viewName,
                         search.loadContentType,
                         search.loadContent,
@@ -180,7 +229,9 @@ YUI.add('ez-searchplugin', function (Y) {
          * @protected
          * @param {EventFacade} e
          * @param {Object} e.search
-         * @param {Object} e.search.criteria the search criteria used as Criteria in LocationQuery
+         * @param {Object} e.search.criteria (deprecated) the search criteria used as Criteria in LocationQuery
+         * @param {Object} e.search.query  the search query used as Query in LocationQuery
+         * @param {Object} e.search.filter the search filter used as Filter in LocationQuery
          * @param {Object} [e.search.sortClauses] the search sort clauses
          * @param {eZ.Location} [search.sortLocation] the Location to use to
          * generate the sort clauses
@@ -274,7 +325,6 @@ YUI.add('ez-searchplugin', function (Y) {
             return content;
         },
 
-
         /**
          * Loads resources for the given array of location structs. Depending on given
          * `loadContentType` and `loadContent` bool parameters it loads Content and ContentType
@@ -282,12 +332,31 @@ YUI.add('ez-searchplugin', function (Y) {
          *
          * @method _loadResources
          * @protected
+         * @deprecated
          * @param {Bool|undefined} loadContentType
          * @param {Bool|undefined} loadContent
          * @param {Array} locationStructArr
          * @param {Function} callback
          */
         _loadResources: function (viewName, loadContentType, loadContent, locationStructArr, callback) {
+            console.log('[DEPRECATED] _loadResources is deprecated, it will be removed from PlatformUI 2.0');
+            console.log('[DEPRECATED] Use _loadLocationResources instead');
+            this._loadLocationResources(viewName, loadContentType, loadContentType, locationStructArr, callback);
+        },
+
+        /**
+         * Loads resources for the given array of location structs. Depending on
+         * given `loadContentType` and `loadContent` bool parameters it loads
+         * Content and ContentType and includes them into the location structs.
+         *
+         * @method _loadLocationResources
+         * @protected
+         * @param {Bool|undefined} loadContentType
+         * @param {Bool|undefined} loadContent
+         * @param {Array} locationStructArr
+         * @param {Function} callback
+         */
+        _loadLocationResources: function (viewName, loadContentType, loadContent, locationStructArr, callback) {
             var tasks = new Y.Parallel(),
                 loadResourcesError = false;
 
@@ -321,6 +390,51 @@ YUI.add('ez-searchplugin', function (Y) {
         },
 
         /**
+         * Loads resources for the given array of content structs. Depending on
+         * given `loadContentType` and `loadLocation` bool parameters it loads
+         * ContentType and Location and includes them into the content structs.
+         *
+         * @method _loadContentResources
+         * @protected
+         * @param {Bool|undefined} loadContentType
+         * @param {Bool|undefined} loadLocation
+         * @param {Array} contentStructArr
+         * @param {Function} callback
+         */
+        _loadContentResources: function (viewName, loadContentType, loadLocation, contentStructArr, callback) {
+            var tasks = new Y.Parallel(),
+                loadResourcesError = false;
+
+            if (loadContentType) {
+                var endContentTypeLoad = tasks.add(function (error) {
+                        if (error) {
+                            loadResourcesError = error;
+                            return;
+                        }
+                    });
+
+                this._loadContentTypeForStruct(contentStructArr, function (struct) {
+                    return struct.content.get('resources').ContentType;
+                }, endContentTypeLoad);
+            }
+
+            if (loadLocation) {
+                var endContentLoad = tasks.add(function (error) {
+                        if (error) {
+                            loadResourcesError = error;
+                            return;
+                        }
+                    });
+
+                this._loadLocationForContent(viewName, contentStructArr, endContentLoad);
+            }
+
+            tasks.done(function () {
+                callback(loadResourcesError, contentStructArr);
+            });
+        },
+
+        /**
          * Loads ContentType for each location struct in given array and puts it in the new `contentType`
          * field for the location struct.
          *
@@ -402,7 +516,7 @@ YUI.add('ez-searchplugin', function (Y) {
             });
 
             query = this._createNewCreateViewStruct('contents-loading-' + viewName, 'ContentQuery', {
-                criteria: {
+                filter: {
                     "ContentIdCriterion": contentIds
                 },
                 // In case we are asking for more then 25 items which is default limit, specify limit
@@ -414,7 +528,7 @@ YUI.add('ez-searchplugin', function (Y) {
                     callback(err, locationStructArr);
                     return;
                 }
-                Y.Array.each(response.document.View.Result.searchHits.searchHit, function (hit, i) {
+                Y.Array.each(response.document.View.Result.searchHits.searchHit, function (hit) {
                     var content = this._createContent(hit),
                         locationIndexes = contentIdsLocationIndexMap[content.get('contentId')];
 
@@ -425,6 +539,63 @@ YUI.add('ez-searchplugin', function (Y) {
                 callback(err, locationStructArr);
             }, this));
         },
+
+        /**
+         * Loads Location for each content struct in given array and puts it in the new `location`
+         * field for the content struct.
+         *
+         * @method _loadLocationForContent
+         * @protected
+         * @param {Array|Null} contentStructArr
+         * @param {Function} callback
+         */
+        _loadLocationForContent: function (viewName, contentStructArr, callback) {
+            var contentService = this._getContentService(),
+                locationsIds,
+                query;
+
+            locationsIds = Y.Array.reduce(contentStructArr, '', function (previousId, struct) {
+                var locationId;
+
+                if (struct.content.get('resources').MainLocation) {
+                    locationId = struct.content.get('resources').MainLocation.split('/').pop();
+
+                    previousId = previousId ? previousId + ',' : previousId;
+                    return previousId + locationId;
+                }
+                return;
+            });
+
+            if (locationsIds) {
+                query = this._createNewCreateViewStruct('locations-loading-' + viewName, 'LocationQuery', {
+                    filter: {
+                        "LocationIdCriterion": locationsIds,
+                    },
+                    limit: contentStructArr.length
+                });
+
+                contentService.createView(query, Y.bind(function (err, response) {
+                    if (err) {
+                        callback(err, contentStructArr);
+                        return;
+                    }
+                    Y.Array.each(response.document.View.Result.searchHits.searchHit, function (hit) {
+                        var location = this._createLocation(hit);
+
+                        Y.Array.some(contentStructArr, function (struct) {
+                            if ( struct.content.get('resources').MainLocation === location.get('id') ) {
+                                struct.location = location;
+                                return true;
+                            }
+                            return false;
+                        });
+                    }, this);
+                    callback(err, contentStructArr);
+                }, this));
+            } else {
+                callback(false, contentStructArr);
+            }
+        },
     }, {
         NS: 'search',
 
diff --git a/Resources/public/js/views/subitem/ez-asynchronoussubitemview.js b/Resources/public/js/views/subitem/ez-asynchronoussubitemview.js
index f03a13c..570af86 100644
--- a/Resources/public/js/views/subitem/ez-asynchronoussubitemview.js
+++ b/Resources/public/js/views/subitem/ez-asynchronoussubitemview.js
@@ -122,7 +122,7 @@ YUI.add('ez-asynchronoussubitemview', function (Y) {
                 loadContentType: true,
                 loadContent: true,
                 search: {
-                    criteria: {
+                    filter: {
                         "ParentLocationIdCriterion": this.get('location').get('locationId'),
                     },
                     offset: forceLimit ? 0 : this.get('offset'),
diff --git a/Resources/public/js/views/universaldiscovery/ez-universaldiscoverysearchview.js b/Resources/public/js/views/universaldiscovery/ez-universaldiscoverysearchview.js
index 5806257..392675e 100644
--- a/Resources/public/js/views/universaldiscovery/ez-universaldiscoverysearchview.js
+++ b/Resources/public/js/views/universaldiscovery/ez-universaldiscoverysearchview.js
@@ -167,7 +167,7 @@ YUI.add('ez-universaldiscoverysearchview', function (Y) {
                     loadContent: this.get('loadContent'),
                     loadContentType: true,
                     search: {
-                        criteria: {
+                        query: {
                             "FullTextCriterion": searchText,
                         },
                         offset: this.get('offset'),
diff --git a/Tests/js/models/assets/ez-contentmodel-tests.js b/Tests/js/models/assets/ez-contentmodel-tests.js
index a2179e9..38454b0 100644
--- a/Tests/js/models/assets/ez-contentmodel-tests.js
+++ b/Tests/js/models/assets/ez-contentmodel-tests.js
@@ -819,16 +819,19 @@ YUI.add('ez-contentmodel-tests', function (Y) {
                     }
                 }
             };
-
-            this.query = {
-                'body': {
-                    ViewInput: {
-                        'LocationQuery': {
-                            'Criteria' : ""
-                        }
-                    }
-                }
-            };
+            
+            this.query = new Y.Mock();
+            Mock.expect(this.query, {
+                method: 'setFilter',
+                args: [Mock.Value.Object],
+                run: Y.bind(function (arg) {
+                    Assert.areSame(
+                        arg.ContentIdCriterion,
+                        this.contentId,
+                        'Parameter should have the contentId as ContentIdCriterion'
+                    );
+                }, this),
+            });
 
             Mock.expect(this.contentService, {
                 method: 'newViewCreateStruct',
@@ -880,6 +883,7 @@ YUI.add('ez-contentmodel-tests', function (Y) {
             });
 
             Assert.isTrue(callbackCalled, 'Should call callback function');
+            Mock.verify(this.query);
         },
 
         'Should pass error to callback function when CAPI loadLocations fails': function () {
@@ -909,6 +913,7 @@ YUI.add('ez-contentmodel-tests', function (Y) {
             });
 
             Assert.isTrue(callbackCalled, 'Should call callback function');
+            Mock.verify(this.query);
         },
     });
 
diff --git a/Tests/js/models/assets/ez-locationmodel-tests.js b/Tests/js/models/assets/ez-locationmodel-tests.js
index 665acd5..70f09fb 100644
--- a/Tests/js/models/assets/ez-locationmodel-tests.js
+++ b/Tests/js/models/assets/ez-locationmodel-tests.js
@@ -765,16 +765,19 @@ YUI.add('ez-locationmodel-tests', function (Y) {
                     }
                 }
             };
-            this.viewCreateStruct = {
-                body: {
-                    ViewInput: {
-                        LocationQuery : {
-
-                        }
-                    }
-                }
-            };
+            this.viewCreateStruct = new Y.Mock();
 
+            Mock.expect(this.viewCreateStruct, {
+                method: 'setFilter',
+                args: [Mock.Value.Object],
+                run: Y.bind(function (arg) {
+                    Assert.areSame(
+                        arg.AncestorCriterion,
+                        this.pathString,
+                        'Parameter should have the pathString as AncestorCriterion'
+                    );
+                }, this),
+            });
             Y.Mock.expect(this.capiMock, {
                 method: 'getContentService',
                 returns: this.contentService,
@@ -835,15 +838,7 @@ YUI.add('ez-locationmodel-tests', function (Y) {
                 method: 'createView',
                 args: [Y.Mock.Value.Object, Y.Mock.Value.Function],
                 run: function (query, callback) {
-                    Assert.isString(
-                        query.body.ViewInput.LocationQuery.Criteria.AncestorCriterion,
-                        "The query should contain AncestorCriterion"
-                    );
-                    Assert.areSame(
-                        query.body.ViewInput.LocationQuery.Criteria.AncestorCriterion,
-                        that.pathString,
-                        "The AncestorCriterion of query should be set to the location's pathString"
-                    );
+                    Mock.verify(that.viewCreateStruct);
                     callback(false, that.loadAncestorsResponse);
                 }
             });
diff --git a/Tests/js/views/dashboard/assets/ez-dashboardblockallcontentview-tests.js b/Tests/js/views/dashboard/assets/ez-dashboardblockallcontentview-tests.js
index 9cf0b3b..2ca0fb4 100644
--- a/Tests/js/views/dashboard/assets/ez-dashboardblockallcontentview-tests.js
+++ b/Tests/js/views/dashboard/assets/ez-dashboardblockallcontentview-tests.js
@@ -129,7 +129,7 @@ YUI.add('ez-dashboardblockallcontentview-tests', function (Y) {
                 );
                 Assert.areSame(
                     view.get('rootLocation').get('pathString'),
-                    event.search.criteria.SubtreeCriterion,
+                    event.search.filter.SubtreeCriterion,
                     'Should pass a correct search `SubtreeCriterion` criterion value'
                 );
                 Assert.areEqual(
diff --git a/Tests/js/views/dashboard/assets/ez-dashboardblockmycontentview-tests.js b/Tests/js/views/dashboard/assets/ez-dashboardblockmycontentview-tests.js
index 0d72470..14f31ef 100644
--- a/Tests/js/views/dashboard/assets/ez-dashboardblockmycontentview-tests.js
+++ b/Tests/js/views/dashboard/assets/ez-dashboardblockmycontentview-tests.js
@@ -129,12 +129,12 @@ YUI.add('ez-dashboardblockmycontentview-tests', function (Y) {
                 );
                 Assert.areSame(
                     view.get('currentUser').get('userId'),
-                    event.search.criteria.UserMetadataCriterion.Value,
+                    event.search.filter.UserMetadataCriterion.Value,
                     'Should pass a correct search `UserMetaData` criterion value'
                 );
                 Assert.areSame(
                     userMetaDataCriterionTarget,
-                    event.search.criteria.UserMetadataCriterion.Target,
+                    event.search.filter.UserMetadataCriterion.Target,
                     'Should pass a correct search `UserMetaData` criterion value'
                 );
                 Assert.areEqual(
diff --git a/Tests/js/views/fields/richtext/assets/ez-richtext-resolveembed-tests.js b/Tests/js/views/fields/richtext/assets/ez-richtext-resolveembed-tests.js
index 72cdcb5..47a2e31 100644
--- a/Tests/js/views/fields/richtext/assets/ez-richtext-resolveembed-tests.js
+++ b/Tests/js/views/fields/richtext/assets/ez-richtext-resolveembed-tests.js
@@ -67,7 +67,7 @@ YUI.add('ez-richtext-resolveembed-tests', function (Y) {
 
                 Assert.areEqual(
                     "41,42",
-                    e.search.criteria.ContentIdCriterion,
+                    e.search.filter.ContentIdCriterion,
                     "The content should be loaded by id"
                 );
             });
diff --git a/Tests/js/views/fields/richtext/assets/ez-richtext-resolveimage-tests.js b/Tests/js/views/fields/richtext/assets/ez-richtext-resolveimage-tests.js
index c7016a6..0d0f845 100644
--- a/Tests/js/views/fields/richtext/assets/ez-richtext-resolveimage-tests.js
+++ b/Tests/js/views/fields/richtext/assets/ez-richtext-resolveimage-tests.js
@@ -78,7 +78,7 @@ YUI.add('ez-richtext-resolveimage-tests', function (Y) {
 
                 Assert.areEqual(
                     "41,42",
-                    e.search.criteria.ContentIdCriterion,
+                    e.search.filter.ContentIdCriterion,
                     "The content should be loaded by id"
                 );
                 Assert.isTrue(
diff --git a/Tests/js/views/services/assets/ez-searchviewservice-tests.js b/Tests/js/views/services/assets/ez-searchviewservice-tests.js
index 635072e..986a052 100644
--- a/Tests/js/views/services/assets/ez-searchviewservice-tests.js
+++ b/Tests/js/views/services/assets/ez-searchviewservice-tests.js
@@ -46,7 +46,7 @@ YUI.add('ez-searchviewservice-tests', function (Y) {
                 that = this;
 
             Mock.expect(this.service.search, {
-                method: 'findLocations',
+                method: 'findContent',
                 args: [Mock.Value.Object, Mock.Value.Function],
                 run: function (search, callback) {
                     callback(false, that.searchResultList, that.searchResultCount);
@@ -89,7 +89,7 @@ YUI.add('ez-searchviewservice-tests', function (Y) {
             this.service.set('request', this.request);
 
             Mock.expect(this.service.search, {
-                method: 'findLocations',
+                method: 'findContent',
                 args: [Mock.Value.Object, Mock.Value.Function],
                 callCount: 0,
 
@@ -109,7 +109,7 @@ YUI.add('ez-searchviewservice-tests', function (Y) {
             var that = this;
 
             Mock.expect(this.service.search, {
-                method: 'findLocations',
+                method: 'findContent',
                 args: [Mock.Value.Object, Mock.Value.Function],
                 run: function (search, callback) {
                     callback(false, that.searchResultList, that.searchResultCount);
@@ -153,7 +153,7 @@ YUI.add('ez-searchviewservice-tests', function (Y) {
 
             this.service.search = new Mock();
             Mock.expect(this.service.search, {
-                method: 'findLocations',
+                method: 'findContent',
                 args: [Mock.Value.Object, Mock.Value.Function],
             });
         },
@@ -215,7 +215,7 @@ YUI.add('ez-searchviewservice-tests', function (Y) {
 
             this.service.search = new Mock();
             Mock.expect(this.service.search, {
-                method: 'findLocations',
+                method: 'findContent',
                 args: [Mock.Value.Object, Mock.Value.Function],
             });
         },
diff --git a/Tests/js/views/services/plugins/assets/ez-contenttreeplugin-tests.js b/Tests/js/views/services/plugins/assets/ez-contenttreeplugin-tests.js
index 8297c65..0c6ce61 100644
--- a/Tests/js/views/services/plugins/assets/ez-contenttreeplugin-tests.js
+++ b/Tests/js/views/services/plugins/assets/ez-contenttreeplugin-tests.js
@@ -115,7 +115,7 @@ YUI.add('ez-contenttreeplugin-tests', function (Y) {
             );
             Assert.areEqual(
                 location.get('locationId'),
-                search.criteria.ParentLocationIdCriterion,
+                search.filter.ParentLocationIdCriterion,
                 "The ParentLocationIdCriterion should be used in the search"
             );
             Assert.areSame(
diff --git a/Tests/js/views/services/plugins/assets/ez-searchplugin-tests.js b/Tests/js/views/services/plugins/assets/ez-searchplugin-tests.js
index 96e82d8..b738fdc 100644
--- a/Tests/js/views/services/plugins/assets/ez-searchplugin-tests.js
+++ b/Tests/js/views/services/plugins/assets/ez-searchplugin-tests.js
@@ -3,45 +3,147 @@
  * For full copyright and license information view LICENSE file distributed with this source code.
  */
 YUI.add('ez-searchplugin-tests', function (Y) {
-    var locationSearchTests, locationSearchEventTests,
-        contentSearchTests, loadResourcesTests, registerTest,
-        Assert = Y.Assert, Mock = Y.Mock;
+    var locationSearchTests, locationSearchEventTests, _configureQueryAndContentServiceMock,
+        contentSearchTests, contentSearchEventTests, loadResourcesTests, registerTest,
+        Assert = Y.Assert, Mock = Y.Mock,
+        contentSearchSetUp, _configureContentSearchQueryMock;
+
+    _configureQueryAndContentServiceMock = function (context, queryMethodName) {
+        Mock.expect(context.query, {
+            method: queryMethodName,
+            args: [Mock.Value.Object],
+        });
+
+        Mock.expect(context.contentService, {
+            method: 'createView',
+            args: [context.query, Mock.Value.Function],
+        });
+    };
+
+    _configureContentSearchQueryMock = function (query, locationQuery, response) {
+        Mock.expect(query, {
+            method: 'setFilter',
+            args: [Mock.Value.Object],
+        });
+        Mock.expect(locationQuery, {
+            method: 'setLimitAndOffset',
+            args: [response.document.View.Result.searchHits.searchHit.length, undefined],
+        });
+        Mock.expect(locationQuery, {
+            method: 'setFilter',
+            args: [Mock.Value.Object],
+        });
+    };
+
+    contentSearchSetUp = function () {
+        var that = this;
+        this.LocationModelConstructor = function () {};
+        this.isFirstContent = true;
+        this.isContentWithoutLocation = false;
+        this.Content = function () {
+            this.get = function (whatever) {
+                if (!that.isContentWithoutLocation) {
+                    if (that.isFirstContent) {
+                        that.isFirstContent = false;
+                        return {ContentType: that.contentTypeId, MainLocation: that.locationId};
 
-    contentSearchTests = new Y.Test.Case({
-        name: "eZ Search Plugin content search tests",
-
-        setUp: function () {
-            this.Content = function () {};
-            this.Content.prototype.loadFromHash = function (hash) {
-                this.hash = hash;
+                    } else {
+                        that.isFirstContent = true;
+                        return {ContentType: that.contentTypeId, MainLocation: that.locationId2};
+                    }
+                }
+                else {
+                    return {ContentType: that.contentTypeId, MainLocation: ''};
+                }
             };
-            this.Content.prototype.get = Y.bind(function (whatever) {
-                return {ContentType: this.contentTypeId};
-            }, this);
-            this.contentTypeId = 'this/is/my/content/type/id';
-            this.capi = new Mock();
-            this.contentService = new Mock();
-            this.viewName = 'REST-View-Name';
-            this.query = {body: {ViewInput: {ContentQuery: {}}}};
+        };
+
+        this.Content.prototype.loadFromHash = function (hash) {
+            this.hash = hash;
+        };
+
+        this.contentTypeId = 'this/is/my/content/type/id';
+        this.locationId = 'Am/I/really/doing/Damien/unit/tests/?/id1';
+        this.locationId2 = 'Karma/is/a/b***h/!/id2';
+        this.limit = 42;
+        this.offset = 69;
+        this.capi = new Mock();
+        this.contentService = new Mock();
+        this.viewName = 'REST-View-Name';
+        this.query = new Y.Mock();
+        this.locationQuery = new Y.Mock();
+        this.sortClauses = {};
+
+        this.LocationModelConstructor.prototype.loadFromHash = function (hash) {
+            this.hash = hash;
+            this.get = function (attr) {
+                switch (attr) {
+                    case 'id':
+                        return that.locationId;
+                    default:
+                        Assert.fail('Requested attribute "' + attr + '" does not exist in the location model');
+                        break;
+                }
+            };
+        };
+        Mock.expect(this.query, {
+            method: 'setLimitAndOffset',
+            args: [this.limit, this.offset],
+        });
+        Mock.expect(this.query, {
+            method: 'setSortClauses',
+            args: [Mock.Value.Object],
+            run: Y.bind(function (arg) {
+                Assert.areSame(
+                    this.sortClauses,
+                    arg,
+                    "method argument should be the sortClauses"
+                );
+            }, this)
+        });
+        Mock.expect(this.capi, {
+            method: 'getContentService',
+            returns: this.contentService,
+        });
+        Mock.expect(this.contentService, {
+            method: 'newViewCreateStruct',
+            args: [Mock.Value.String, Mock.Value.String],
+            run: Y.bind(function (viewName, queryType) {
+                if (queryType == 'LocationQuery') {
+                    Assert.areSame(
+                        viewName,
+                        'locations-loading-' + this.viewName,
+                        "Should have a view name"
+                    );
+                    return this.locationQuery;
+                } else if (queryType == 'ContentQuery') {
+                    Assert.areSame(
+                        viewName,
+                        this.viewName,
+                        "Should have a view name"
+                    );
+                    return this.query;
+                } else {
+                    Assert.fail('Wrong parameters');
+                }
 
-            Mock.expect(this.capi, {
-                method: 'getContentService',
-                returns: this.contentService,
-            });
-            Mock.expect(this.contentService, {
-                method: 'newViewCreateStruct',
-                args: [this.viewName, 'ContentQuery'],
-                returns: this.query,
-            });
-            this.service = new Y.Base();
-            this.service.set('capi', this.capi);
-            this.view = new Y.Base({bubbleTargets: this.service});
-            this.plugin = new Y.eZ.Plugin.Search({
-                host: this.service,
-                contentModelConstructor: this.Content,
-            });
-            Y.eZ.ContentType = Y.Model;
-        },
+            }, this)
+
+        });
+        this.service = new Y.Base();
+        this.service.set('capi', this.capi);
+        this.plugin = new Y.eZ.Plugin.Search({
+            host: this.service,
+            contentModelConstructor: this.Content,
+            locationModelConstructor: this.LocationModelConstructor,
+        });
+        Y.eZ.ContentType = Y.Model;
+    };
+
+    contentSearchTests = new Y.Test.Case({
+        name: "eZ Search Plugin findContent tests",
+
+        setUp: contentSearchSetUp,
 
         tearDown: function () {
             this.service.destroy();
@@ -54,46 +156,477 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             delete Y.eZ.ContentType;
         },
 
-        "Should create a content search query": function () {
-            var criteria = {}, sortClauses = {},
-                offset = 42, limit = 43;
+        "Should create a content search query with criteria": function () {
+            var criteria = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setCriteria');
+            this.plugin.findContent({
+                viewName: this.viewName,
+                criteria: criteria,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
+            }, function () {});
+            Mock.verify(this.query);
+        },
+
+        "Should create a content search query with query": function () {
+            var query = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setQuery');
+            this.plugin.findContent({
+                viewName: this.viewName,
+                query: query,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
+            }, function () {});
+            Mock.verify(this.query);
+        },
+
+        "Should create a content search query with filter": function () {
+            var filter = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setFilter');
+            this.plugin.findContent({
+                viewName: this.viewName,
+                filter: filter,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
+            }, function () {});
+            Mock.verify(this.query);
+        },
+
+        "Should handle the search error": function () {
+            var callbackCalled = false,
+                err = new Error();
 
             Mock.expect(this.contentService, {
                 method: 'createView',
                 args: [this.query, Mock.Value.Function],
                 run: function (query, cb) {
+                    cb(err);
+                }
+            });
+
+            this.plugin.findContent({
+                viewName: this.viewName,
+                offset: this.offset,
+                limit: this.limit,
+            }, function (error) {
+                callbackCalled = true;
+                Assert.areSame(
+                    err, error,
+                    "The error should be provided to the callback"
+                );
+            });
+
+            Assert.isTrue(callbackCalled, "The callback should have been called");
+        },
+
+        "Should parse the results and provide them to the callback": function () {
+            var callbackCalled = false,
+                resultCount = 42,
+                contents = [{}, {}],
+                response = {
+                    document: {
+                        View: {
+                            Result: {
+                                count: resultCount,
+                                searchHits: {
+                                    searchHit: [{
+                                        value: {
+                                            Content: contents[0]
+                                        },
+                                    }, {
+                                        value: {
+                                            Content: contents[1]
+                                        }
+                                    }]
+                                }
+                            }
+                        }
+                    }
+                };
+
+            Mock.expect(this.contentService, {
+                method: 'createView',
+                args: [this.query, Mock.Value.Function],
+                run: function (query, cb) {
+                    cb(false, response);
+                }
+            });
+
+            this.plugin.findContent({
+                viewName: this.viewName,
+                offset: this.offset,
+                limit: this.limit,
+            }, function (error, result, count) {
+                callbackCalled = true;
+
+                Assert.areEqual(
+                    resultCount, count,
+                    "The result count should be provided"
+                );
+                Assert.areSame(
+                    contents[0], result[0].content.hash,
+                    "The content should have been created from the results"
+                );
+                Assert.areSame(
+                    contents[1], result[1].content.hash,
+                    "The content should have been created from the results"
+                );
+            });
+
+            Assert.isTrue(callbackCalled, "The callback provided in the event should have been called");
+        },
+
+        "Should load the content type": function () {
+            var callbackCalled = false,
+                response = {
+                    document: {
+                        View: {
+                            Result: {
+                                count: 1,
+                                searchHits: {
+                                    searchHit: [{
+                                        value: {
+                                            Content: {},
+                                        },
+                                    }]
+                                }
+                            }
+                        }
+                    }
+                };
+
+            Mock.expect(this.contentService, {
+                method: 'createView',
+                args: [this.query, Mock.Value.Function],
+                run: function (query, cb) {
+                    cb(false, response);
+                }
+            });
+
+            this.plugin.findContent({
+                viewName: this.viewName,
+                offset: this.offset,
+                limit: this.limit,
+                loadContentType: true,
+            }, Y.bind(function (error, result) {
+                callbackCalled = true;
+
+                Assert.isInstanceOf(
+                    Y.eZ.ContentType, result[0].contentType,
+                    "The contentType model should be added to the struct"
+                );
+                Assert.areEqual(
+                    this.contentTypeId, result[0].contentType.get('id'),
+                    "The contentType should have the content type id"
+                );
+            }, this));
+
+            Assert.isTrue(callbackCalled, "The callback should have been called");
+        },
+
+        "Should load the location": function () {
+            var callbackCalled = false,
+                resultCount = 42,
+                response = {
+                    document: {
+                        View: {
+                            Result: {
+                                count: resultCount,
+                                searchHits: {
+                                    searchHit: [{
+                                        value: {
+                                            Content: {}
+                                        },
+                                    }, {
+                                        value: {
+                                            Content: {}
+                                        },
+                                    }, {
+                                        value: {
+                                            Content: {}
+                                        },
+                                    }]
+                                }
+                            }
+                        }
+                    }
+                };
+
+            _configureContentSearchQueryMock(this.query, this.locationQuery, response);
+
+            Mock.expect(this.contentService, {
+                method: 'createView',
+                args: [Mock.Value.Object, Mock.Value.Function],
+                run: Y.bind(function (query, callback) {
+                    callback(false, response);
+                }, this)
+            });
+
+            this.plugin.findContent({
+                viewName: this.viewName,
+                offset: this.offset,
+                limit: this.limit,
+                loadLocation: true,
+            }, Y.bind(function (error, result) {
+                callbackCalled = true;
+
+                Y.Array.each(result, Y.bind(function (hit) {
+                    if (hit.location) {
+                        Assert.isInstanceOf(
+                            this.LocationModelConstructor, hit.location,
+                            "The location model should be added to the struct"
+                        );
+                        Assert.areEqual(
+                            this.locationId, hit.location.get('id'),
+                            "The location should have the location id"
+                        );
+                    }
+
+                }, this));
+            }, this));
+            Assert.isTrue(callbackCalled, "The callback should have been called");
+        },
+
+        "Should NOT load the location if content has no location": function () {
+            var callbackCalled = false,
+                resultCount = 42,
+                response = {
+                    document: {
+                        View: {
+                            Result: {
+                                count: resultCount,
+                                searchHits: {
+                                    searchHit: [{
+                                        value: {
+                                            Content: {}
+                                        },
+                                    }, {
+                                        value: {
+                                            Content: {}
+                                        },
+                                    }, {
+                                        value: {
+                                            Content: {}
+                                        },
+                                    }]
+                                }
+                            }
+                        }
+                    }
+                };
+            this.isContentWithoutLocation = true;
+
+            _configureContentSearchQueryMock(this.query, this.locationQuery, response);
+
+            Mock.expect(this.contentService, {
+                method: 'createView',
+                args: [Mock.Value.Object, Mock.Value.Function],
+                run: Y.bind(function (query, callback) {
+                    callback(false, response);
+                }, this)
+            });
+
+            this.plugin.findContent({
+                viewName: this.viewName,
+                offset: this.offset,
+                limit: this.limit,
+                loadLocation: true,
+            }, Y.bind(function (error, result) {
+                callbackCalled = true;
+
+                Y.Array.each(result, Y.bind(function (hit) {
+                    Assert.isUndefined(hit.location, 'There should not be a location');
+                }, this));
+            }, this));
+            Assert.isTrue(callbackCalled, "The callback should have been called");
+        },
+
+        "Should handle location loading error": function () {
+            var callbackCalled = false,
+                resultCount = 42,
+                response = {
+                    document: {
+                        View: {
+                            Result: {
+                                count: resultCount,
+                                searchHits: {
+                                    searchHit: [{
+                                        value: {
+                                            Content: {},
+                                        },
+                                    }]
+                                }
+                            }
+                        }
+                    }
+                },
+                err = new Error();
+
+            _configureContentSearchQueryMock(this.query, this.locationQuery, response);
+
+            Mock.expect(this.contentService, {
+                method: 'createView',
+                args: [Mock.Value.Object, Mock.Value.Function],
+                run: Y.bind(function (query, callback) {
+                    if (query == this.query) {
+                        callback(false, response);
+                    } else {
+                        callback(err, response);
+                    }
+                }, this)
+            });
+
+            Mock.expect(this.query, {
+                method: 'setSortClauses',
+                args: [Mock.Value.Object],
+                run: Y.bind(function (arg) {
                     Assert.areSame(
-                        criteria,
-                        query.body.ViewInput.ContentQuery.Criteria,
-                        "The criteria should be set on the view create struct"
-                    );
-                    Assert.areSame(
-                        sortClauses,
-                        query.body.ViewInput.ContentQuery.SortClauses,
-                        "The sortClauses should be set on the view create struct"
-                    );
-                    Assert.areEqual(
-                        offset,
-                        query.body.ViewInput.ContentQuery.offset,
-                        "The offset should be set on the view create struct"
-                    );
-                    Assert.areEqual(
-                        limit,
-                        query.body.ViewInput.ContentQuery.limit,
-                        "The limit should be set on the view create struct"
+                        this.sortClauses,
+                        arg,
+                        "method argument should be the sortClauses"
                     );
+                }, this)
+            });
+
+            this.plugin.findContent({
+                viewName: this.viewName,
+                offset: this.offset,
+                limit: this.limit,
+                loadLocation: true,
+            }, Y.bind(function (error) {
+                callbackCalled = true;
+
+                Assert.areSame(
+                    err, error,
+                    "The loading location error should be provided"
+                );
+            }, this));
+
+            Assert.isTrue(callbackCalled, "The callback should have been called");
+        },
+
+        "Should handle content type loading error": function () {
+            var callbackCalled = false,
+                response = {
+                    document: {
+                        View: {
+                            Result: {
+                                count: 1,
+                                searchHits: {
+                                    searchHit: [{
+                                        value: {
+                                            Content: {},
+                                        },
+                                    }]
+                                }
+                            }
+                        }
+                    }
+                },
+                err = new Error();
+
+            Mock.expect(this.contentService, {
+                method: 'createView',
+                args: [this.query, Mock.Value.Function],
+                run: function (query, cb) {
+                    cb(false, response);
                 }
             });
 
+            Y.eZ.ContentType = function () {};
+            Y.eZ.ContentType.prototype.load = function (options, callback) {
+                callback(err);
+            };
+
+            this.plugin.findContent({
+                viewName: this.viewName,
+                offset: this.offset,
+                limit: this.limit,
+                loadContentType: true,
+            }, function (error) {
+                callbackCalled = true;
+
+                Assert.areSame(
+                    err, error,
+                    "The loading content type error should be provided"
+                );
+            });
+
+            Assert.isTrue(callbackCalled, "The callback should have been called");
+        },
+    });
+
+    contentSearchEventTests = new Y.Test.Case({
+        name: "eZ Search Plugin content search tests",
+
+        setUp: function () {
+            contentSearchSetUp.apply(this);
+            this.view = new Y.Base({bubbleTargets: this.service});
+        },
+
+        tearDown: function () {
+            this.service.destroy();
+            this.plugin.destroy();
+            delete this.service;
+            delete this.plugin;
+            delete this.capi;
+            delete this.contentService;
+            delete this.query;
+            delete Y.eZ.ContentType;
+        },
+
+        "Should create a content search query with criteria": function () {
+            var criteria = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setCriteria');
             this.view.fire('contentSearch', {
                 viewName: this.viewName,
                 search: {
                     criteria: criteria,
                     sortClauses: sortClauses,
-                    offset: offset,
-                    limit: limit,
+                    offset: this.offset,
+                    limit: this.limit,
+                }
+            });
+            Y.Mock.verify(this.query);
+        },
+
+        "Should create a content search query with query": function () {
+            var query = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setQuery');
+            this.view.fire('contentSearch', {
+                viewName: this.viewName,
+                search: {
+                    query: query,
+                    sortClauses: sortClauses,
+                    offset: this.offset,
+                    limit: this.limit,
                 }
             });
+            Y.Mock.verify(this.query);
+        },
+
+        "Should create a content search query with filter": function () {
+            var filter = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setFilter');
+            this.view.fire('contentSearch', {
+                viewName: this.viewName,
+                search: {
+                    filter: filter,
+                    sortClauses: sortClauses,
+                    offset: this.offset,
+                    limit: this.limit,
+                }
+            });
+            Y.Mock.verify(this.query);
         },
 
         "Should handle the search error": function () {
@@ -111,7 +644,8 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.view.fire('contentSearch', {
                 viewName: this.viewName,
                 search: {
-                    criteria: {},
+                    offset: this.offset,
+                    limit: this.limit,
                 },
                 callback: function (error) {
                     callbackCalled = true;
@@ -161,7 +695,8 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.view.fire('contentSearch', {
                 viewName: this.viewName,
                 search: {
-                    criteria: {},
+                    offset: this.offset,
+                    limit: this.limit,
                 },
                 callback: function (error, result, count) {
                     callbackCalled = true;
@@ -214,7 +749,8 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.view.fire('contentSearch', {
                 viewName: this.viewName,
                 search: {
-                    criteria: {},
+                    offset: this.offset,
+                    limit: this.limit,
                 },
                 loadContentType: true,
                 callback: Y.bind(function (error, result, count) {
@@ -271,7 +807,8 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.view.fire('contentSearch', {
                 viewName: this.viewName,
                 search: {
-                    criteria: {},
+                    offset: this.offset,
+                    limit: this.limit,
                 },
                 loadContentType: true,
                 callback: Y.bind(function (error) {
@@ -296,8 +833,19 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.capi = new Mock();
             this.contentService = new Mock();
             this.viewName = 'REST-View-Name';
-            this.query = {body: {ViewInput: {LocationQuery: {}}}};
-
+            this.query = new Mock();
+            this.sortClauses = {};
+            this.limit = 42;
+            this.offset = 69;
+
+            Mock.expect(this.query, {
+                method: 'setLimitAndOffset',
+                args: [this.limit, this.offset],
+            });
+            Mock.expect(this.query, {
+                method: 'setSortClauses',
+                args: [this.sortClauses],
+            });
             Mock.expect(this.capi, {
                 method: 'getContentService',
                 returns: this.contentService,
@@ -330,47 +878,60 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.plugin.findLocations(search, callback);
         },
 
-        "Should create a LocationQuery with the given name and search properties": function () {
-            var criteria = {}, sortClauses = {},
-                offset = 42, limit = 43;
+        "Should create a LocationQuery with criteria": function () {
+            var criteria = {}, sortClauses = this.sortClauses;
 
-            Mock.expect(this.contentService, {
-                method: 'createView',
-                args: [this.query, Mock.Value.Function],
-                run: function (query, cb) {
-                    Assert.areSame(
-                        criteria,
-                        query.body.ViewInput.LocationQuery.Criteria,
-                        "The criteria should be set on the view create struct"
-                    );
-                    Assert.areSame(
-                        sortClauses,
-                        query.body.ViewInput.LocationQuery.SortClauses,
-                        "The sortClauses should be set on the view create struct"
-                    );
-                    Assert.areEqual(
-                        offset,
-                        query.body.ViewInput.LocationQuery.offset,
-                        "The offset should be set on the view create struct"
-                    );
-                    Assert.areEqual(
-                        limit,
-                        query.body.ViewInput.LocationQuery.limit,
-                        "The limit should be set on the view create struct"
-                    );
-                }
-            });
+            _configureQueryAndContentServiceMock(this, 'setCriteria');
+            this._runSearch({
+                criteria: criteria,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
+            }, function () {});
+            Y.Mock.verify(this.query);
+        },
+
+        "Should create a LocationQuery with filter": function () {
+            var filter = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setFilter');
+            this._runSearch({
+                filter: filter,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
+            }, function () {});
+            Y.Mock.verify(this.query);
+        },
+
+        "Should create a LocationQuery with query": function () {
+            var query = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setQuery');
+            this._runSearch({
+                query: query,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
+            }, function () {});
+            Y.Mock.verify(this.query);
+        },
+
+        "Should create a LocationQuery with a criteria": function () {
+            var criteria = {}, sortClauses = this.sortClauses;
 
+            _configureQueryAndContentServiceMock(this, 'setCriteria');
             this._runSearch({
                 criteria: criteria,
                 sortClauses: sortClauses,
-                offset: offset,
-                limit: limit,
+                offset: this.offset,
+                limit: this.limit,
             }, function () {});
+            Y.Mock.verify(this.query);
         },
 
         "Should set the sort clause based on the given Location": function () {
-            var sortClauses = {},
+            var sortClauses = this.sortClauses,
                 location = new Mock();
 
             Mock.expect(location, {
@@ -381,18 +942,15 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             Mock.expect(this.contentService, {
                 method: 'createView',
                 args: [this.query, Mock.Value.Function],
-                run: function (query, cb) {
-                    Assert.areSame(
-                        sortClauses,
-                        query.body.ViewInput.LocationQuery.SortClauses,
-                        "The sortClauses should be set on the view create struct"
-                    );
-                }
+                run: Y.bind(function () {
+                    Mock.verify(this.query);
+                }, this)
             });
 
             this._runSearch({
-                criteria: {},
                 sortLocation: location,
+                offset: this.offset,
+                limit: this.limit,
             }, function () {});
 
         },
@@ -410,7 +968,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 }
             });
 
-            this._runSearch({criteria: {}}, function (error, result, count) {
+            this._runSearch({offset: this.offset, limit: this.limit}, function (error, result, count) {
                 callbackCalled = true;
 
                 Assert.areSame(
@@ -468,7 +1026,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 }
             });
 
-            this._runSearch({criteria: {}}, Y.bind(function (error, result, count) {
+            this._runSearch({offset: this.offset, limit: this.limit}, Y.bind(function (error, result, count) {
                 callbackCalled = true;
                 Assert.isFalse(
                     error,
@@ -531,7 +1089,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 }
             });
 
-            this._runSearch({criteria: {}}, function (error, result, count) {
+            this._runSearch({offset: this.offset, limit: this.limit}, function (error, result, count) {
                 callbackCalled = true;
                 Assert.isFalse(
                     error,
@@ -567,8 +1125,19 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.capi = new Mock();
             this.contentService = new Mock();
             this.viewName = 'REST-View-Name';
-            this.query = {body: {ViewInput: {LocationQuery: {}}}};
-
+            this.query = new Mock();
+            this.sortClauses = {};
+            this.offset = 42;
+            this.limit = 99;
+
+            Mock.expect(this.query, {
+                method: 'setLimitAndOffset',
+                args: [this.limit, this.offset],
+            });
+            Mock.expect(this.query, {
+                method: 'setSortClauses',
+                args: [this.sortClauses],
+            });
             Mock.expect(this.capi, {
                 method: 'getContentService',
                 returns: this.contentService,
@@ -606,43 +1175,43 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             });
         },
 
-        "Should create a LocationQuery with the given name and search properties": function () {
-            var criteria = {}, sortClauses = {},
-                offset = 42, limit = 43;
+        "Should create a LocationQuery with criteria": function () {
+            var criteria = {}, sortClauses = this.sortClauses;
 
-            Mock.expect(this.contentService, {
-                method: 'createView',
-                args: [this.query, Mock.Value.Function],
-                run: function (query, cb) {
-                    Assert.areSame(
-                        criteria,
-                        query.body.ViewInput.LocationQuery.Criteria,
-                        "The criteria should be set on the view create struct"
-                    );
-                    Assert.areSame(
-                        sortClauses,
-                        query.body.ViewInput.LocationQuery.SortClauses,
-                        "The sortClauses should be set on the view create struct"
-                    );
-                    Assert.areEqual(
-                        offset,
-                        query.body.ViewInput.LocationQuery.offset,
-                        "The offset should be set on the view create struct"
-                    );
-                    Assert.areEqual(
-                        limit,
-                        query.body.ViewInput.LocationQuery.limit,
-                        "The limit should be set on the view create struct"
-                    );
-                }
+            _configureQueryAndContentServiceMock(this, 'setCriteria');
+            this._runSearch({
+                criteria: criteria,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
             });
+            Y.Mock.verify(this.query);
+        },
+
+        "Should create a LocationQuery with query": function () {
+            var query = {}, sortClauses = this.sortClauses;
 
+            _configureQueryAndContentServiceMock(this, 'setQuery');
             this._runSearch({
-                criteria: criteria,
+                query: query,
                 sortClauses: sortClauses,
-                offset: offset,
-                limit: limit,
+                offset: this.offset,
+                limit: this.limit,
             });
+            Y.Mock.verify(this.query);
+        },
+
+        "Should create a LocationQuery with filter": function () {
+            var filter = {}, sortClauses = this.sortClauses;
+
+            _configureQueryAndContentServiceMock(this, 'setFilter');
+            this._runSearch({
+                filter: filter,
+                sortClauses: sortClauses,
+                offset: this.offset,
+                limit: this.limit,
+            });
+            Y.Mock.verify(this.query);
         },
 
         "Should handle the search error": function () {
@@ -654,7 +1223,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 }
             });
 
-            this._runSearch({criteria: {}});
+            this._runSearch({offset: this.offset, limit: this.limit});
 
             Assert.isTrue(
                 this.view.get('loadingError'),
@@ -699,7 +1268,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 }
             });
 
-            this._runSearch({criteria: {}}, resultAttr, resultCountAttr);
+            this._runSearch({offset: this.offset, limit: this.limit}, resultAttr, resultCountAttr);
 
             Assert.isFalse(
                 this.view.get('loadingError'),
@@ -763,7 +1332,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 }
             });
 
-            this._runSearch({criteria: {}}, resultAttr, resultCountAttr);
+            this._runSearch({offset: this.offset, limit: this.limit}, resultAttr, resultCountAttr);
 
             Assert.isFalse(
                 this.view.get('loadingError'),
@@ -796,8 +1365,11 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             this.capi = new Mock();
             this.contentService = new Mock();
             this.viewName = 'REST-View-Name';
-            this.locationQuery = {body: {ViewInput: {LocationQuery: {}}}};
-            this.contentQuery = {body: {ViewInput: {ContentQuery: {}}}};
+            this.filter = {};
+            this.limit = 42;
+            this.offset = 44;
+            this.locationQuery = new Mock();
+            this.contentQuery = new Mock();
             this.contentInfo1 = {
                 id: '/content/id/4112',
                 contentId: '4112',
@@ -904,6 +1476,25 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                     }
                 };
             };
+
+            Mock.expect(this.contentQuery, {
+                method: 'setFilter',
+                args: [Mock.Value.Object],
+                run: Y.bind(function (arg) {
+                    Assert.areSame(
+                        arg.ContentIdCriterion,
+                        this.contentInfo1.contentId + ',' + this.contentInfo2.contentId + ',' + this.contentInfo2.contentId
+                    );
+                }, this),
+            });
+            Mock.expect(this.locationQuery, {
+                method: 'setLimitAndOffset',
+                args: [this.limit, this.offset],
+            });
+            Mock.expect(this.contentQuery, {
+                method: 'setLimitAndOffset',
+                args: [this.contentResponse.document.View.Result.searchHits.searchHit.length, undefined],
+            });
             Mock.expect(this.capi, {
                 method: 'getContentService',
                 returns: this.contentService,
@@ -927,7 +1518,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
             Mock.expect(this.contentService, {
                 method: 'createView',
                 args: [Mock.Value.Object, Mock.Value.Function],
-                run: function (query, cb) {
+                run: Y.bind(function (query, cb) {
                     if (query === that.locationQuery) {
                         cb(false, that.locationResponse);
                     } else if (query === that.contentQuery) {
@@ -943,14 +1534,10 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                                 return '' + hit.value.Location.contentInfo.contentId;
                             }
                         );
-                        Assert.areEqual(
-                            contentIds.join(','),
-                            query.body.ViewInput.ContentQuery.Criteria.ContentIdCriterion,
-                            "The request should be on the Content Ids"
-                        );
+                        Mock.verify(this.contentQuery);
                         cb(false, that.contentResponse);
                     }
-                }
+                }, this)
             });
             this.service = new Y.Base();
             this.service.set('capi', this.capi);
@@ -998,9 +1585,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 resultAttribute: resultAttr,
                 loadContent: true,
                 loadContentType: true,
-                search: {
-                    criteria: {},
-                }
+                search: {offset: this.offset, limit: this.limit}
             });
 
             Assert.isFalse(
@@ -1051,7 +1636,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
 
         "Should handle loading content error": function () {
             var resultAttr = 'whateverAttr',
-            that = this;
+                that = this;
 
             Mock.expect(this.contentService, {
                 method: 'createView',
@@ -1071,9 +1656,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 viewName: this.viewName,
                 resultAttribute: resultAttr,
                 loadContent: true,
-                search: {
-                    criteria: {},
-                }
+                search: {offset: this.offset, limit: this.limit}
             });
 
             Assert.isTrue(
@@ -1094,9 +1677,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 viewName: this.viewName,
                 resultAttribute: resultAttr,
                 loadContentType: true,
-                search: {
-                    criteria: {},
-                }
+                search: {offset: this.offset, limit: this.limit}
             });
 
             Assert.isTrue(
@@ -1104,6 +1685,17 @@ YUI.add('ez-searchplugin-tests', function (Y) {
                 "The loadingError flag should be true"
             );
         },
+
+        "Should call _loadLocationRessources when using deprecated loadRessources method": function () {
+            var loadLocationResourcesCalled = false;
+
+            this.plugin._loadLocationResources = function () {
+                loadLocationResourcesCalled = true;
+            };
+            this.plugin._loadResources('', true, true, [], function(){});
+
+            Assert.isTrue(loadLocationResourcesCalled, '_loadLocationRessources method should have been called');
+        },
     });
 
     registerTest = new Y.Test.Case(Y.eZ.Test.PluginRegisterTest);
@@ -1112,6 +1704,7 @@ YUI.add('ez-searchplugin-tests', function (Y) {
 
     Y.Test.Runner.setName("eZ Search Plugin Tests");
     Y.Test.Runner.add(contentSearchTests);
+    Y.Test.Runner.add(contentSearchEventTests);
     Y.Test.Runner.add(locationSearchTests);
     Y.Test.Runner.add(locationSearchEventTests);
     Y.Test.Runner.add(loadResourcesTests);
diff --git a/Tests/js/views/services/plugins/assets/ez-universaldiscoverycontenttreeplugin-tests.js b/Tests/js/views/services/plugins/assets/ez-universaldiscoverycontenttreeplugin-tests.js
index ddee17d..b6303ab 100644
--- a/Tests/js/views/services/plugins/assets/ez-universaldiscoverycontenttreeplugin-tests.js
+++ b/Tests/js/views/services/plugins/assets/ez-universaldiscoverycontenttreeplugin-tests.js
@@ -201,7 +201,7 @@ YUI.add('ez-universaldiscoverycontenttreeplugin-tests', function (Y) {
                 args: [Mock.Value.Object, Mock.Value.Function],
                 run: Y.bind(function (search, callback) {
                     Assert.areEqual(
-                        1, search.criteria.ParentLocationIdCriterion,
+                        1, search.filter.ParentLocationIdCriterion,
                         "The search should be on the children of the virtual root"
                     );
                     Assert.areEqual(
diff --git a/Tests/js/views/subitem/assets/ez-asynchronoussubitemview-tests.js b/Tests/js/views/subitem/assets/ez-asynchronoussubitemview-tests.js
index c1f01e2..fc27573 100644
--- a/Tests/js/views/subitem/assets/ez-asynchronoussubitemview-tests.js
+++ b/Tests/js/views/subitem/assets/ez-asynchronoussubitemview-tests.js
@@ -35,7 +35,7 @@ YUI.add('ez-asynchronoussubitemview-tests', function (Y) {
                 "The content should be loaded"
             );
             Assert.areEqual(
-                evt.search.criteria.ParentLocationIdCriterion,
+                evt.search.filter.ParentLocationIdCriterion,
                 this.location.get('locationId'),
                 "The subitems of the location should be loaded"
             );
@@ -288,7 +288,7 @@ YUI.add('ez-asynchronoussubitemview-tests', function (Y) {
                     "The content should be loaded"
                 );
                 Assert.areEqual(
-                    evt.search.criteria.ParentLocationIdCriterion,
+                    evt.search.filter.ParentLocationIdCriterion,
                     this.location.get('locationId'),
                     "The subitems of the location should be loaded"
                 );
diff --git a/Tests/js/views/universaldiscovery/assets/ez-universaldiscoverysearchview-tests.js b/Tests/js/views/universaldiscovery/assets/ez-universaldiscoverysearchview-tests.js
index de53dd6..8be1105 100644
--- a/Tests/js/views/universaldiscovery/assets/ez-universaldiscoverysearchview-tests.js
+++ b/Tests/js/views/universaldiscovery/assets/ez-universaldiscoverysearchview-tests.js
@@ -666,7 +666,7 @@ YUI.add('ez-universaldiscoverysearchview-tests', function (Y) {
 
                 Assert.isTrue(e.loadContentType, "The loadContentType param should be set to TRUE");
                 Assert.areSame(
-                    e.search.criteria.FullTextCriterion,
+                    e.search.query.FullTextCriterion,
                     that.searchText,
                     "The search text should be set as FullTextCriterion"
                 );
diff --git a/composer.json b/composer.json
index 6271e0f..79ebd95 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
         }
     },
     "require": {
-        "ezsystems/platform-ui-assets-bundle": "^3.0.0",
+        "ezsystems/platform-ui-assets-bundle": "^3.1.0",
         "ezsystems/repository-forms": "^1.2",
         "ezsystems/ezpublish-kernel": "^6.6",
         "ezsystems/ez-support-tools": "~0.1.0",
