Commit 0c28a9831a0771df9ac32e5755a325ae861c0249

Authored by Victor Costa
2 parents 9d2ce382 0e0b4a70

Merge branch 'edit-blocks'

src/app/layout/blocks/block-content.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,91 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Input, provide, Component} from 'ng-forward';
  3 +
  4 +import {BlockContentComponent} from './block-content.component';
  5 +
  6 +const tcb = new TestComponentBuilder();
  7 +
  8 +const htmlTemplate: string = '<noosfero-block-content [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-block-content>';
  9 +
  10 +describe("Components", () => {
  11 + describe("Block Component", () => {
  12 +
  13 + // the karma preprocessor html2js transform the templates html into js files which put
  14 + // the templates to the templateCache into the module templates
  15 + // we need to load the module templates here as the template for the
  16 + // component Block will be load on our tests
  17 + beforeEach(angular.mock.module("templates"));
  18 +
  19 + it("receives the block and the owner as inputs", done => {
  20 +
  21 + // Creating a container component (BlockContainerComponent) to include
  22 + // the component under test (Block)
  23 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockContentComponent] })
  24 + class BlockContainerComponent {
  25 + block = { type: 'Block' };
  26 + owner = { name: 'profile-name' };
  27 + constructor() {
  28 + }
  29 + }
  30 +
  31 + // uses the TestComponentBuilder instance to initialize the component
  32 + tcb
  33 + .createAsync(BlockContainerComponent).then(fixture => {
  34 + // and here we can inspect and run the test assertions
  35 + let myComponent: BlockContentComponent = fixture.componentInstance;
  36 +
  37 + // assure the block object inside the Block matches
  38 + // the provided through the parent component
  39 + expect(myComponent.block.type).toEqual("Block");
  40 + expect(myComponent.owner.name).toEqual("profile-name");
  41 + done();
  42 + });
  43 + });
  44 +
  45 +
  46 + it("renders a component which matches to the block type", done => {
  47 + // CustomBlock component created to check if it will be used
  48 + // when a block with type 'CustomBlock' is provided to the noosfero-block (Block)
  49 + // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider
  50 + @Component({ selector: 'noosfero-custom-block', template: "<h1>My Custom Block</h1>" })
  51 + class CustomBlock {
  52 + @Input() block: any;
  53 + @Input() owner: any;
  54 + }
  55 +
  56 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockContentComponent, CustomBlock] })
  57 + class CustomBlockType {
  58 + block = { type: 'CustomBlock' };
  59 + owner = { name: 'profile-name' };
  60 + constructor() {
  61 + }
  62 + }
  63 + tcb
  64 + .createAsync(CustomBlockType).then(fixture => {
  65 + let myComponent: CustomBlockType = fixture.componentInstance;
  66 + expect(myComponent.block.type).toEqual("CustomBlock");
  67 + expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("My Custom Block");
  68 + done();
  69 + });
  70 + });
  71 +
  72 +
  73 + it("renders the default block when hasn't defined a block type", done => {
  74 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockContentComponent] })
  75 + class CustomBlockType {
  76 + block: any = { type: null };
  77 + owner: any = { name: 'profile-name' };
  78 + constructor() {
  79 + }
  80 + }
  81 + tcb
  82 + .createAsync(CustomBlockType).then(fixture => {
  83 + let myComponent: CustomBlockType = fixture.componentInstance;
  84 + expect(myComponent.block.type).toBeNull();
  85 + expect(!!fixture.debugElement.nativeElement.querySelector("noosfero-default-block")).toBeTruthy();
  86 + done();
  87 + });
  88 + });
  89 +
  90 + });
  91 +});
... ...
src/app/layout/blocks/block-content.component.ts 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +import { Input, Inject, Component } from 'ng-forward';
  2 +
  3 +@Component({
  4 + selector: 'noosfero-block-content',
  5 + template: '<div></div>'
  6 +})
  7 +@Inject("$element", "$scope", "$injector", "$compile")
  8 +export class BlockContentComponent {
  9 +
  10 + @Input() block: any;
  11 + @Input() owner: any;
  12 +
  13 + ngOnInit() {
  14 + let blockName = (this.block && this.block.type) ? this.block.type.replace(/::/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block";
  15 + this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope));
  16 + }
  17 +
  18 + constructor(private $element: any, private $scope: ng.IScope, private $injector: ng.auto.IInjectorService, private $compile: ng.ICompileService) {
  19 + }
  20 +}
