Commit 399dd21d5c05c9a1a570c3fa7f0c9c914f62aeef

Authored by Leandro Santos
2 parents a2b0d2b1 8784fe3c
Exists in staging

Merge branch 'master' into staging

Showing 43 changed files with 728 additions and 116 deletions   Show diff stats
bower.json
... ... @@ -38,8 +38,8 @@
38 38 "angular-bind-html-compile": "^1.2.1",
39 39 "angular-click-outside": "^2.7.1",
40 40 "ng-ckeditor": "^0.2.1",
41   - "angular-bootstrap-toggle-switch": "^0.5.6",
42   - "angular-tag-cloud": "^0.3.0"
  41 + "angular-tag-cloud": "^0.3.0",
  42 + "angular-ui-switch": "^0.1.1"
43 43 },
44 44 "devDependencies": {
45 45 "angular-mocks": "~1.5.0"
... ...
gulp/conf.js
... ... @@ -55,12 +55,7 @@ exports.themeExists = function (path) {
55 55 * @param skin The skin name passed by arg to gulp task
56 56 */
57 57 exports.skinExists = function (skin) {
58   -
59   - //Skip skin verification on 'build' task
60   - // if(exports.isBuild()){
61   - // return;
62   - // }
63   -
  58 +
64 59 var skinPath, prefixPath = '';
65 60 var skinFile = skin+'.scss';
66 61 if (/skin-/.test(skin)) {
... ...
src/app/admin/layout-edit/designModeToggler.component.spec.ts
1 1 import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper';
  2 +import {INgForwardJQuery} from 'ng-forward/cjs/util/jqlite-extensions';
2 3 import * as helpers from '../../../spec/helpers';
3 4 import {DesignModeTogglerComponent} from './designModeToggler.component';
4 5 import {DesignModeService} from './designMode.service';
5 6 import {INoosferoLocalStorage} from "./../../shared/models/interfaces";
6 7  
7 8 describe('DesignModeToggler Component', () => {
8   - const htmlTemplate: string = '<noosfero-design-toggler></noosfero-design-toggler>';
  9 + const htmlTemplate: string = '<design-toggler></design-toggler>';
9 10  
10 11 let helper: ComponentTestHelper<DesignModeTogglerComponent>;
11 12 beforeEach(() => {
12 13 angular.mock.module('templates');
13 14 angular.mock.module('ngSanitize');
14   - angular.mock.module('toggle-switch');
  15 + angular.mock.module('uiSwitch');
15 16 });
16 17  
17 18 let designModeService: DesignModeService;
... ... @@ -30,12 +31,12 @@ describe(&#39;DesignModeToggler Component&#39;, () =&gt; {
30 31 });
31 32  
32 33 it('changes css classes representing the switch is on or off', () => {
33   - expect(helper.debugElement.query('div.switch-animate').hasClass('switch-off')).toBeTruthy();
34   - expect(helper.debugElement.query('div.switch-animate').hasClass('switch-on')).toBeFalsy();
  34 + let switchEl: INgForwardJQuery = helper.debugElement.query('span.switch');
  35 +
  36 + expect(switchEl.hasClass('checked')).toBeFalsy();
35 37 helper.component.inDesignMode = true;
36 38 helper.detectChanges();
37   - expect(helper.debugElement.query('div.switch-animate').hasClass('switch-on')).toBeTruthy();
38   - expect(helper.debugElement.query('div.switch-animate').hasClass('switch-off')).toBeFalsy();
  39 + expect(switchEl.hasClass('checked')).toBeTruthy();
39 40 });
40 41  
41 42 it('emits event with value "true" when changing inDesignMode to On', (done) => {
... ... @@ -60,4 +61,4 @@ describe(&#39;DesignModeToggler Component&#39;, () =&gt; {
60 61 helper.component.inDesignMode = false;
61 62 helper.detectChanges();
62 63 });
63   -});
64 64 \ No newline at end of file
  65 +});
... ...
src/app/admin/layout-edit/designModeToggler.component.ts
1   -import {Component, Inject} from 'ng-forward';
  1 +import {Component, Inject, Input} from 'ng-forward';
2 2 import {DesignModeService} from './designMode.service';
3 3 import {AuthService, AuthEvents} from '../../login';
4 4  
5 5 @Component({
6   - selector: 'noosfero-design-toggler',
  6 + selector: 'design-toggler',
7 7 templateUrl: 'app/admin/layout-edit/designModeToggler.html'
8 8 })
9   -@Inject(DesignModeService, AuthService)
  9 +@Inject(DesignModeService, AuthService, '$sce')
10 10 export class DesignModeTogglerComponent {
11 11  
12   - icon: string = "&nbsp;<i class='glyphicon glyphicon-wrench'></i>&nbsp;";
  12 + private _inDesignMode: boolean = false;
13 13  
14   - constructor(private designModeService: DesignModeService, private authService: AuthService) {
  14 + constructor(private designModeService: DesignModeService, private authService: AuthService, private $sce: ng.ISCEService) {
15 15 this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => {
16 16 this.designModeService.destroy();
17 17 });
18 18 }
19 19  
20   - private _inDesignMode: boolean = false;
21   -
22 20 get inDesignMode(): boolean {
23 21 return this.designModeService.isInDesignMode();
24 22 };
... ...
src/app/admin/layout-edit/designModeToggler.html
1   -<toggle-switch
2   - html="true"
3   - ng-model="ctrl.inDesignMode"
4   - on-label="{{'designMode.toggle.ON' | translate}}"
5   - off-label="{{'designMode.toggle.OFF' | translate}}"
6   - class="switch-small"
7   - knob-label="{{ ctrl.icon + ('designMode.label' | translate) }}">
8   -</toggle-switch>
9 1 \ No newline at end of file
  2 +<switch id="enabled" name="enabled" ng-model="ctrl.inDesignMode" on="ON" off="OFF" class="green"></switch>
... ...
src/app/admin/layout-edit/designModeToggler.scss 0 → 100644
... ... @@ -0,0 +1,55 @@
  1 +$off-text: #949494;
  2 +$off-color: #EFEFEF;
  3 +$blue-color: #36B4F6;
  4 +$red-color: #DF3B3A;
  5 +
  6 +body.noosfero-design-on {
  7 +
  8 + div.content-wrapper {
  9 + opacity: 0.5;
  10 + }
  11 +}
  12 +
  13 +.switch {
  14 + @include not-select();
  15 + background-color: $off-color;
  16 +
  17 + &[on][off] {
  18 + width: 60px;
  19 + }
  20 +
  21 + &:focus {
  22 + outline: none;
  23 + }
  24 +
  25 + &.checked small {
  26 + left: initial;
  27 + right: 0px;
  28 + }
  29 +
  30 + &.blue.checked {
  31 + background: $blue-color;
  32 + border-color: $blue-color;
  33 + }
  34 +
  35 + &.red.checked {
  36 + background: $red-color;
  37 + border-color: $red-color;
  38 + }
  39 +
  40 + %switch-label {
  41 + font-weight: bold;
  42 + }
  43 +
  44 + .on {
  45 + @extend %switch-label;
  46 + }
  47 +
  48 + .off {
  49 + @extend %switch-label;
  50 + right: 2px;
  51 + top: 25%;
  52 + color: $off-text;
  53 + text-align: center;
  54 + }
  55 +}
... ...
src/app/environment/environment.html
1 1 <div class="environment-container">
2 2 <div class="row">
3   - <noosfero-boxes ng-if="vm.boxes" [boxes]="vm.boxes" [owner]="vm.environment"></noosfero-boxes>
  3 + <noosfero-boxes ng-if="vm.boxes"
  4 + [layout]="vm.environment.layout_template"
  5 + [boxes]="vm.boxes"
  6 + [owner]="vm.environment">
  7 + </noosfero-boxes>
4 8 </div>
5 9 </div>
... ...
src/app/layout/blocks/recent-activities-plugin-activities/activities/add_member_in_community.html 0 → 100644
... ... @@ -0,0 +1 @@
  1 +{{'joined the community' | translate}}
... ...
src/app/layout/blocks/recent-activities-plugin-activities/activities/create_article.html 0 → 100644
... ... @@ -0,0 +1 @@
  1 +{{'published an article:' | translate}} <a ng-href="{{ctrl.urlFor(activity.params.url)}}">{{activity.params.name}}</a>
... ...
src/app/layout/blocks/recent-activities-plugin-activities/activities/event.html 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<img ng-src="{{activity.params.first_image}}" ng-attr-alt="{{activity.params.name}}" class="event-image" />
  2 +<p class="event-description"><b>{{activity.params.name}}</b><br />{{activity.params.lead | stripTags}}</p>
  3 +<br style="clear: both;" />
... ...
src/app/layout/blocks/recent-activities-plugin-activities/activities/favorite_enterprise.html 0 → 100644
... ... @@ -0,0 +1 @@
  1 +{{'favorited an enterprise:' | translate}} <a ng-href="{{ctrl.urlFor(activity.params.enterprise_url)}}">{{activity.params.enterprise_name}}</a>
... ...
src/app/layout/blocks/recent-activities-plugin-activities/activities/new_friendship.html 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +{{'has made new friends:' | translate}}<br />
  2 +<span ng-repeat="name in activity.params.friend_name">
  3 + <a ng-href="{{ctrl.urlFor(activity.params.friend_url[$index])}}" ng-attr-title="{{name}}">
  4 + <img ng-src="{{activity.params.friend_profile_custom_icon[$index]}}" ng-attr-alt="{{name}}" />
  5 + </a>
  6 +</span>
... ...
src/app/layout/blocks/recent-activities-plugin-activities/activities/upload_image.html 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +{{'uploaded images:' | translate}}<br />
  2 +<span ng-repeat="path in activity.params.thumbnail_path">
  3 + <a class="upimg" ng-href="{{ctrl.urlFor(activity.params.view_url[$index])}}" ng-style="{'background-image':'url({{path}})'}">
  4 + <span>{{path}}</span>
  5 + </a>
  6 +</span>
... ...
src/app/layout/blocks/recent-activities-plugin-activities/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./recent-activities-plugin-activities-block.component";
... ...
src/app/layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,56 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Provider, Input, provide, Component} from 'ng-forward';
  3 +import {provideFilters} from '../../../../spec/helpers';
  4 +import {RecentActivitiesPluginActivitiesBlockComponent} from './recent-activities-plugin-activities-block.component';
  5 +import * as helpers from "./../../../../spec/helpers";
  6 +
  7 +const htmlTemplate: string = '<noosfero-recent-activities-plugin-activities-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-recent-activities-plugin-activities-block>';
  8 +
  9 +const tcb = new TestComponentBuilder();
  10 +
  11 +describe("Components", () => {
  12 + describe("Recent Activities Block Component", () => {
  13 +
  14 + let settingsObj = {};
  15 + let person = <noosfero.Person>{ name: "Person" };
  16 + let mockedService = {
  17 + getApiContent: (block: noosfero.Block): any => {
  18 + return Promise.resolve({ activities: [{ verb: 'new_friendship' }], headers: (name: string) => { return name; } });
  19 + }
  20 + };
  21 + beforeEach(angular.mock.module("templates"));
  22 +
  23 + let state = jasmine.createSpyObj("state", ["go"]);
  24 +
  25 +
  26 + function getProviders() {
  27 + return [
  28 + new Provider('$state', { useValue: state }),
  29 + new Provider('BlockService', {
  30 + useValue: mockedService
  31 + })
  32 + ].concat(provideFilters("truncateFilter", "stripTagsFilter"));
  33 + }
  34 + let componentClass: any = null;
  35 +
  36 + function getComponent() {
  37 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [RecentActivitiesPluginActivitiesBlockComponent], providers: getProviders() })
  38 + class BlockContainerComponent {
  39 + block = { type: 'Block', settings: settingsObj };
  40 + owner = person;
  41 + constructor() {
  42 + }
  43 + }
  44 + return BlockContainerComponent;
  45 + }
  46 +
  47 + it("get activities from block service", done => {
  48 + tcb.createAsync(getComponent()).then(fixture => {
  49 + let RecentActivitiesPluginActivitiesBlock: RecentActivitiesPluginActivitiesBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  50 + expect(RecentActivitiesPluginActivitiesBlock.activities[0]['verb']).toEqual('new_friendship');
  51 + done();
  52 + });
  53 + });
  54 +
  55 + });
  56 +});
