Commit e61ba345d27b4244b946750bd5fc6a06c475d2a4
Exists in
master
and in
26 other branches
Merge branch 'display-blocks' into 'master'
Display blocks based on visibility rules See merge request !19
Showing
9 changed files
with
246 additions
and
44 deletions
Show diff stats
src/app/environment/environment.html
1 | <div class="environment-container"> | 1 | <div class="environment-container"> |
2 | <div class="row"> | 2 | <div class="row"> |
3 | - <noosfero-boxes [boxes]="vm.boxes" [owner]="vm.environment"></noosfero-boxes> | 3 | + <noosfero-boxes ng-if="vm.boxes" [boxes]="vm.boxes" [owner]="vm.environment"></noosfero-boxes> |
4 | </div> | 4 | </div> |
5 | </div> | 5 | </div> |
src/app/layout/boxes/box.html
1 | <div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}"> | 1 | <div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}"> |
2 | - <div ng-repeat="block in box.blocks | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" > | 2 | + <div ng-repeat="block in box.blocks | displayBlocks:ctrl.isHomepage:ctrl.currentUser | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" > |
3 | <div class="panel-heading" ng-show="block.title"> | 3 | <div class="panel-heading" ng-show="block.title"> |
4 | <h3 class="panel-title">{{block.title}}</h3> | 4 | <h3 class="panel-title">{{block.title}}</h3> |
5 | </div> | 5 | </div> |
src/app/layout/boxes/boxes.component.spec.ts
1 | import {Component} from 'ng-forward'; | 1 | import {Component} from 'ng-forward'; |
2 | - | ||
3 | import {BoxesComponent} from './boxes.component'; | 2 | import {BoxesComponent} from './boxes.component'; |
4 | - | ||
5 | -import { | ||
6 | - createComponentFromClass, | ||
7 | - quickCreateComponent, | ||
8 | - provideEmptyObjects, | ||
9 | - createProviderToValue, | ||
10 | - provideFilters | ||
11 | -} from "../../../spec/helpers"; | 3 | +import * as helpers from "../../../spec/helpers"; |
4 | +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | ||
12 | 5 | ||
13 | // this htmlTemplate will be re-used between the container components in this spec file | 6 | // this htmlTemplate will be re-used between the container components in this spec file |
14 | const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl.profile"></noosfero-blog>'; | 7 | const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl.profile"></noosfero-blog>'; |
@@ -16,49 +9,79 @@ const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl | @@ -16,49 +9,79 @@ const htmlTemplate: string = '<noosfero-boxes [boxes]="ctrl.boxes" [owner]="ctrl | ||
16 | 9 | ||
17 | describe("Boxes Component", () => { | 10 | describe("Boxes Component", () => { |
18 | 11 | ||
12 | + let helper: ComponentTestHelper<BoxesComponent>; | ||
19 | beforeEach(() => { | 13 | beforeEach(() => { |
20 | angular.mock.module("templates"); | 14 | angular.mock.module("templates"); |
21 | }); | 15 | }); |
22 | 16 | ||
23 | - @Component({ | ||
24 | - selector: 'test-container-component', | ||
25 | - template: htmlTemplate, | ||
26 | - directives: [BoxesComponent], | ||
27 | - providers: [] | ||
28 | - }) | ||
29 | - class BoxesContainerComponent { | ||
30 | - boxes: noosfero.Box[] = [ | 17 | + let properties = { |
18 | + boxes: [ | ||
31 | { id: 1, position: 1 }, | 19 | { id: 1, position: 1 }, |
32 | { id: 2, position: 2 } | 20 | { id: 2, position: 2 } |
33 | - ]; | ||
34 | - | ||
35 | - owner: noosfero.Profile = <noosfero.Profile> { | 21 | + ], |
22 | + owner: { | ||
36 | id: 1, | 23 | id: 1, |
37 | identifier: 'profile-name', | 24 | identifier: 'profile-name', |
38 | type: 'Person' | 25 | type: 'Person' |
39 | - }; | ||
40 | - } | 26 | + } |
27 | + }; | ||
28 | + beforeEach((done) => { | ||
29 | + let cls = createClass({ | ||
30 | + template: htmlTemplate, | ||
31 | + directives: [BoxesComponent], | ||
32 | + properties: properties, | ||
33 | + providers: [ | ||
34 | + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})), | ||
35 | + helpers.createProviderToValue('AuthService', helpers.mocks.authService), | ||
36 | + helpers.createProviderToValue('$state', state), | ||
37 | + helpers.createProviderToValue('TranslatorService', translatorService) | ||
38 | + ] | ||
39 | + }); | ||
40 | + helper = new ComponentTestHelper<BoxesComponent>(cls, done); | ||
41 | + }); | ||
41 | 42 | ||
42 | - it("renders boxes into a container", (done: Function) => { | ||
43 | - createComponentFromClass(BoxesContainerComponent).then((fixture) => { | ||
44 | - let boxesHtml = fixture.debugElement; | ||
45 | - expect(boxesHtml.query('div.col-md-7').length).toEqual(1); | ||
46 | - expect(boxesHtml.query('div.col-md-2-5').length).toEqual(1); | 43 | + let translatorService = jasmine.createSpyObj("translatorService", ["currentLanguage"]); |
44 | + let state = jasmine.createSpyObj("state", ["current"]); | ||
45 | + state.current = { name: "" }; | ||
47 | 46 | ||
48 | - done(); | ||
49 | - }); | 47 | + it("renders boxes into a container", () => { |
48 | + expect(helper.find('div.col-md-7').length).toEqual(1); | ||
49 | + expect(helper.find('div.col-md-2-5').length).toEqual(1); | ||
50 | + }); | ||
51 | + | ||
52 | + it("check the boxes order", () => { | ||
53 | + expect(helper.component.boxesOrder(properties['boxes'][0])).toEqual(1); | ||
54 | + expect(helper.component.boxesOrder(properties['boxes'][1])).toEqual(0); | ||
50 | }); | 55 | }); |
51 | 56 | ||
52 | - it("check the boxes order", (done: Function) => { | ||
53 | - createComponentFromClass(BoxesContainerComponent).then((fixture) => { | 57 | + it("set isHomepage as false by default", () => { |
58 | + expect(helper.component.isHomepage).toBeFalsy(); | ||
59 | + }); | ||
54 | 60 | ||
55 | - let boxesComponent: BoxesComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
56 | - let boxesContainer: BoxesContainerComponent = fixture.componentInstance; | 61 | + it("set isHomepage as true when in profile home page", () => { |
62 | + state.current = { name: "main.profile.home" }; | ||
63 | + helper.component.ngOnInit(); | ||
64 | + expect(helper.component.isHomepage).toBeTruthy(); | ||
65 | + }); | ||
57 | 66 | ||
58 | - expect(boxesComponent.boxesOrder(boxesContainer.boxes[0])).toEqual(1); | ||
59 | - expect(boxesComponent.boxesOrder(boxesContainer.boxes[1])).toEqual(0); | 67 | + it("set isHomepage as true when in profile info page", () => { |
68 | + state.current = { name: "main.profile.info" }; | ||
69 | + helper.component.ngOnInit(); | ||
70 | + expect(helper.component.isHomepage).toBeTruthy(); | ||
71 | + }); | ||
60 | 72 | ||
61 | - done(); | ||
62 | - }); | 73 | + it("set isHomepage as true when in profile page", () => { |
74 | + state.current = { name: "main.profile.page" }; | ||
75 | + state.params = { page: "/page" }; | ||
76 | + (<noosfero.Profile>helper.component.owner).homepage = '/page'; | ||
77 | + helper.component.ngOnInit(); | ||
78 | + expect(helper.component.isHomepage).toBeTruthy(); | ||
79 | + }); | ||
80 | + | ||
81 | + it("set isHomepage as true when in environment home page", () => { | ||
82 | + state.current = { name: "main.environment.home" }; | ||
83 | + helper.component.owner = <noosfero.Environment>{}; | ||
84 | + helper.component.ngOnInit(); | ||
85 | + expect(helper.component.isHomepage).toBeTruthy(); | ||
63 | }); | 86 | }); |
64 | }); | 87 | }); |
src/app/layout/boxes/boxes.component.ts
1 | import {Input, Inject, Component} from 'ng-forward'; | 1 | import {Input, Inject, Component} from 'ng-forward'; |
2 | +import {SessionService, AuthService, AuthEvents} from "../../login"; | ||
3 | +import {DisplayBlocks} from "./display-blocks.filter"; | ||
2 | 4 | ||
3 | @Component({ | 5 | @Component({ |
4 | selector: "noosfero-boxes", | 6 | selector: "noosfero-boxes", |
5 | - templateUrl: "app/layout/boxes/boxes.html" | 7 | + templateUrl: "app/layout/boxes/boxes.html", |
8 | + directives: [DisplayBlocks] | ||
6 | }) | 9 | }) |
10 | +@Inject("SessionService", 'AuthService', "$state", "$rootScope") | ||
7 | export class BoxesComponent { | 11 | export class BoxesComponent { |
8 | 12 | ||
9 | @Input() boxes: noosfero.Box[]; | 13 | @Input() boxes: noosfero.Box[]; |
10 | - @Input() owner: noosfero.Profile; | 14 | + @Input() owner: noosfero.Profile | noosfero.Environment; |
15 | + | ||
16 | + currentUser: noosfero.User; | ||
17 | + isHomepage = true; | ||
18 | + | ||
19 | + constructor(private session: SessionService, | ||
20 | + private authService: AuthService, | ||
21 | + private $state: ng.ui.IStateService, | ||
22 | + private $rootScope: ng.IRootScopeService) { | ||
23 | + | ||
24 | + this.currentUser = this.session.currentUser(); | ||
25 | + this.authService.subscribe(AuthEvents[AuthEvents.loginSuccess], () => { | ||
26 | + this.currentUser = this.session.currentUser(); | ||
27 | + this.verifyHomepage(); | ||
28 | + }); | ||
29 | + this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => { | ||
30 | + this.currentUser = this.session.currentUser(); | ||
31 | + this.verifyHomepage(); | ||
32 | + }); | ||
33 | + this.$rootScope.$on("$stateChangeSuccess", (event: ng.IAngularEvent, toState: ng.ui.IState) => { | ||
34 | + this.verifyHomepage(); | ||
35 | + }); | ||
36 | + } | ||
37 | + | ||
38 | + ngOnInit() { | ||
39 | + this.verifyHomepage(); | ||
40 | + } | ||
11 | 41 | ||
12 | boxesOrder(box: noosfero.Box) { | 42 | boxesOrder(box: noosfero.Box) { |
13 | if (box.position === 2) return 0; | 43 | if (box.position === 2) return 0; |
14 | return box.position; | 44 | return box.position; |
15 | } | 45 | } |
46 | + | ||
47 | + private verifyHomepage() { | ||
48 | + if (this.owner && ["Profile", "Community", "Person"].indexOf((<any>this.owner)['type']) >= 0) { | ||
49 | + let profile = <noosfero.Profile>this.owner; | ||
50 | + this.isHomepage = this.$state.current.name === "main.profile.home"; | ||
51 | + if (profile.homepage) { | ||
52 | + this.isHomepage = this.isHomepage || | ||
53 | + (this.$state.current.name === "main.profile.page" && profile.homepage === this.$state.params['page']); | ||
54 | + } else { | ||
55 | + this.isHomepage = this.isHomepage || this.$state.current.name === "main.profile.info"; | ||
56 | + } | ||
57 | + } else { | ||
58 | + this.isHomepage = this.$state.current.name === "main.environment.home"; | ||
59 | + } | ||
60 | + } | ||
16 | } | 61 | } |
@@ -0,0 +1,86 @@ | @@ -0,0 +1,86 @@ | ||
1 | +import {quickCreateComponent} from "../../../spec/helpers"; | ||
2 | +import {DisplayBlocks} from './display-blocks.filter'; | ||
3 | + | ||
4 | +describe("Filters", () => { | ||
5 | + describe("Display Blocks Filter", () => { | ||
6 | + | ||
7 | + let translatorService = jasmine.createSpyObj("translatorService", ["currentLanguage"]); | ||
8 | + | ||
9 | + it("not fail when blocks is null", done => { | ||
10 | + let filter = new DisplayBlocks(translatorService); | ||
11 | + expect(filter.transform(null, true, <noosfero.User>{})).toEqual([]); | ||
12 | + done(); | ||
13 | + }); | ||
14 | + | ||
15 | + it("return blocks when no setting is passed", done => { | ||
16 | + let blocks = [{}]; | ||
17 | + let filter = new DisplayBlocks(translatorService); | ||
18 | + expect(filter.transform(<any>blocks, true, <noosfero.User>{})).toEqual(blocks); | ||
19 | + done(); | ||
20 | + }); | ||
21 | + | ||
22 | + it("return blocks when no display is passed", done => { | ||
23 | + let blocks = [{ setting: {} }]; | ||
24 | + let filter = new DisplayBlocks(translatorService); | ||
25 | + expect(filter.transform(<any>blocks, true, <noosfero.User>{})).toEqual(blocks); | ||
26 | + done(); | ||
27 | + }); | ||
28 | + | ||
29 | + it("filter invisible blocks", done => { | ||
30 | + let blocks = [{ settings: { display: "never" } }]; | ||
31 | + let filter = new DisplayBlocks(translatorService); | ||
32 | + expect(filter.transform(<any>blocks, true, <noosfero.User>{})).toEqual([]); | ||
33 | + done(); | ||
34 | + }); | ||
35 | + | ||
36 | + it("filter blocks with except_home_page in homepage", done => { | ||
37 | + let blocks = [{ settings: { display: "except_home_page" } }]; | ||
38 | + let filter = new DisplayBlocks(translatorService); | ||
39 | + expect(filter.transform(<any>blocks, true, <noosfero.User>{})).toEqual([]); | ||
40 | + done(); | ||
41 | + }); | ||
42 | + | ||
43 | + it("filter blocks with home_page_only outside homepage", done => { | ||
44 | + let blocks = [{ settings: { display: "home_page_only" } }]; | ||
45 | + let filter = new DisplayBlocks(translatorService); | ||
46 | + expect(filter.transform(<any>blocks, false, <noosfero.User>{})).toEqual([]); | ||
47 | + done(); | ||
48 | + }); | ||
49 | + | ||
50 | + it("show all blocks when display_user is all for logged user", done => { | ||
51 | + let blocks = [{ settings: { display_user: "all" } }]; | ||
52 | + let filter = new DisplayBlocks(translatorService); | ||
53 | + expect(filter.transform(<any>blocks, true, <noosfero.User>{})).toEqual(blocks); | ||
54 | + done(); | ||
55 | + }); | ||
56 | + | ||
57 | + it("show all blocks when display_user is all for not logged user", done => { | ||
58 | + let blocks = [{ settings: { display_user: "all" } }]; | ||
59 | + let filter = new DisplayBlocks(translatorService); | ||
60 | + expect(filter.transform(<any>blocks, true, null)).toEqual(blocks); | ||
61 | + done(); | ||
62 | + }); | ||
63 | + | ||
64 | + it("filter blocks when display_user is logged for not logged user", done => { | ||
65 | + let blocks = [{ settings: { display_user: "logged" } }]; | ||
66 | + let filter = new DisplayBlocks(translatorService); | ||
67 | + expect(filter.transform(<any>blocks, true, null)).toEqual([]); | ||
68 | + done(); | ||
69 | + }); | ||
70 | + | ||
71 | + it("filter blocks when display_user is not_logged for logged user", done => { | ||
72 | + let blocks = [{ settings: { display_user: "not_logged" } }]; | ||
73 | + let filter = new DisplayBlocks(translatorService); | ||
74 | + expect(filter.transform(<any>blocks, true, <noosfero.User>{})).toEqual([]); | ||
75 | + done(); | ||
76 | + }); | ||
77 | + | ||
78 | + it("filter blocks with different language", done => { | ||
79 | + let blocks = [{ settings: { language: "en" } }]; | ||
80 | + translatorService.currentLanguage = jasmine.createSpy("currentLanguage").and.returnValue("pt"); | ||
81 | + let filter = new DisplayBlocks(translatorService); | ||
82 | + expect(filter.transform(<any>blocks, true, <noosfero.User>{})).toEqual([]); | ||
83 | + done(); | ||
84 | + }); | ||
85 | + }); | ||
86 | +}); |
@@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
1 | +import {Pipe, Inject} from "ng-forward"; | ||
2 | +import {TranslatorService} from "../../shared/services/translator.service"; | ||
3 | + | ||
4 | +@Pipe("displayBlocks") | ||
5 | +@Inject(TranslatorService) | ||
6 | +export class DisplayBlocks { | ||
7 | + | ||
8 | + constructor(private translatorService: TranslatorService) { } | ||
9 | + | ||
10 | + transform(blocks: noosfero.Block[], isHomepage: boolean, currentUser: noosfero.User) { | ||
11 | + let selected: noosfero.Block[] = []; | ||
12 | + blocks = blocks || []; | ||
13 | + for (let block of blocks) { | ||
14 | + if (this.visible(block, isHomepage) && this.displayToUser(block, currentUser) && | ||
15 | + this.displayOnLanguage(block, this.translatorService.currentLanguage())) { | ||
16 | + selected.push(block); | ||
17 | + } | ||
18 | + } | ||
19 | + return selected; | ||
20 | + } | ||
21 | + | ||
22 | + private visible(block: noosfero.Block, isHomepage: boolean) { | ||
23 | + let display = block.settings ? (<any>block.settings)['display'] : null; | ||
24 | + return !display || ((isHomepage ? display !== "except_home_page" : display !== "home_page_only") && display !== "never"); | ||
25 | + } | ||
26 | + | ||
27 | + private displayToUser(block: noosfero.Block, currentUser: noosfero.User) { | ||
28 | + let displayUser = block.settings ? (<any>block.settings)['display_user'] : null; | ||
29 | + return !displayUser || displayUser === "all" || | ||
30 | + (currentUser ? displayUser === "logged" : displayUser === "not_logged"); | ||
31 | + } | ||
32 | + | ||
33 | + private displayOnLanguage(block: noosfero.Block, language: string) { | ||
34 | + let displayLanguage = block.settings ? (<any>block.settings)['language'] : null; | ||
35 | + return !displayLanguage || displayLanguage === "all" || | ||
36 | + language === displayLanguage; | ||
37 | + } | ||
38 | +} |
src/app/profile/profile-home.component.ts
@@ -18,8 +18,10 @@ export class ProfileHomeComponent { | @@ -18,8 +18,10 @@ export class ProfileHomeComponent { | ||
18 | return profileService.getHomePage(<number>this.profile.id, { fields: 'path' }); | 18 | return profileService.getHomePage(<number>this.profile.id, { fields: 'path' }); |
19 | }).then((response: restangular.IResponse) => { | 19 | }).then((response: restangular.IResponse) => { |
20 | if (response.data.article) { | 20 | if (response.data.article) { |
21 | + this.profile.homepage = response.data.article.path; | ||
21 | $state.transitionTo('main.profile.page', { page: response.data.article.path, profile: this.profile.identifier }, { location: false }); | 22 | $state.transitionTo('main.profile.page', { page: response.data.article.path, profile: this.profile.identifier }, { location: false }); |
22 | } else { | 23 | } else { |
24 | + this.profile.homepage = null; | ||
23 | $state.transitionTo('main.profile.info', { profile: this.profile.identifier }, { location: false }); | 25 | $state.transitionTo('main.profile.info', { profile: this.profile.identifier }, { location: false }); |
24 | } | 26 | } |
25 | }); | 27 | }); |
src/app/profile/profile.html
1 | <div class="profile-container"> | 1 | <div class="profile-container"> |
2 | <div class="profile-header" ng-bind-html="vm.profile.custom_header"></div> | 2 | <div class="profile-header" ng-bind-html="vm.profile.custom_header"></div> |
3 | <div class="row"> | 3 | <div class="row"> |
4 | - <noosfero-boxes [boxes]="vm.boxes" [owner]="vm.profile"></noosfero-boxes> | 4 | + <noosfero-boxes ng-if="vm.boxes" [boxes]="vm.boxes" [owner]="vm.profile"></noosfero-boxes> |
5 | </div> | 5 | </div> |
6 | <div class="profile-footer" ng-bind-html="vm.profile.custom_footer"></div> | 6 | <div class="profile-footer" ng-bind-html="vm.profile.custom_footer"></div> |
7 | </div> | 7 | </div> |
src/lib/ng-noosfero-api/interfaces/profile.ts
@@ -22,13 +22,13 @@ namespace noosfero { | @@ -22,13 +22,13 @@ namespace noosfero { | ||
22 | * @returns {string} The unque identifier for the Profile | 22 | * @returns {string} The unque identifier for the Profile |
23 | */ | 23 | */ |
24 | identifier: string; | 24 | identifier: string; |
25 | - | 25 | + |
26 | /** | 26 | /** |
27 | * @ngdoc property | 27 | * @ngdoc property |
28 | * @name created_at | 28 | * @name created_at |
29 | * @propertyOf noofero.Profile | 29 | * @propertyOf noofero.Profile |
30 | * @returns {string} The timestamp this object was created | 30 | * @returns {string} The timestamp this object was created |
31 | - */ | 31 | + */ |
32 | created_at: string; | 32 | created_at: string; |
33 | 33 | ||
34 | /** | 34 | /** |
@@ -54,5 +54,13 @@ namespace noosfero { | @@ -54,5 +54,13 @@ namespace noosfero { | ||
54 | * @returns {string} A key => value custom fields data of Profile (e.g.: "{'Address':'Street A, Number 102...'}") | 54 | * @returns {string} A key => value custom fields data of Profile (e.g.: "{'Address':'Street A, Number 102...'}") |
55 | */ | 55 | */ |
56 | additional_data?: any; | 56 | additional_data?: any; |
57 | + | ||
58 | + /** | ||
59 | + * @ngdoc property | ||
60 | + * @name homepage | ||
61 | + * @propertyOf noofero.Profile | ||
62 | + * @returns {string} The Profile homepage | ||
63 | + */ | ||
64 | + homepage: string; | ||
57 | } | 65 | } |
58 | } | 66 | } |