... ...
src/app/layout/blocks/block-edition/block-edition.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +import {Component} from 'ng-forward';
  2 +import {BlockEditionComponent} from './block-edition.component';
  3 +import * as helpers from "../../../../spec/helpers";
  4 +import {ComponentTestHelper, createClass} from '../../../../spec/component-test-helper';
  5 +
  6 +const htmlTemplate: string = '<noosfero-block-edition></noosfero-block-edition>';
  7 +
  8 +describe("Boxes Component", () => {
  9 +
  10 + let helper: ComponentTestHelper<BlockEditionComponent>;
  11 + let translatorService = {
  12 + availableLanguages: { 'en': 'English', 'pt': 'Portuguese' }
  13 + };
  14 +
  15 + beforeEach(() => {
  16 + angular.mock.module("templates");
  17 + });
  18 +
  19 + beforeEach((done) => {
  20 + let cls = createClass({
  21 + template: htmlTemplate,
  22 + directives: [BlockEditionComponent],
  23 + providers: [
  24 + helpers.createProviderToValue('TranslatorService', translatorService)
  25 + ]
  26 + });
  27 + helper = new ComponentTestHelper<BlockEditionComponent>(cls, done);
  28 + });
  29 +
  30 + it("get available languages from translator service", () => {
  31 + expect(helper.component.languageOptions).toEqual(['all', 'en', 'pt']);
  32 + });
  33 +
  34 +});
... ...
src/app/layout/blocks/block-edition/block-edition.component.ts 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +import { Input, Inject, Component } from 'ng-forward';
  2 +import { TranslatorService } from "../../../shared/services/translator.service";
  3 +
  4 +@Component({
  5 + selector: 'noosfero-block-edition',
  6 + templateUrl: 'app/layout/blocks/block-edition/block-edition.html'
  7 +})
  8 +@Inject(TranslatorService)
  9 +export class BlockEditionComponent {
  10 +
  11 + static $inject = ["TranslatorService"]; // @Inject doesn't works with uibModal.open
  12 +
  13 + displayOptions: any;
  14 + displayUserOptions: any;
  15 + languageOptions: any;
  16 +
  17 + constructor(private translatorService: TranslatorService) {
  18 + this.displayOptions = ["always", "home_page_only", "except_home_page", "never"];
  19 + this.displayUserOptions = ["all", "logged", "not_logged"];
  20 + this.languageOptions = ["all"].concat(Object.keys(translatorService.availableLanguages));
  21 + }
  22 +}
... ...
src/app/layout/blocks/block-edition/block-edition.html 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +<div class="edit-block">
  2 + <h3>{{"block.edition.title" | translate}}</h3>
  3 +
  4 + <form class="options">
  5 + <div class="title block-option">
  6 + <label for="titleInput">{{"block.edition.title.label" | translate}}</label>
  7 + <input type="text" id="titleInput" ng-model="ctrl.block.title" class="block-input">
  8 + </div>
  9 + <div class="display block-option">
  10 + <label for="displayInput">{{"block.edition.display.label" | translate}}</label>
  11 + <select id="displayInput" ng-model="ctrl.block.settings.display" class="block-input">
  12 + <option ng-repeat="option in modal.displayOptions" value="{{option}}">{{"block.edition.display." + option | translate}}</option>
  13 + </select>
  14 + </div>
  15 + <div class="displayUser block-option">
  16 + <label for="displayUserInput">{{"block.edition.display_user.label" | translate}}</label>
  17 + <select id="displayUserInput" ng-model="ctrl.block.settings.display_user" class="block-input">
  18 + <option ng-repeat="option in modal.displayUserOptions" value="{{option}}">{{"block.edition.display_user." + option | translate}}</option>
  19 + </select>
  20 + </div>
  21 + <div class="language block-option">
  22 + <label for="languageInput">{{"block.edition.language.label" | translate}}</label>
  23 + <select id="languageInput" ng-model="ctrl.block.settings.language" class="block-input">
  24 + <option ng-repeat="option in modal.languageOptions" value="{{option}}">{{"language." + option | translate}}</option>
  25 + </select>
  26 + </div>
  27 + </form>
  28 +
  29 + <div class="actions">
  30 + <button type="submit" class="btn btn-default" ng-click="ctrl.save()">Save</button>
  31 + <button type="submit" class="btn btn-warning" ng-click="ctrl.preview()">Preview</button>
  32 + <button type="submit" class="btn btn-danger" ng-click="ctrl.cancel()">Cancel</button>
  33 + </div>
  34 +</div>
... ...
src/app/layout/blocks/block-edition/block-edition.scss 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +.edit-block {
  2 + margin: 20px;
  3 +
  4 + .options {
  5 + margin-bottom: 20px;
  6 +
  7 + .block-option {
  8 + @extend .form-group;
  9 + .block-input {
  10 + @extend .form-control;
  11 + }
  12 + }
  13 + }
  14 +}
... ...
src/app/layout/blocks/block.component.spec.ts
1   -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Input, provide, Component} from 'ng-forward';
3   -
  1 +import {Component} from 'ng-forward';