... ...
src/app/layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.component.ts 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +import {Component, Inject, Input} from "ng-forward";
  2 +import {BlockService} from "../../../../lib/ng-noosfero-api/http/block.service";
  3 +import {Arrays} from "./../../../../lib/util/arrays";
  4 +
  5 +@Component({
  6 + selector: "noosfero-recent-activities-plugin-activities-block",
  7 + templateUrl: 'app/layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.html'
  8 +})
  9 +@Inject(BlockService, "$state")
  10 +export class RecentActivitiesPluginActivitiesBlockComponent {
  11 +
  12 + @Input() block: any;
  13 + @Input() owner: any;
  14 +
  15 + profile: any;
  16 + activities: any;
  17 +
  18 + constructor(private blockService: BlockService, private $state: any) { }
  19 +
  20 + getActivityTemplate(activity: any) {
  21 + if (activity.label === 'events') {
  22 + return 'app/layout/blocks/recent-activities-plugin-activities/activities/event.html';
  23 + }
  24 + else {
  25 + return 'app/layout/blocks/recent-activities-plugin-activities/activities/' + activity.verb + '.html';
  26 + }
  27 + }
  28 +
  29 + urlFor(params: any) {
  30 + let url = '//' + params.host;
  31 + if (params.port) {
  32 + url += ':' + params.port;
  33 + }
  34 + url += '/' + params.profile + '/';
  35 + if (params.page) {
  36 + url += params.page.join('/');
  37 + }
  38 + return url;
  39 + }
  40 +
  41 + ngOnInit() {
  42 + this.profile = this.owner;
  43 + this.activities = [];
  44 + this.blockService.getApiContent(this.block).then((content: any) => {
  45 + this.activities = content.activities;
  46 + });
  47 + }
  48 +}
