Skip to content
This repository has been archived by the owner on May 3, 2022. It is now read-only.

Commit

Permalink
Merge pull request #430 from mhyndle/ezp-24871_breadcrumb_using_ances…
Browse files Browse the repository at this point in the history
…tor_criterion

EZP-24871: use ancestor criterion for getting breadcrumb
  • Loading branch information
mhyndle committed Nov 17, 2015
2 parents 4cfecd4 + a711001 commit 9f951d2
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 227 deletions.
67 changes: 67 additions & 0 deletions Resources/public/js/models/ez-locationmodel.js
Expand Up @@ -178,6 +178,73 @@ YUI.add('ez-locationmodel', function (Y) {
location.reset();
callback(error, response);
});
},

/**
* Loads path of the current location. The result is the array containing
* eZ.Location objects present on the path sorted by depth.
* The array doesn't contain current location.
*
* @method loadPath
* @param {Object} options
* @param {Object} options.api the JS REST client instance
* @param {Function} callback
* @param {false|Error} callback.error
* @param {Array} callback.locations the array of Locations
*/
loadPath: function (options, callback) {
var locations = [],
that = this;

this._loadAncestors(options, function (error, response) {
var hits;

if (error) {
callback(error);
return;
}

hits = response.document.View.Result.searchHits.searchHit;

Y.Array.each(hits, function (hit) {
if (hit.value.Location._href !== this.get('id')) {
var location = new Y.eZ.Location();

location.setAttrs(location.parse({document: hit.value}));
locations.push(location);
}
}, that);

locations.sort(function (a, b) {
return (a.get('depth') - b.get('depth'));
});

callback(error, locations);
});
},