4 2 import {BlockComponent} from './block.component';
  3 +import * as helpers from "../../../spec/helpers";
  4 +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper';
5 5  
6   -const tcb = new TestComponentBuilder();
7   -
8   -const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-block>';
9   -
10   -describe("Components", () => {
11   - describe("Block Component", () => {
12   -
13   - // the karma preprocessor html2js transform the templates html into js files which put
14   - // the templates to the templateCache into the module templates
15   - // we need to load the module templates here as the template for the
16   - // component Block will be load on our tests
17   - beforeEach(angular.mock.module("templates"));
18   -
19   - it("receives the block and the owner as inputs", done => {
20   -
21   - // Creating a container component (BlockContainerComponent) to include
22   - // the component under test (Block)
23   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent] })
24   - class BlockContainerComponent {
25   - block = { type: 'Block' };
26   - owner = { name: 'profile-name' };
27   - constructor() {
28   - }
29   - }
30   -
31   - // uses the TestComponentBuilder instance to initialize the component
32   - tcb
33   - .createAsync(BlockContainerComponent).then(fixture => {
34   - // and here we can inspect and run the test assertions
35   - let myComponent: BlockComponent = fixture.componentInstance;
36   -
37   - // assure the block object inside the Block matches
38   - // the provided through the parent component
39   - expect(myComponent.block.type).toEqual("Block");
40   - expect(myComponent.owner.name).toEqual("profile-name");
41   - done();
42   - });
43   - });
  6 +const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.profile"></noosfero-block>';
44 7  
  8 +describe("Boxes Component", () => {
45 9  
46   - it("renders a component which matches to the block type", done => {
47   - // CustomBlock component created to check if it will be used
48   - // when a block with type 'CustomBlock' is provided to the noosfero-block (Block)
49   - // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider
50   - @Component({ selector: 'noosfero-custom-block', template: "<h1>My Custom Block</h1>" })
51   - class CustomBlock {
52   - @Input() block: any;
53   - @Input() owner: any;
54   - }
55   -
56   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent, CustomBlock] })
57   - class CustomBlockType {
58   - block = { type: 'CustomBlock' };
59   - owner = { name: 'profile-name' };
60   - constructor() {
61   - }
62   - }
63   - tcb
64   - .createAsync(CustomBlockType).then(fixture => {
65   - let myComponent: CustomBlockType = fixture.componentInstance;
66   - expect(myComponent.block.type).toEqual("CustomBlock");
67   - expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("My Custom Block");
68   - done();
69   - });
  10 + let helper: ComponentTestHelper<BlockComponent>;
  11 + beforeEach(() => {
  12 + angular.mock.module("templates");
  13 + });
  14 +
  15 + let properties = {
  16 + block: { id: 1 },
  17 + owner: {
  18 + id: 1,
  19 + identifier: 'profile-name',
  20 + type: 'Person'
  21 + }
  22 + };
  23 + beforeEach((done) => {
  24 + let cls = createClass({
  25 + template: htmlTemplate,
  26 + directives: [BlockComponent],
  27 + properties: properties,
  28 + providers: [
  29 + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})),
  30 + helpers.createProviderToValue('AuthService', helpers.mocks.authService),
  31 + helpers.createProviderToValue('$state', state),
  32 + helpers.createProviderToValue('TranslatorService', translatorService),
  33 + helpers.createProviderToValue('$uibModal', helpers.mocks.$modal),
  34 + helpers.createProviderToValue('BlockService', blockService),
  35 + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService)
  36 + ]
70 37 });
  38 + helper = new ComponentTestHelper<BlockComponent>(cls, done);
  39 + });
71 40  
  41 + let translatorService = jasmine.createSpyObj("translatorService", ["currentLanguage"]);
  42 + let blockService = jasmine.createSpyObj("blockService", ["update"]);
  43 + let state = jasmine.createSpyObj("state", ["current"]);
  44 + state.current = { name: "" };