... ...
src/app/layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.html 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +<div class="deckgrid recent-activities-block">
  2 + <div ng-repeat="activity in ctrl.activities" class="a-card panel media">
  3 +
  4 + <div class="subheader">
  5 + <p ng-if="activity.label === 'events'">
  6 + {{ 'activities.event.description' | translate }} <b>{{ activity.start_date | date:longDate }}</b> {{ 'time.at' | translate }} {{ activity.start_date | date:'HH:mm' }} - <a ng-href="/{{activity.user.identifier}}">{{activity.user.name}}</a> <span class="activity-label">{{activity.label}}</span>
  7 + </p>
  8 +
  9 + <p ng-if="activity.label !== 'events'">
  10 + {{ 'date.on' | translate }} <b>{{ activity.created_at | date:longDate }}</b> {{ 'time.at' | translate }} {{ activity.created_at | date:'HH:mm' }} - <a ng-href="/{{activity.user.identifier}}">{{activity.user.name}}</a> <span class="activity-label">{{activity.label}}</span>
  11 + </p>
  12 + </div>
  13 +
  14 + <div class="header media-body">
  15 + <h5 class="title media-heading">
  16 + <ng-include src="ctrl.getActivityTemplate(activity)"></ng-include>
  17 + </h5>
  18 + </div>
  19 +
  20 + <hr />
  21 + </div>
  22 +</div>
... ...
src/app/layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.scss 0 → 100644
... ... @@ -0,0 +1,72 @@
  1 +.col-md-2-5 .deckgrid[deckgrid]::before {
  2 + visibility: hidden;
  3 +}
  4 +
  5 +.recent-activities-block {
  6 + img, .upimg {
  7 + padding: 1px;
  8 + border: 1px solid #ccc;
  9 + margin-right: 2px;
  10 + margin-top: 2px;
  11 + }
  12 +
  13 + .upimg {
  14 + display: inline-block;
  15 + width: 20px;
  16 + height: 20px;
  17 + background-size: cover;
  18 +
  19 + span {
  20 + display: none;
  21 + }
  22 + }
  23 +
  24 + .panel {
  25 + margin-bottom: 15px;
  26 + box-shadow: none;
  27 + border-radius: 0;
  28 + }
  29 +
  30 + h5 {
  31 + text-transform: capitalize;
  32 + }
  33 +
  34 + .subheader {
  35 + p {
  36 + margin: 2px 0;
  37 + font-size: 11px;
  38 + }
  39 + }
  40 +
  41 + hr {
  42 + border: 0;
  43 + height: 1px;
  44 + background: #ccc;
  45 + margin: 0;
  46 + margin-top: 15px;
  47 + }
  48 +
  49 + .activity-label {
  50 + @include border-radius(2px);
  51 + font-size: 11px;
  52 + text-transform: capitalize;
  53 + background: #333;
  54 + color: #fff;
  55 + padding: 2px;
  56 + margin-left: 5px;
  57 + display: inline-block;
  58 + }
  59 +
  60 + .event-image {
  61 + width: 15%;
  62 + height: auto;
  63 + float: left;
  64 + }
  65 +
  66 + .event-description {
  67 + width: 83%;
  68 + margin: 2px 0 0 2px;
  69 + float: left;
  70 + display: block;
  71 + }
  72 +}