/**
* Loads ancestors of the location basing on the `pathString`. The result is the REST API view
* containing locations from the path.
*
* @method _loadAncestors
* @protected
* @param {Object} options
* @param {Object} options.api the JS REST client instance
* @param {Function} callback
*/
_loadAncestors: function (options, callback) {
var contentService = options.api.getContentService(),
query = contentService.newViewCreateStruct('ancestors-' + this.get('locationId'), 'LocationQuery');

query.body.ViewInput.LocationQuery.Criteria = {
AncestorCriterion: this.get('pathString')
};

contentService.createView(
query,
callback
);
}
}, {
REST_STRUCT_ROOT: "Location",
Expand Down
97 changes: 6 additions & 91 deletions Resources/public/js/views/services/ez-locationviewviewservice.js
Expand Up @@ -273,8 +273,7 @@ YUI.add('ez-locationviewviewservice', function (Y) {
request = this.get('request'),
service = this,
location = this.get('location'), content = this.get('content'),
type = this.get('contentType'),
discoveryService = this.get('capi').getDiscoveryService();
type = this.get('contentType');

location.set('id', request.params.id);
location.load(loadOptions, function (error) {
Expand Down Expand Up @@ -307,21 +306,14 @@ YUI.add('ez-locationviewviewservice', function (Y) {
});

endLoadPath = tasks.add();
discoveryService.getInfoObject('rootLocation', function (error, response) {
var rootLocationId;

location.loadPath(loadOptions, function(error, path) {
if ( error ) {
service._error("Failed to contact the REST API");
service._error("Failed to load locations's path with REST API");
return;
}

rootLocationId = response._href;
if ( rootLocationId === location.get('id') || location.get('depth') == 1 ) {
service.set('path', []);
endLoadPath();
return;
}
service._loadPath(rootLocationId, endLoadPath);
service.set('path', path);
endLoadPath();
});

tasks.done(function () {
Expand All @@ -335,66 +327,6 @@ YUI.add('ez-locationviewviewservice', function (Y) {
});
},

/**
* Recursively loads the path to the current location
*
* @protected
* @method _loadPath
* @param {String} rootLocationId the root location id
* @param {Function} end the callback to call when the just is done
*/
_loadPath: function (rootLocationId, end) {
var service = this,
loadParentCallback,
path = [];

loadParentCallback = function (error, parentLocation) {
if ( error ) {
service._error("Fail to load the path");
return;
}
path.push(parentLocation);
if ( rootLocationId === parentLocation.get('id') || parentLocation.get('depth') == 1 ) {
service.set('path', path);
end();
} else {
service._loadParent(parentLocation, loadParentCallback);
}
};

this._loadParent(this.get('location'), loadParentCallback);
},

/**
* Loads the parent location and its content
*
* @protected
* @method _loadParent
* @param {Y.eZ.Location} location
* @param {Function} callback the function to call when the location is loaded
* @param {Boolean} callback.error the error, truthy if an error occurred
* @param {Object} callback.location an object containing the
* Y.eZ.Location
*/
_loadParent: function (location, callback) {
var loadOptions = {
api: this.get('capi')
},
parentLocation;

parentLocation = this._newLocation({
'id': location.get('resources').ParentLocation
});
parentLocation.load(loadOptions, function (error) {
if ( error ) {
callback(error);
return;
}

callback(error, parentLocation);
});
},

/**
* Set languageCode attribute basing on parameter from request
*
Expand All @@ -415,19 +347,6 @@ YUI.add('ez-locationviewviewservice', function (Y) {
languageCode: this.get('request').params.languageCode,
};
},

/**
* Creates a new instance of Y.eZ.Location with the given params
*
* @method _newLocation
* @protected
* @param {Object} params the parameters passed to the Y.eZ.Location
* constructor
*/
_newLocation: function (params) {
return new Y.eZ.Location(params);
},

}, {
ATTRS: {
/**
Expand Down Expand Up @@ -471,11 +390,7 @@ YUI.add('ez-locationviewviewservice', function (Y) {
* @type Array
*/
path: {
getter: function (value) {
return value.sort(function (a, b) {
return (a.get('depth') - b.get('depth'));
});
}
value: []
},

/**
Expand Down
134 changes: 133 additions & 1 deletion Tests/js/models/assets/ez-locationmodel-tests.js
Expand Up @@ -3,7 +3,7 @@
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-locationmodel-tests', function (Y) {
var modelTest, trashTest, moveTest, hideTest, removeTest,
var modelTest, trashTest, moveTest, hideTest, removeTest, loadPathTest,
Assert = Y.Assert, Mock = Y.Mock;

modelTest = new Y.Test.Case(Y.merge(Y.eZ.Test.ModelTests, {
Expand Down Expand Up @@ -504,10 +504,142 @@ YUI.add('ez-locationmodel-tests', function (Y) {
}
});

loadPathTest = new Y.Test.Case({
name: "eZ location model load path tests",

setUp: function () {
this.capiMock = new Y.Mock();
this.contentService = new Y.Mock();
this.locationId = '/1/2/3/4';
this.pathString = '/path/string/1/2/3';
this.model = new Y.eZ.Location({id: this.locationId, pathString: this.pathString});
this.loadAncestorsResponse = {
document: {
View: {
Result: {
searchHits: {
searchHit: [
{value: { Location: {_href: '/parent/location', depth: 2}}},
{value: { Location: {_href: '/grand/parent/location', depth: 1}}},
{value: { Location: {_href: this.locationId, depth: 3}}},
{value: { Location: {_href: '/home/location', depth: 0}}},
]
}
}
}
}
};
this.viewCreateStruct = {
body: {
ViewInput: {
LocationQuery : {

}
}
}
};

Y.Mock.expect(this.capiMock, {
method: 'getContentService',
returns: this.contentService,
});

Y.Mock.expect(this.contentService, {
method: 'newViewCreateStruct',
args: [Y.Mock.Value.String, 'LocationQuery'],
returns: this.viewCreateStruct
});
},

tearDown: function () {
this.model.destroy();
delete this.model;
},

"Should load path for the location": function () {
var that = this, i;

Y.Mock.expect(this.contentService, {
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"
);
callback(false, that.loadAncestorsResponse);
}
});

this.model.loadPath({
api: this.capiMock
}, function (error, locations) {
Assert.isFalse(
error,
"No error should be detected"
);
Assert.isArray(
locations,
"The result in callback should be an array"
);
Assert.areEqual(
that.loadAncestorsResponse.document.View.Result.searchHits.searchHit.length-1,
locations.length,
"The result array should be reduced by current location"
);
Y.Array.each(locations, function (location) {
Assert.isObject(location, "The item included in result array should be an object");
Assert.areEqual(
location.name,
'locationModel',
"The item included in result array should be the locationModel instance"
);
Assert.areNotEqual(
location.get('id'),
that.model.get('id'),
"Current location should not be included in the result"
);
});

for (i = 0; i != locations.length; ++i) {
Y.Assert.areSame(
i, locations[i].get('depth'),
"The path should be sorted by depth"
);
}
});
},

"Should handle the error if create REST view fails": function () {
Y.Mock.expect(this.contentService, {
method: 'createView',
args: [Y.Mock.Value.Object, Y.Mock.Value.Function],
run: function (options, callback) {
callback(true);
}
});

this.model.loadPath({
api: this.capiMock
}, function (error) {
Assert.isTrue(
error,
"Error should be detected"
);
});
}
});

Y.Test.Runner.setName("eZ Location Model tests");
Y.Test.Runner.add(modelTest);
Y.Test.Runner.add(trashTest);
Y.Test.Runner.add(moveTest);
Y.Test.Runner.add(hideTest);
Y.Test.Runner.add(removeTest);
Y.Test.Runner.add(loadPathTest);
}, '', {requires: ['test', 'model-tests', 'ez-locationmodel', 'ez-restmodel']});

0 comments on commit 9f951d2

Please sign in to comment.