72 45  
73   - it("renders the default block when hasn't defined a block type", done => {
74   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [BlockComponent] })
75   - class CustomBlockType {
76   - block: any = { type: null };
77   - owner: any = { name: 'profile-name' };
78   - constructor() {
79   - }
80   - }
81   - tcb
82   - .createAsync(CustomBlockType).then(fixture => {
83   - let myComponent: CustomBlockType = fixture.componentInstance;
84   - expect(myComponent.block.type).toBeNull();
85   - expect(!!fixture.debugElement.nativeElement.querySelector("noosfero-default-block")).toBeTruthy();
86   - done();
87   - });
88   - });
  46 + it("set isHomepage as false by default", () => {
  47 + expect(helper.component.isHomepage).toBeFalsy();
  48 + });
  49 +
  50 + it("set isHomepage as true when in profile home page", () => {
  51 + state.current = { name: "main.profile.home" };
  52 + helper.component.ngOnInit();
  53 + expect(helper.component.isHomepage).toBeTruthy();
  54 + });
  55 +
  56 + it("set isHomepage as true when in profile info page", () => {
  57 + state.current = { name: "main.profile.info" };
  58 + helper.component.ngOnInit();
  59 + expect(helper.component.isHomepage).toBeTruthy();
  60 + });
  61 +
  62 + it("set isHomepage as true when in profile page", () => {
  63 + state.current = { name: "main.profile.page" };
  64 + state.params = { page: "/page" };
  65 + (<noosfero.Profile>helper.component.owner).homepage = '/page';
  66 + helper.component.ngOnInit();
  67 + expect(helper.component.isHomepage).toBeTruthy();
  68 + });
  69 +
  70 + it("set isHomepage as true when in environment home page", () => {
  71 + state.current = { name: "main.environment.home" };
  72 + helper.component.owner = <noosfero.Environment>{};
  73 + helper.component.ngOnInit();
  74 + expect(helper.component.isHomepage).toBeTruthy();
  75 + });
  76 +
  77 + it("return true in canDisplay when no display option is setted", () => {
  78 + helper.component.block = <any>{};
  79 + expect(helper.component.canDisplay()).toEqual(true);
  80 + });
  81 +
  82 + it("return false in canDisplay for an invisible block", () => {
  83 + helper.component.block = <any>{ settings: { display: "never" } };
  84 + expect(helper.component.canDisplay()).toEqual(false);
  85 + });
  86 +
  87 + it("return false in canDisplay with except_home_page in homepage", () => {
  88 + helper.component.block = <any>{ settings: { display_user: "except_home_page" } };
  89 + expect(helper.component.canDisplay()).toEqual(false);
  90 + });
  91 +
  92 + it("return false in canDisplay with home_page_only outside homepage", () => {
  93 + helper.component.block = <any>{ settings: { display_user: "home_page_only" } };
  94 + expect(helper.component.canDisplay()).toEqual(false);
  95 + });
  96 +
  97 + it("return true in canDisplay when display_user is all for logged user", () => {
  98 + helper.component.block = <any>{ settings: { display_user: "all" } };
  99 + expect(helper.component.canDisplay()).toEqual(true);
  100 + });
  101 +
  102 + it("return true in canDisplay when display_user is all for not logged user", () => {
  103 + helper.component.currentUser = null;
  104 + helper.component.block = <any>{ settings: { display_user: "all" } };
  105 + expect(helper.component.canDisplay()).toEqual(true);
  106 + });
89 107  
  108 + it("return false in canDisplay when display_user is logged for not logged user", () => {
  109 + helper.component.currentUser = null;
  110 + helper.component.block = <any>{ settings: { display_user: "logged" } };
  111 + expect(helper.component.canDisplay()).toEqual(false);
90 112 });
91   -});
92 113 \ No newline at end of file
  114 +
  115 + it("return false in canDisplay when display_user is not_logged for logged user", () => {
  116 + helper.component.block = <any>{ settings: { display_user: "not_logged" } };
  117 + expect(helper.component.canDisplay()).toEqual(false);
  118 + });
  119 +
  120 + it("return false in canDisplay when current language is not equal to language in block settings", () => {
  121 + helper.component['translatorService'].currentLanguage = jasmine.createSpy("currentLanguage").and.returnValue("pt");
  122 + helper.component.block = <any>{ settings: { language: "en" } };
  123 + expect(helper.component.canDisplay()).toEqual(false);
  124 + });
  125 +
  126 + it("return false in canDisplay when hide is true", () => {
  127 + helper.component.block = <any>{ id: 1, hide: true };
  128 + expect(helper.component.canDisplay()).toEqual(false);
  129 + });
  130 +
  131 + it("return true in canDisplay when hide is not true", () => {
  132 + helper.component.block = <any>{ id: 1, hide: false };
  133 + expect(helper.component.canDisplay()).toEqual(true);
  134 + });
  135 +
  136 +});
... ...
src/app/layout/blocks/block.component.ts
1   -import { Input, Inject, Component } from 'ng-forward';
  1 +import { Input, Component, Inject } from 'ng-forward';
  2 +import { BlockEditionComponent } from './block-edition/block-edition.component';
  3 +import { BlockService } from '../../../lib/ng-noosfero-api/http/block.service';
  4 +import { NotificationService } from '../../shared/services/notification.service';
  5 +import { AuthService, SessionService, AuthEvents } from "../../login";
  6 +import { TranslatorService } from "../../shared/services/translator.service";
2 7  
3 8 @Component({
4 9 selector: 'noosfero-block',
5   - template: '<div></div>'
  10 + templateUrl: 'app/layout/blocks/block.html',
  11 + directives: [BlockEditionComponent]
6 12 })
7   -@Inject("$element", "$scope", "$injector", "$compile")
  13 +@Inject("$uibModal", "$scope", "$state", "$rootScope", BlockService, NotificationService, AuthService, SessionService, TranslatorService)