... ...
src/app/layout/boxes/box.html
1   -<div ng-class="{'col-md-2-5': box.position!=1, 'col-md-7': box.position==1}">
2   - <noosfero-block ng-repeat="block in box.blocks | orderBy: 'position'" [block]="block" [owner]="ctrl.owner"></noosfero-block>
  1 +<div ng-class="box.position | setBoxLayout:ctrl.layout">
  2 + <noosfero-block
  3 + ng-repeat="block in box.blocks | orderBy: 'position'"
  4 + [block]="block" [owner]="ctrl.owner">
  5 + </noosfero-block>
3 6 </div>
... ...
src/app/layout/boxes/boxes.component.spec.ts
... ... @@ -45,12 +45,7 @@ describe(&quot;Boxes Component&quot;, () =&gt; {
45 45 state.current = { name: "" };
46 46  
47 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);
  48 + expect(helper.find('div.col-md-6').length).toEqual(1);
  49 + expect(helper.find('div.col-md-3').length).toEqual(1);
55 50 });
56 51 });
... ...
src/app/layout/boxes/boxes.component.ts
1 1 import {Input, Component} from 'ng-forward';
  2 +import {DisplayBoxes} from "./display-boxes.filter";
  3 +import {SetBoxLayout} from "./set-box-layout.filter";
2 4  
3 5 @Component({
4 6 selector: "noosfero-boxes",
5   - templateUrl: "app/layout/boxes/boxes.html"
  7 + templateUrl: "app/layout/boxes/boxes.html",
  8 + directives: [DisplayBoxes, SetBoxLayout]
6 9 })
7 10 export class BoxesComponent {
8 11  
9 12 @Input() boxes: noosfero.Box[];
10 13 @Input() owner: noosfero.Profile | noosfero.Environment;
  14 + @Input() layout: string;
11 15  
12   - boxesOrder(box: noosfero.Box) {
13   - if (box.position === 2) return 0;
14   - return box.position;
15   - }
16 16 }
... ...
src/app/layout/boxes/boxes.html
1   -<ng-include ng-repeat="box in ctrl.boxes | orderBy: ctrl.boxesOrder" src="'app/layout/boxes/box.html'"></ng-include>
  1 +<ng-include ng-repeat="box in ctrl.boxes | displayBoxes:ctrl.layout" src="'app/layout/boxes/box.html'"></ng-include>
... ...
src/app/layout/boxes/display-boxes.filter.spec.ts 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +import {DisplayBoxes} from './display-boxes.filter';
  2 +
  3 +describe("Boxes Filters", () => {
  4 + describe("Display Boxes Filter", () => {
  5 +
  6 + let boxes: noosfero.Box[] = [
  7 + {id: 1, position: 1 },
  8 + {id: 2, position: 2 },
  9 + {id: 3, position: 3 },
  10 + {id: 4, position: 4 }
  11 + ];
  12 +
  13 + let expected_on_default: noosfero.Box[] = [
  14 + {id: 1, position: 1 },
  15 + {id: 2, position: 2 },
  16 + {id: 3, position: 3 },
  17 + ];
  18 +
  19 + let expected_on_rightbar: noosfero.Box[] = [
  20 + {id: 1, position: 1 },
  21 + {id: 3, position: 3 },
  22 + ];
  23 +
  24 + it("filter boxes when layout is set to default", done => {
  25 + let filter = new DisplayBoxes();
  26 +
  27 + let filtered_boxes: noosfero.Box[] = filter.transform(boxes, "default");
  28 + expect(filtered_boxes.length).toEqual(3);
  29 + expect(filtered_boxes).toEqual(expected_on_default);
  30 + done();
  31 + });
  32 +
  33 + it("filter boxes when layout is set to rightbar", done => {
  34 + let filter = new DisplayBoxes();
  35 +
  36 + let filtered_boxes: noosfero.Box[] = filter.transform(boxes, "rightbar");
  37 + expect(filtered_boxes.length).toEqual(2);
  38 + expect(filtered_boxes).toEqual(expected_on_rightbar);
  39 + done();
  40 + });
  41 +
  42 + it("filter boxes with default layout when invalid layout is given", done => {
  43 + let filter = new DisplayBoxes();
  44 +
  45 + let filtered_boxes: noosfero.Box[] = filter.transform(boxes, "");
  46 + expect(filtered_boxes.length).toEqual(3);
  47 + expect(filtered_boxes).toEqual(expected_on_default);
  48 + done();
  49 + });
  50 + });
  51 +});
... ...
src/app/layout/boxes/display-boxes.filter.ts 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +import {Pipe, Inject} from "ng-forward";
  2 +
  3 +@Pipe("displayBoxes")
  4 +export class DisplayBoxes {
  5 +
  6 + transform(boxes: noosfero.Box[], layout: string) {
  7 + let function_str: string = "visible_on" + layout;
  8 + let valid_boxes: number[] = [];
  9 + let selected: noosfero.Box[] = [];
  10 + boxes = boxes || [];
  11 +
  12 + if (layout === "rightbar") {
  13 + valid_boxes = this.visible_on_right_bar();
  14 + }else {
  15 + valid_boxes = this.visible_on_default();
  16 + }
  17 +
  18 + for (let box of boxes) {
  19 + if (valid_boxes.indexOf(box.position) !== -1) {
  20 + selected.push(box);
  21 + }
  22 + }
  23 + return selected;
  24 + }
  25 +
  26 + private visible_on_default() {
  27 + return [1, 2, 3];
  28 + }
  29 +
  30 + private visible_on_right_bar() {
  31 + return [1, 3];
  32 + }
  33 +
  34 +}
  35 +