8 14 export class BlockComponent {
9 15  
10   - @Input() block: any;
11   - @Input() owner: any;
  16 + @Input() block: noosfero.Block;
  17 + @Input() owner: noosfero.Profile | noosfero.Environment;
  18 +
  19 + private modalInstance: any = null;
  20 + originalBlock: noosfero.Block;
  21 +
  22 + currentUser: noosfero.User;
  23 + isHomepage = true;
  24 + editionMode = false;
  25 +
  26 + constructor(private $uibModal: any,
  27 + private $scope: ng.IScope,
  28 + private $state: ng.ui.IStateService,
  29 + private $rootScope: ng.IRootScopeService,
  30 + private blockService: BlockService,
  31 + private notificationService: NotificationService,
  32 + private authService: AuthService,
  33 + private session: SessionService,
  34 + private translatorService: TranslatorService) {
  35 +
  36 + this.currentUser = this.session.currentUser();
  37 + this.authService.subscribe(AuthEvents[AuthEvents.loginSuccess], () => {
  38 + this.currentUser = this.session.currentUser();
  39 + this.verifyHomepage();
  40 + });
  41 + this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => {
  42 + this.currentUser = this.session.currentUser();
  43 + this.verifyHomepage();
  44 + });
  45 + this.$rootScope.$on("$stateChangeSuccess", (event: ng.IAngularEvent, toState: ng.ui.IState) => {
  46 + this.verifyHomepage();
  47 + });
  48 + }