... ...
src/app/layout/boxes/set-box-layout.filter.spec.ts 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +import {SetBoxLayout} from './set-box-layout.filter';
  2 +
  3 +describe("Boxes Filters", () => {
  4 + describe("Set Box Layout Filter", () => {
  5 + let box_layout_filter = new SetBoxLayout();
  6 + describe("When layout is set to default", () => {
  7 + it("return style when box position is 1 ", done => {
  8 + expect(box_layout_filter.transform(1, "default")).toEqual("col-md-6 col-md-push-3");
  9 + done();
  10 + });
  11 + it("return style when box position is 2", done => {
  12 + expect(box_layout_filter.transform(2, "default")).toEqual("col-md-3 col-md-pull-6");
  13 + done();
  14 + });
  15 + it("return style when any other position is given", done => {
  16 + expect(box_layout_filter.transform(null, "default")).toEqual("col-md-3");
  17 + expect(box_layout_filter.transform(3, "default")).toEqual("col-md-3");
  18 + expect(box_layout_filter.transform(99, "default")).toEqual("col-md-3");
  19 + done();
  20 + });
  21 + });
  22 +
  23 + describe("When layout is set to right_bar", () => {
  24 + it("return style when box position is 1 ", done => {
  25 + expect(box_layout_filter.transform(1, "rightbar")).toEqual("col-sm-12 col-md-8");
  26 + done();
  27 + });
  28 + it("return style when box other position is given", done => {
  29 + expect(box_layout_filter.transform(2, "rightbar")).toEqual("col-sm-12 col-md-4");
  30 + done();
  31 + });
  32 + });
  33 + });
  34 +});
... ...
src/app/layout/boxes/set-box-layout.filter.ts 0 → 100644
... ... @@ -0,0 +1,32 @@
  1 +import {Pipe, Inject} from "ng-forward";
  2 +
  3 +@Pipe("setBoxLayout")
  4 +export class SetBoxLayout {
  5 +
  6 + transform(pos: number, layout: string) {
  7 + if (layout === "rightbar") {
  8 + return this.right_bar(pos);
  9 + }else {
  10 + return this.default(pos);
  11 + }
  12 + }
  13 +
  14 + private default(position: number) {
  15 + if (position === 1) {
  16 + return "col-md-6 col-md-push-3";
  17 + }else if (position === 2) {
  18 + return "col-md-3 col-md-pull-6";
  19 + }else {
  20 + return "col-md-3";
  21 + }
  22 + }
  23 +
  24 + private right_bar(position: number) {
  25 + if (position === 1) {
  26 + return "col-sm-12 col-md-8";
  27 + }else {
  28 + return "col-sm-12 col-md-4";
  29 + }
  30 + }
  31 +
  32 +}
... ...
src/app/layout/scss/_layout.scss
... ... @@ -34,17 +34,4 @@
34 34 .main-box-body {
35 35 padding: 0 20px 20px 20px;
36 36 }
37   -}
38   -
39   -ul.nav > li.toggler-container {
40   - position: relative;
41   - padding-top: 12px;
42   -}
43   -
44   -.noosfero-main-toolbar {
45   - padding: 5px;
46   - @include make-row();
47   - margin-left: 0px;
48   - margin-right: 0px;
49   - background-color: #edecec;
50   -}
  37 +}
51 38 \ No newline at end of file
... ...
src/app/layout/scss/_mixins.scss
... ... @@ -4,6 +4,12 @@
4 4 background-clip: padding-box; /* stops bg color from leaking outside the border: */
5 5 }
6 6  
  7 +@mixin box-shadow($args...) {
  8 + -webkit-box-shadow: $args;
  9 + -moz-box-shadow: $args;
  10 + box-shadow: $args;
  11 +}
  12 +
7 13 @mixin checkbox-mark($width, $height, $top, $left, $bg, $border, $content: "", $cursor: pointer, $position: absolute) {
8 14 width: $width;
9 15 height: $height;
... ... @@ -48,3 +54,31 @@
48 54 outline-width: $width;
49 55 }
50 56 }
  57 +
  58 +@mixin not-select() {
  59 + -webkit-touch-callout: none; /* iOS Safari */
  60 + -webkit-user-select: none; /* Chrome/Safari/Opera */
  61 + -khtml-user-select: none; /* Konqueror */
  62 + -moz-user-select: none; /* Firefox */
  63 + -ms-user-select: none; /* Internet Explorer/Edge */
  64 + user-select: none; /* Non-prefixed version, currently
  65 + not supported by any browser */
  66 +}
  67 +
  68 +@mixin keyframes($animation-name) {
  69 + @-webkit-keyframes #{$animation-name} {
  70 + @content;
  71 + }
  72 + @-moz-keyframes #{$animation-name} {
  73 + @content;
  74 + }
  75 + @-ms-keyframes #{$animation-name} {
  76 + @content;
  77 + }
  78 + @-o-keyframes #{$animation-name} {
  79 + @content;
  80 + }
  81 + @keyframes #{$animation-name} {
  82 + @content;
  83 + }
  84 +}