12 49  
13 50 ngOnInit() {
14   - let blockName = (this.block && this.block.type) ? this.block.type.replace(/::/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block";
15   - this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope));
  51 + this.verifyHomepage();
  52 + }
  53 +
  54 + openEdit() {
  55 + this.editionMode = true;
  56 + if (!this.originalBlock) this.originalBlock = JSON.parse(JSON.stringify(this.block)); // deep copy of block data
  57 + this.modalInstance = this.$uibModal.open({
  58 + templateUrl: 'app/layout/blocks/block-edition/block-edition.html',
  59 + size: 'lg',
  60 + controller: BlockEditionComponent,
  61 + controllerAs: 'modal',
  62 + bindToController: true,
  63 + scope: this.$scope
  64 + });
  65 + }
  66 +
  67 + save() {
  68 + this.editionMode = false;
  69 + this.blockService.update(this.attributesToUpdate()).then(() => {
  70 + this.closeEdit();
  71 + this.notificationService.success({ title: "block.edition.success.title", message: "block.edition.success.message" });
  72 + });
  73 + }
  74 +
  75 + preview() {
  76 + this.closeEdit();
  77 + }
  78 +
  79 + cancel() {
  80 + this.editionMode = false;
  81 + this.block = this.originalBlock;
  82 + this.closeEdit();
16 83 }
17 84  
18   - constructor(private $element: any, private $scope: ng.IScope, private $injector: ng.auto.IInjectorService, private $compile: ng.ICompileService) {
  85 + canDisplay() {
  86 + return this.visible() && this.displayToUser() &&
  87 + this.displayOnLanguage(this.translatorService.currentLanguage()) &&
  88 + !this.block.hide;
19 89 }
  90 +
  91 + protected visible() {
  92 + let display = this.block.settings ? (<any>this.block.settings)['display'] : null;
  93 + return !display || ((this.isHomepage ? display !== "except_home_page" : display !== "home_page_only") && display !== "never");
  94 + }
  95 +
  96 + protected displayToUser() {
  97 + let displayUser = this.block.settings ? (<any>this.block.settings)['display_user'] : null;
  98 + return !displayUser || displayUser === "all" ||
  99 + (this.currentUser ? displayUser === "logged" : displayUser === "not_logged");
  100 + }
  101 +
  102 + protected displayOnLanguage(language: string) {
  103 + let displayLanguage = this.block.settings ? (<any>this.block.settings)['language'] : null;
  104 + return !displayLanguage || displayLanguage === "all" ||
  105 + language === displayLanguage;
  106 + }
  107 +
  108 + protected attributesToUpdate() {
  109 + return <any>{
  110 + id: this.block.id,
  111 + display: (<any>this.block.settings).display,
  112 + title: this.block.title,
  113 + display_user: (<any>this.block.settings).display_user,
  114 + language: (<any>this.block.settings).language
  115 + };
  116 + }
  117 +
  118 + protected verifyHomepage() {
  119 + if (this.owner && ["Profile", "Community", "Person"].indexOf((<any>this.owner)['type']) >= 0) {
  120 + let profile = <noosfero.Profile>this.owner;
  121 + this.isHomepage = this.$state.current.name === "main.profile.home";
  122 + if (profile.homepage) {
  123 + this.isHomepage = this.isHomepage ||
  124 + (this.$state.current.name === "main.profile.page" && profile.homepage === this.$state.params['page']);
  125 + } else {
  126 + this.isHomepage = this.isHomepage || this.$state.current.name === "main.profile.info";
  127 + }
  128 + } else {
  129 + this.isHomepage = this.$state.current.name === "main.environment.home";
  130 + }
  131 + }
  132 +
  133 + private closeEdit() {
  134 + if (this.modalInstance) {
  135 + this.modalInstance.close();
  136 + this.modalInstance = null;
  137 + }
  138 + }
  139 +
20 140 }
... ...
src/app/layout/blocks/block.html 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +<div ng-show="ctrl.canDisplay() || ctrl.editionMode" ng-class="{'invisible-block': !ctrl.canDisplay()}" class="noosfero-block" ng-mouseover="displayActions = true" ng-mouseleave="displayActions = false">
  2 + <div ng-show="displayActions" class="actions" permission="ctrl.block.permissions" permission-action="allow_edit">
  3 + <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i></button>
  4 + </div>
  5 + <div class="panel panel-default block {{ctrl.block.type | lowercase}}" >
  6 + <div class="panel-heading" ng-show="ctrl.block.title">
  7 + <h3 class="panel-title">{{ctrl.block.title}}</h3>
  8 + </div>
  9 + <div class="panel-body {{ctrl.block.type | lowercase}}" >
  10 + <noosfero-block-content [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-block-content>
  11 + </div>
  12 + </div>
  13 +</div>
... ...
src/app/layout/blocks/block.scss
1   -.block {
2   - .panel-title {
3   - font-size: 15px;
4   - font-weight: bold;
  1 +.noosfero-block {
  2 + position: relative;
  3 + &.invisible-block {
  4 + .block {
  5 + opacity: 0.4;
  6 + }
5 7 }
6   - .panel-heading {
7   - background-color: transparent;
8   - border: 0;
  8 + .block {
  9 + .panel-title {
  10 + font-size: 15px;
  11 + font-weight: bold;
  12 + }
  13 + .panel-heading {
  14 + background-color: transparent;
  15 + border: 0;
  16 + }
  17 + }
  18 + .actions {
  19 + position: absolute;
  20 + z-index: 100;
  21 + right: 1px;
  22 + top: 1px;
9 23 }
10 24 }
... ...
src/app/layout/blocks/index.ts
1 1 /* Module Index Entry - generated using the script npm run generate-index */
2   -export * from "./block.component";
  2 +export * from "./block-content.component";
... ...
src/app/layout/blocks/main/main-block.component.ts
1 1 import {Component, Input} from 'ng-forward';
2   -import {BlockComponent} from '../block.component';
3 2  
4 3 @Component({
5 4 selector: 'noosfero-main-block',
... ...
src/app/layout/boxes/box.html
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 | displayBlocks:ctrl.isHomepage:ctrl.currentUser | orderBy: 'position'" class="panel panel-default block {{block.type | lowercase}}" >
3   - <div class="panel-heading" ng-show="block.title">
4   - <h3 class="panel-title">{{block.title}}</h3>
5   - </div>
6   - <div class="panel-body {{block.type | lowercase}}" >
7   - <noosfero-block [block]="block" [owner]="ctrl.owner"></noosfero-block>
8   - </div>
9   - </div>
  2 + <noosfero-block ng-repeat="block in box.blocks | orderBy: 'position'" [block]="block" [owner]="ctrl.owner"></noosfero-block>
10 3 </div>
... ...
src/app/layout/boxes/boxes.component.spec.ts
... ... @@ -53,35 +53,4 @@ describe(&quot;Boxes Component&quot;, () =&gt; {
53 53 expect(helper.component.boxesOrder(properties['boxes'][0])).toEqual(1);
54 54 expect(helper.component.boxesOrder(properties['boxes'][1])).toEqual(0);
55 55 });
56   -
57   - it("set isHomepage as false by default", () => {
58   - expect(helper.component.isHomepage).toBeFalsy();
59   - });
60   -
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   - });
66   -
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   - });
72   -
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();
86   - });
87 56 });
... ...
src/app/layout/boxes/boxes.component.ts
1   -import {Input, Inject, Component} from 'ng-forward';
2   -import {SessionService, AuthService, AuthEvents} from "../../login";
3   -import {DisplayBlocks} from "./display-blocks.filter";
  1 +import {Input, Component} from 'ng-forward';