... ...
src/app/layout/scss/skins/_whbl.scss
... ... @@ -291,11 +291,7 @@ $whbl-font-color: #16191c;
291 291 background-color: #fff;
292 292 }
293 293  
294   - noosfero-design-toggler .ats-switch .knob i {
295   - color: #999999;
296   - }
297   -
298   - .ats-switch .knob {
  294 + .ats-switch .knob {
299 295 padding-right: 15px;
300 296 }
301 297  
... ...
src/app/layout/services/body-state-classes.service.spec.ts
... ... @@ -193,7 +193,6 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
193 193  
194 194 service.start();
195 195  
196   - debugger;
197 196 designModeService.setInDesignMode(true);
198 197  
199 198  
... ...
src/app/main/main.component.ts
... ... @@ -20,6 +20,7 @@ import {StatisticsBlockComponent} from &quot;../layout/blocks/statistics/statistics-b
20 20 import {PersonTagsPluginInterestsBlockComponent} from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component";
21 21 import {TagsBlockComponent} from "../layout/blocks/tags/tags-block.component";
22 22 import {CustomContentComponent} from "../profile/custom-content/custom-content.component";
  23 +import {RecentActivitiesPluginActivitiesBlockComponent} from "../layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.component";
23 24  
24 25 import {MembersBlockComponent} from "../layout/blocks/members/members-block.component";
25 26 import {CommunitiesBlockComponent} from "../layout/blocks/communities/communities-block.component";
... ... @@ -87,7 +88,7 @@ export class EnvironmentContent {
87 88 * @name main.Main
88 89 * @requires AuthService, Session, Notification, ArticleBlog, ArticleView, Boxes, Block, LinkListBlock,
89 90 * MainBlock, RecentDocumentsBlock, Navbar, ProfileImageBlock, MembersBlock,
90   - * NoosferoTemplate, DateFormat, RawHTMLBlock, PersonTagsPluginInterestsBlock
  91 + * NoosferoTemplate, DateFormat, RawHTMLBlock, PersonTagsPluginInterestsBlock, RecentActivitiesPluginActivitiesBlock,
91 92 * @description
92 93 * The Main controller for the Noosfero Angular Theme application.
93 94 *
... ... @@ -106,7 +107,7 @@ export class EnvironmentContent {
106 107 MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent,
107 108 MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent,
108 109 LoginBlockComponent, CustomContentComponent, PermissionDirective, SearchFormComponent, SearchComponent,
109   - PersonTagsPluginInterestsBlockComponent, TagsBlockComponent, BlockComponent
  110 + PersonTagsPluginInterestsBlockComponent, TagsBlockComponent, RecentActivitiesPluginActivitiesBlockComponent, BlockComponent
110 111 ].concat(plugins.mainComponents).concat(plugins.hotspots),
111 112 providers: [AuthService, SessionService, NotificationService, BodyStateClassesService,
112 113 "ngAnimate", "ngCookies", "ngStorage", "ngTouch",
... ... @@ -115,7 +116,7 @@ export class EnvironmentContent {
115 116 "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid",
116 117 "angular-timeline", "duScroll", "oitozero.ngSweetAlert",
117 118 "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad",
118   - "angular-click-outside", "toggle-switch", "ngTagCloud", "noosfero.init"]
  119 + "angular-click-outside", "ngTagCloud", "noosfero.init", "uiSwitch"]
119 120 })
120 121 @StateConfig([
121 122 {
... ...
src/app/profile/config-bar.component.ts 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +import {Component, Inject, provide} from "ng-forward";
  2 +import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service";
  3 +
  4 +@Component({
  5 + selector: "configbar",
  6 + templateUrl: "app/profile/configbar.html",
  7 + providers: [
  8 + provide('profileService', { useClass: ProfileService })
  9 + ]
  10 +})
  11 +@Inject(ProfileService)
  12 +export class ConfigBarComponent {
  13 + profile: noosfero.Profile;
  14 + parentId: number;
  15 +
  16 + constructor(profileService: ProfileService) {
  17 + profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
  18 + this.profile = profile;
  19 + });
  20 + }
  21 +}
... ...
src/app/profile/configbar.html 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +<div id="config-tool" ng-class="{'closed': !showConfig}">
  2 + <a id="config-tool-cog" ng-click="showConfig = !showConfig">
  3 + <i class="fa fa-cog" ng-class="{'anim-icon': showConfig}"></i>
  4 + </a>
  5 +
  6 + <div id="config-tool-options">
  7 + <h4>{{ 'configbar.label' | translate }}</h4>
  8 + <div class="section-title">{{ 'configbar.section.layout' | translate }}</div>
  9 + <ul>
  10 + <li>
  11 + <label class="pull-left">
  12 + <i class="fa fa-th-large"></i>
  13 + <span class="item-label">{{ 'designMode.label' | translate }}</span>
  14 + </label>
  15 + <label class="pull-right">
  16 + <design-toggler></design-toggler>
  17 + </label>
  18 + </li>
  19 + </ul>
  20 + </div>
  21 +</div>
... ...
src/app/profile/configbar.scss 0 → 100644
... ... @@ -0,0 +1,110 @@
  1 +@import "../layout/scss/mixins";
  2 +
  3 +@include keyframes(rotating) {
  4 + from {
  5 + transform: rotate(0deg);
  6 + }
  7 + to {
  8 + transform: rotate(360deg);
  9 + }
  10 +}
  11 +
  12 +/* CONFIG TOOLS */
  13 +#config-tool {
  14 + @include transition(all 0.2s ease-in-out 0s);
  15 + @include box-shadow(0px 1px 4px rgba(0, 0, 0, 0.3));
  16 + position: fixed;
  17 + right: 0;
  18 + top: 120px;
  19 + min-width: 200px;
  20 + z-index: 1000;
  21 +
  22 + #config-tool-cog {
  23 + @include border-radius($border-radius-base 0 0 $border-radius-base);
  24 + @include transition(all 0.1s ease-in-out 0s);
  25 + @include box-shadow(-3px 3px 3px -2px rgba(0, 0, 0, 0.1));
  26 + background: #fff;
  27 + cursor: pointer;
  28 + left: -50px;
  29 + padding: 10px;
  30 + position: absolute;
  31 + text-align: center;
  32 + width: 50px;
  33 + top: 0;
  34 +
  35 + i {
  36 + font-size: 2.2em;
  37 + }
  38 +
  39 + .anim-icon {
  40 + @include animation(rotating 0.7s ease-in-out 0s);
  41 + }
  42 + }
  43 +
  44 + &.closed {
  45 + right: -280px;
  46 + box-shadow: none;
  47 +
  48 + #config-tool-cog {
  49 + &:hover {
  50 + background-color: $primary-color;
  51 + color: #fff;
  52 + }
  53 + }
  54 + }
  55 +
  56 + &.opened {
  57 + right: 0;
  58 + }
  59 +
  60 + .section-title {
  61 + margin: 30px 0 5px;
  62 + font: {
  63 + size: 15px;
  64 + weight: bold;
  65 + }
  66 +
  67 + }
  68 +
  69 + #config-tool-options {
  70 + @include box-shadow(-3px 3px 3px -2px rgba(0, 0, 0, 0.1));
  71 + background: #fff;
  72 + padding: 15px;
  73 +
  74 + h4 {
  75 + margin: 0;
  76 + font-size: 1.3em;
  77 + }
  78 +
  79 + ul {
  80 + list-style: none;
  81 + border-radius: 2px;
  82 + padding: 0;
  83 +
  84 + background: {
  85 + clip: padding-box;
  86 + color: #f1f3f2;
  87 + }
  88 +
  89 + li {
  90 + padding: 10px;
  91 + width: 250px;
  92 + min-height: 50px;
  93 + font: {
  94 + size: 13px;
  95 + weight: 300;
  96 + }
  97 +
  98 + .checkbox {
  99 + margin: 0;
  100 + }
  101 +
  102 + .pull-left {
  103 + margin-top: 8px;
  104 + font-weight: normal;
  105 + }
  106 +
  107 + }
  108 + }
  109 + }
  110 +}
... ...
src/app/profile/profile-toolbar.component.ts
... ... @@ -1,21 +0,0 @@
1   -import {Component, Inject, provide} from "ng-forward";
2   -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service";
3   -
4   -@Component({
5   - selector: "profile-toolbar",
6   - templateUrl: "app/profile/toolbar.html",
7   - providers: [
8   - provide('profileService', { useClass: ProfileService })
9   - ]
10   -})
11   -@Inject(ProfileService)
12   -export class ProfileToolbarComponent {
13   - profile: noosfero.Profile;
14   - parentId: number;
15   -
16   - constructor(profileService: ProfileService) {
17   - profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
18   - this.profile = profile;
19   - });
20   - }
21   -}
src/app/profile/profile.component.ts
... ... @@ -10,7 +10,7 @@ import {ProfileService} from &quot;../../lib/ng-noosfero-api/http/profile.service&quot;;
10 10 import {NotificationService} from "../shared/services/notification.service";
11 11 import {MyProfileComponent} from "./myprofile.component";
12 12 import {ProfileActionsComponent} from "./profile-actions.component";
13   -import {ProfileToolbarComponent} from "./profile-toolbar.component";
  13 +import {ConfigBarComponent} from "./config-bar.component";
14 14 /**
15 15 * @ngdoc controller
16 16 * @name profile.Profile
... ... @@ -44,8 +44,8 @@ import {ProfileToolbarComponent} from &quot;./profile-toolbar.component&quot;;
44 44 controllerAs: "vm"
45 45 },
46 46 "toolbar@main": {
47   - templateUrl: "app/profile/toolbar.html",
48   - controller: ProfileToolbarComponent,
  47 + templateUrl: "app/profile/configbar.html",
  48 + controller: ConfigBarComponent,
49 49 controllerAs: "vm"
50 50 }
51 51 }
... ... @@ -61,8 +61,8 @@ import {ProfileToolbarComponent} from &quot;./profile-toolbar.component&quot;;
61 61 controllerAs: "vm"
62 62 },
63 63 "toolbar@main": {
64   - templateUrl: "app/profile/toolbar.html",
65   - controller: ProfileToolbarComponent,
  64 + templateUrl: "app/profile/configbar.html",
  65 + controller: ConfigBarComponent,
66 66 controllerAs: "vm"
67 67 }
68 68 }
... ... @@ -118,8 +118,8 @@ import {ProfileToolbarComponent} from &quot;./profile-toolbar.component&quot;;
118 118 controllerAs: "vm"
119 119 },
120 120 "toolbar@main": {
121   - templateUrl: "app/profile/toolbar.html",
122   - controller: ProfileToolbarComponent,
  121 + templateUrl: "app/profile/configbar.html",
  122 + controller: ConfigBarComponent,
123 123 controllerAs: "vm"
124 124 }
125 125 }
... ...
src/app/profile/profile.html
1 1 <div class="profile-container">
2   - <custom-content class="profile-header" [label]="'profile.custom_header.label'" [attribute]="'custom_header'" [profile]="vm.profile"></custom-content>
3   - <div class="row">
4   - <noosfero-boxes ng-if="vm.boxes" [boxes]="vm.boxes" [owner]="vm.profile"></noosfero-boxes>
5   - </div>
  2 + <custom-content class="profile-header"
  3 + [label]="'profile.custom_header.label'"
  4 + [attribute]="'custom_header'"
  5 + [profile]="vm.profile">
  6 + </custom-content>
  7 + <noosfero-boxes ng-if="vm.boxes"
  8 + [layout]="vm.profile.layout_template"
  9 + [boxes]="vm.boxes"
  10 + [owner]="vm.profile" class="row">
  11 + </noosfero-boxes>
6 12 <custom-content class="profile-footer" [label]="'profile.custom_footer.label'" [attribute]="'custom_footer'" [profile]="vm.profile"></custom-content>
7 13 </div>
... ...
src/app/profile/toolbar.html
... ... @@ -1,4 +0,0 @@
1   -<div class="noosfero-main-toolbar" permission="vm.profile.permissions" permission-action="allow_edit">
2   - <noosfero-design-toggler class="pull-right"></noosfero-design-toggler>
3   -
4   -</div>
5 0 \ No newline at end of file
src/languages/en.json
... ... @@ -6,6 +6,8 @@
6 6 "navbar.logout": "Log Out",
7 7 "navbar.login": "Login",
8 8 "navbar.toggle_menu": "Toggle navigation",
  9 + "configbar.label": "Configuration",
  10 + "configbar.section.layout": "Layout",
9 11 "language.all": "All languages",
10 12 "language.en": "English",
11 13 "language.pt": "Portuguese",
... ... @@ -77,9 +79,7 @@
77 79 "custom_content.title": "Edit content",
78 80 "profile.custom_header.label": "Header",
79 81 "profile.custom_footer.label": "Footer",
80   - "designMode.label": "In Design",
81   - "designMode.toggle.ON": "ON",
82   - "designMode.toggle.OFF": "OFF",
  82 + "designMode.label": "Design mode",
83 83 "search.results.summary": "{results, plural, one{result} other{# results}}",
84 84 "search.results.query.label": "Search therm:",
85 85 "search.label": "Search",
... ... @@ -97,5 +97,8 @@
97 97 "block.edition.display_user.all": "All users",
98 98 "block.edition.display_user.logged": "Logged",
99 99 "block.edition.display_user.not_logged": "Not logged",
100   - "block.edition.language.label": "Show for:"
  100 + "block.edition.language.label": "Show for:",
  101 + "activities.event.description": "Event on",
  102 + "time.at": "at",
  103 + "date.on": "On"
101 104 }
... ...
src/languages/pt.json
... ... @@ -6,6 +6,8 @@
6 6 "navbar.logout": "Sair",
7 7 "navbar.login": "Login",
8 8 "navbar.toggle_menu": "Abrir Menu",
  9 + "configbar.label": "Configurações",
  10 + "configbar.section.layout": "Visual",
9 11 "language.all": "Todos os idiomas",
10 12 "language.en": "Inglês",
11 13 "language.pt": "Português",
... ... @@ -29,7 +31,7 @@
29 31 "navbar.content_viewer_actions.new_item": "Novo Item",
30 32 "navbar.profile_actions.new_item": "Novo Item",
31 33 "navbar.content_viewer_actions.new_post": "Novo Artigo",
32   - "navbar.content_viewer_actions.new_discussion": "Nova Discussão",
  34 + "navbar.content_viewer_actions.new_discussion": "Nova Consulta",
33 35 "navbar.profile_actions.new_discussion": "Nova Discussão",
34 36 "notification.error.default.message": "Algo deu errado!",
35 37 "notification.error.default.title": "Oops...",
... ... @@ -77,9 +79,7 @@
77 79 "custom_content.title": "Editar conteúdo",
78 80 "profile.custom_header.label": "Cabeçalho",
79 81 "profile.custom_footer.label": "Rodapé",
80   - "designMode.label": "Modo de Edição",
81   - "designMode.toggle.ON": "Ligado",
82   - "designMode.toggle.OFF": "Desligado",
  82 + "designMode.label": "Alterar design",
83 83 "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}",
84 84 "search.results.query.label": "Termo da busca:",
85 85 "search.label": "Pesquisar",
... ... @@ -97,5 +97,8 @@
97 97 "block.edition.display_user.all": "Todos os usuários",
98 98 "block.edition.display_user.logged": "Logados",
99 99 "block.edition.display_user.not_logged": "Não logados",
100   - "block.edition.language.label": "Exibir para:"
  100 + "block.edition.language.label": "Exibir para:",
  101 + "activities.event.description": "Evento em",
  102 + "time.at": "às",
  103 + "date.on": "Em"
101 104 }
... ...
src/lib/ng-noosfero-api/interfaces/environment.ts
... ... @@ -15,5 +15,14 @@ namespace noosfero {
15 15 */
16 16 id: number;
17 17 settings: any
  18 +
  19 + /**
  20 + * @ngdoc property
  21 + * @name layout_template
  22 + * @propertyOf noofero.Environment
  23 + * @returns {string} The Environment layout (e.g. default, rightbar)
  24 + */
  25 + layout_template: string;