4 2  
5 3 @Component({
6 4 selector: "noosfero-boxes",
7   - templateUrl: "app/layout/boxes/boxes.html",
8   - directives: [DisplayBlocks]
  5 + templateUrl: "app/layout/boxes/boxes.html"
9 6 })
10   -@Inject("SessionService", 'AuthService', "$state", "$rootScope")
11 7 export class BoxesComponent {
12 8  
13 9 @Input() boxes: noosfero.Box[];
14 10 @Input() owner: noosfero.Profile | noosfero.Environment;
15 11  
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   - }
41   -
42 12 boxesOrder(box: noosfero.Box) {
43 13 if (box.position === 2) return 0;
44 14 return box.position;
45 15 }
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   - }
61 16 }
... ...
src/app/layout/boxes/display-blocks.filter.spec.ts
... ... @@ -1,100 +0,0 @@
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   - it("filter blocks when hide is true", done => {
87   - let blocks = [{ hide: true }];
88   - let filter = new DisplayBlocks(translatorService);
89   - expect(filter.transform(<any>blocks, true, null)).toEqual([]);
90   - done();
91   - });
92   -
93   - it("not filter blocks when hide is not true", done => {
94   - let blocks = [{ id: 1, hide: false }, { id: 2 }];
95   - let filter = new DisplayBlocks(translatorService);
96   - expect(filter.transform(<any>blocks, true, null)).toEqual(blocks);
97   - done();
98   - });
99   - });
100   -});
src/app/layout/boxes/display-blocks.filter.ts
... ... @@ -1,39 +0,0 @@
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   - && !block.hide) {
17   - selected.push(block);
18   - }
19   - }
20   - return selected;
21   - }
22   -
23   - private visible(block: noosfero.Block, isHomepage: boolean) {
24   - let display = block.settings ? (<any>block.settings)['display'] : null;
25   - return !display || ((isHomepage ? display !== "except_home_page" : display !== "home_page_only") && display !== "never");
26   - }
27   -
28   - private displayToUser(block: noosfero.Block, currentUser: noosfero.User) {
29   - let displayUser = block.settings ? (<any>block.settings)['display_user'] : null;
30   - return !displayUser || displayUser === "all" ||
31   - (currentUser ? displayUser === "logged" : displayUser === "not_logged");
32   - }
33   -
34   - private displayOnLanguage(block: noosfero.Block, language: string) {
35   - let displayLanguage = block.settings ? (<any>block.settings)['language'] : null;
36   - return !displayLanguage || displayLanguage === "all" ||
37   - language === displayLanguage;
38   - }
39   -}
src/app/main/main.component.ts
... ... @@ -6,6 +6,7 @@ import {ArticleViewComponent} from &quot;./../article/article-default-view.component&quot;
6 6  
7 7 import {ProfileComponent} from "../profile/profile.component";
8 8 import {BoxesComponent} from "../layout/boxes/boxes.component";
  9 +import {BlockContentComponent} from "../layout/blocks/block-content.component";