18 26 }
19   -}
20 27 \ No newline at end of file
  28 +}
  29 +
... ...
src/lib/ng-noosfero-api/interfaces/profile.ts
... ... @@ -80,5 +80,13 @@ namespace noosfero {
80 80 custom_footer: string;
81 81  
82 82 permissions: string[];
  83 +
  84 + /**
  85 + * @ngdoc property
  86 + * @name layout_template
  87 + * @propertyOf noofero.Profile
  88 + * @returns {string} The Profile layout template (e.g.: "rightbar", "default")
  89 + */
  90 + layout_template: string;
83 91 }
84 92 }
... ...
themes/angular-participa-consulta/app/layout/scss/skins/_yellow.scss
  1 +$yellow-hover: #f5b025;
  2 +$yellow-base: #f9c404;
  3 +
1 4 .skin-yellow {
2 5 @extend %skin-base;
3 6  
... ... @@ -49,7 +52,7 @@
49 52  
50 53 .container-fluid .navbar-header .navbar-toggle {
51 54 &:hover, &:focus {
52   - background-color: #f5b025;
  55 + background-color: $yellow-hover;
53 56 }
54 57 }
55 58  
... ... @@ -61,4 +64,26 @@
61 64 }
62 65 }
63 66  
  67 + #config-tool {
  68 +
  69 + #config-tool-cog {
  70 + color: $yellow-hover;
  71 + }
  72 +
  73 + #config-tool-options {
  74 + h4 {
  75 + color: #2c3e50;
  76 + font-weight: bold;
  77 + }
  78 + }
  79 +
  80 + &.closed {
  81 + #config-tool-cog {
  82 + &:hover {
  83 + background-color: $yellow-hover;
  84 + }
  85 + }
  86 + }
  87 + }
  88 +
64 89 }
... ...