9 10 import {BlockComponent} from "../layout/blocks/block.component";
10 11 import {EnvironmentComponent} from "../environment/environment.component";
11 12 import {EnvironmentHomeComponent} from "../environment/environment-home.component";
... ... @@ -97,12 +98,13 @@ export class EnvironmentContent {
97 98 selector: 'main',
98 99 template: '<ui-view></ui-view>',
99 100 directives: [
100   - ArticleBlogComponent, ArticleViewComponent, BoxesComponent, BlockComponent,
  101 + ArticleBlogComponent, ArticleViewComponent, BoxesComponent, BlockContentComponent,
101 102 EnvironmentComponent, PeopleBlockComponent, DisplayContentBlockComponent,
102 103 LinkListBlockComponent, CommunitiesBlockComponent, HtmlEditorComponent, ProfileComponent,
103 104 MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent,
104 105 MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent,
105   - LoginBlockComponent, CustomContentComponent, PermissionDirective, SearchFormComponent, SearchComponent
  106 + LoginBlockComponent, CustomContentComponent, PermissionDirective, SearchFormComponent, SearchComponent,
  107 + BlockComponent
106 108 ].concat(plugins.mainComponents).concat(plugins.hotspots),
107 109 providers: [AuthService, SessionService, NotificationService, BodyStateClassesService,
108 110 "ngAnimate", "ngCookies", "ngStorage", "ngTouch",
... ...
src/languages/en.json
... ... @@ -6,6 +6,7 @@
6 6 "navbar.logout": "Log Out",
7 7 "navbar.login": "Login",
8 8 "navbar.toggle_menu": "Toggle navigation",
  9 + "language.all": "All languages",
9 10 "language.en": "English",
10 11 "language.pt": "Portuguese",
11 12 "language.selector": "Language",
... ... @@ -80,5 +81,20 @@
80 81 "designMode.toggle.OFF": "OFF",
81 82 "search.results.summary": "{results, plural, one{result} other{# results}}",
82 83 "search.results.query.label": "Search for:",
83   - "search.results.query.placeholder": "Search"
  84 + "search.results.query.placeholder": "Search",
  85 + "block.edit": "Edit",
  86 + "block.edition.title": "Edit Block",
  87 + "block.edition.success.title": "Good job!",
  88 + "block.edition.success.message": "Block saved!",
  89 + "block.edition.display.label": "Display this block:",
  90 + "block.edition.title.label": "Custom title for this block:",
  91 + "block.edition.display.always": "In all pages",
  92 + "block.edition.display.home_page_only": "Only in the homepage",
  93 + "block.edition.display.except_home_page": "In all pages, except in the homepage",
  94 + "block.edition.display.never": "Don't display",
  95 + "block.edition.display_user.label": "Display to users:",
  96 + "block.edition.display_user.all": "All users",
  97 + "block.edition.display_user.logged": "Logged",
  98 + "block.edition.display_user.not_logged": "Not logged",
  99 + "block.edition.language.label": "Show for:"
84 100 }
... ...
src/languages/pt.json
... ... @@ -6,6 +6,7 @@
6 6 "navbar.logout": "Sair",
7 7 "navbar.login": "Login",
8 8 "navbar.toggle_menu": "Abrir Menu",
  9 + "language.all": "Todos os idiomas",
9 10 "language.en": "Inglês",
10 11 "language.pt": "Português",
11 12 "language.selector": "Idioma",
... ... @@ -80,5 +81,20 @@
80 81 "designMode.toggle.OFF": "Desligado",
81 82 "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}",
82 83 "search.results.query.label": "Buscar:",
83   - "search.results.query.placeholder": "Informe aqui sua busca"
  84 + "search.results.query.placeholder": "Informe aqui sua busca",
  85 + "block.edit": "Editar",
  86 + "block.edition.title": "Editar Bloco",
  87 + "block.edition.success.title": "Bom trabalho!",
  88 + "block.edition.success.message": "Bloco salvo com sucesso!",
  89 + "block.edition.display.label": "Exibir este bloco:",
  90 + "block.edition.title.label": "Título personalizado do bloco:",
  91 + "block.edition.display.always": "Em todas as páginas",
  92 + "block.edition.display.home_page_only": "Apenas na página inicial",
  93 + "block.edition.display.except_home_page": "Em todas as páginas, exceto na inicial",
  94 + "block.edition.display.never": "Não exibir",
  95 + "block.edition.display_user.label": "Exibir para usuários:",
  96 + "block.edition.display_user.all": "Todos os usuários",
  97 + "block.edition.display_user.logged": "Logados",
  98 + "block.edition.display_user.not_logged": "Não logados",
  99 + "block.edition.language.label": "Exibir para:"
84 100 }
... ...
src/lib/ng-noosfero-api/http/block.service.spec.ts
... ... @@ -29,6 +29,16 @@ describe(&quot;Services&quot;, () =&gt; {
29 29 });
30 30 $httpBackend.flush();
31 31 });
  32 +
  33 + it("update block settings", (done) => {
  34 + let blockId = 1;
  35 + $httpBackend.expectPOST(`/api/v1/blocks/${blockId}`).respond(200, { block: { id: blockId } });
  36 + blockService.update(<any>{ id: blockId, display: 'never' }).then((result: noosfero.RestResult<noosfero.Block>) => {
  37 + expect(result.data).toEqual({ id: blockId });
  38 + done();
  39 + });
  40 + $httpBackend.flush();
  41 + });
32 42 });
33 43  
34 44  
... ...
src/lib/ng-noosfero-api/http/block.service.ts
... ... @@ -47,4 +47,12 @@ export class BlockService extends RestangularService&lt;noosfero.Block&gt; {
47 47 return deferred.promise;
48 48 }
49 49  
  50 + update(block: noosfero.Block) {
  51 + let element = this.getElement(block.id);
  52 + let headers = {
  53 + 'Content-Type': 'application/json'
  54 + };
  55 + return this.post(null, element, { block: block }, headers);
  56 + }
  57 +
50 58 }
... ...
src/lib/ng-noosfero-api/http/restangular_service.ts
... ... @@ -289,7 +289,7 @@ export abstract class RestangularService&lt;T extends noosfero.RestModel&gt; {
289 289 let restRequest: ng.IPromise<any>;
290 290  
291 291 if (rootElement) {
292   - restRequest = rootElement.customPOST(data, path, headers);
  292 + restRequest = rootElement.customPOST(data, path, null, headers);
293 293 } else {
294 294 restRequest = this.baseResource.customPOST(data, path, headers);
295 295 }
... ...
src/lib/ng-noosfero-api/interfaces/block.ts
... ... @@ -5,5 +5,6 @@ namespace noosfero {
5 5 limit: number;
6 6 api_content: any;
7 7 hide: boolean;
  8 + title: string;
8 9 }
9 10 }
... ...