Compare View

switch
from
...
to
 
Commits (43)
Showing 81 changed files   Show diff stats
bower.json
... ... @@ -38,8 +38,9 @@
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 + "angular-password": "^1.0.1"
43 44 },
44 45 "devDependencies": {
45 46 "angular-mocks": "~1.5.0"
... ...
src/app/account/index.ts 0 → 100644
... ... @@ -0,0 +1 @@
  1 +export * from "./register.component";
... ...
src/app/account/register-component.html 0 → 100644
... ... @@ -0,0 +1,77 @@
  1 +<div class="register-page">
  2 + <div class="welcome-message">
  3 + <h1>{{"account.register.welcomeMessageTitle" | translate }}</h1>
  4 + <div class="environment-signup-intro" ng-bind-html="ctrl.environment.signup_intro"></div>
  5 + </div>
  6 + <form name="signupForm">
  7 + <div class="row">
  8 + <div class="col-md-12 register-field">
  9 + <div class="input-group">
  10 + <span class="input-group-addon"><i class="fa fa-male"></i><i class="fa fa-female"></i></span>
  11 + <input type="text" class="form-control" id="name" name="fullName" placeholder="{{'account.register.fullNameLabel' | translate }}" ng-model="ctrl.account.name">
  12 + </div>
  13 + </div>
  14 +
  15 + <div class="form-group col-md-12 register-field" ng-class="ctrl.isInvalid(signupForm.username)">
  16 + <div class="input-group">
  17 + <span class="input-group-addon" style="font-weight: bold;"><i class="fa fa-globe"></i>&nbsp;{{ ctrl.environment.host }}</span>
  18 + <input type="text" id="username" name="username" class="form-control" placeholder="{{'account.register.usernameLabel' | translate }}" ng-model="ctrl.account.login" required>
  19 + </div>
  20 + <div class="help-block" ng-show="signupForm.username.$touched" ng-messages="signupForm.username.$error">
  21 + <ul class="list-unstyled">
  22 + <li ng-messages-include="languages/messages.html"></li>
  23 + </ul>
  24 + </div>
  25 + </div>
  26 +
  27 + <div class="form-group col-md-12 register-field" ng-class="ctrl.isInvalid(signupForm.email)">
  28 + <div class="input-group">
  29 + <span class="input-group-addon"><i class="fa fa-envelope"></i></span>
  30 + <input type="email" class="form-control" id="email" name="email" placeholder="{{'account.register.emailLabel' | translate }}" ng-model="ctrl.account.email" required>
  31 + </div>
  32 + <div class="help-block" ng-show="signupForm.email.$touched" ng-messages="signupForm.email.$error">
  33 + <ul class="list-unstyled">
  34 + <li ng-messages-include="languages/messages.html"></li>
  35 + </ul>
  36 + </div>
  37 + </div>
  38 +
  39 + <div class="form-group col-md-6 register-field" ng-class="ctrl.isInvalid(signupForm.password)">
  40 + <div class="input-group">
  41 + <span class="input-group-addon"><i class="fa fa-lock"></i></span>
  42 + <input type="password" class="form-control" id="password" name="password" placeholder="{{'account.register.passwordLabel' | translate }}" ng-model="ctrl.account.password" required>
  43 + </div>
  44 + <div class="help-block" ng-show="signupForm.password.$touched" ng-messages="signupForm.password.$error">
  45 + <ul class="list-unstyled">
  46 + <li ng-messages-include="languages/messages.html"></li>
  47 + </ul>
  48 + </div>
  49 + </div>
  50 +
  51 + <div class="form-group col-md-6 register-field" ng-class="ctrl.isInvalid(signupForm.passwordConfirm)">
  52 + <div class="input-group">
  53 + <span class="input-group-addon"><i class="fa fa-unlock-alt"></i></span>
  54 + <input type="password" class="form-control" id="passwordConfirm" name="passwordConfirm" match-password="password" placeholder="{{'account.register.passwordConfirmationLabel' | translate}}" ng-model="ctrl.account.passwordConfirmation" required>
  55 + </div>
  56 + <div class="help-block" ng-show="signupForm.passwordConfirm.$touched" ng-messages="signupForm.passwordConfirm.$error">
  57 + <ul class="list-unstyled">
  58 + <li ng-messages-include="languages/messages.html"></li>
  59 + <li ng-message="passwordMatch" translate="messages.invalid.passwordMatch"></li>
  60 + </ul>
  61 + </div>
  62 + </div>
  63 +
  64 + <div class="col-md-12">
  65 + <p class="terms-info">{{"account.register.accountCreatingMessage" | translate}} <a (click)="ctrl.openTerms()" href="#">{{"account.register.termsOfUseMessage" | translate}}</a>.</p>
  66 + </div>
  67 +
  68 + <div class="col-md-12">
  69 + <button type="submit" class="btn btn-default" ng-disabled="signupForm.$invalid" ng-click="ctrl.signup()">{{"account.register.signupMessage" | translate}}</button>
  70 + </div>
  71 +
  72 + </div>
  73 + </form>
  74 +
  75 + <p class="already-registered-message">{{"account.register.haveAccountMessage" | translate}}</p>
  76 +
  77 +</div>
... ...
src/app/account/register-terms.html 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<div class="modal-header">
  2 + <h3 class="modal-title">Register terms</h3>
  3 +</div>
  4 +<div class="modal-body modal-body-overflow" ng-bind-html="ctrl.environment.terms_of_use"></div>
  5 +<div class="modal-footer">
  6 + <button class="btn btn-primary" type="button" (click)="vm.closeTerms()">OK</button>
  7 +</div>
... ...
src/app/account/register.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,52 @@
  1 +import { ComponentTestHelper, createClass } from "../../spec/component-test-helper";
  2 +import * as helpers from "../../spec/helpers";
  3 +import { RegisterComponent } from "./register.component";
  4 +// import {RegisterService} from "../../lib/ng-noosfero-api/http/register.service"
  5 +
  6 +
  7 +describe("Register Component", () => {
  8 + const htmlTemplate: string = '<noosfero-register></noosfero-register>';
  9 +
  10 + let helper: ComponentTestHelper<RegisterComponent>;
  11 + let registerService = helpers.mocks.registerService;
  12 + let stateService = jasmine.createSpyObj("$state", ["transitionTo"]);
  13 + let notificationService = helpers.mocks.notificationService;
  14 + notificationService.success = jasmine.createSpy('success');
  15 + notificationService.error = jasmine.createSpy('error');
  16 +
  17 +
  18 + let account: any = {
  19 + id: 1,
  20 + login: 'test',
  21 + email: 'test@email.com',
  22 + password: 'xxx',
  23 + passwordConfirmation: 'xxx'
  24 + };
  25 +
  26 + beforeEach(() => {
  27 + angular.mock.module('templates');
  28 + angular.mock.module('ngSanitize');
  29 + angular.mock.module('ngMessages');
  30 + angular.mock.module('ngPassword');
  31 + });
  32 +
  33 + beforeEach((done) => {
  34 + let cls = createClass({
  35 + template: htmlTemplate,
  36 + directives: [RegisterComponent],
  37 + providers: [
  38 + helpers.createProviderToValue('$state', stateService),
  39 + helpers.createProviderToValue('$uibModal', helpers.mocks.$modal),
  40 + helpers.createProviderToValue('RegisterService', registerService),
  41 + helpers.createProviderToValue('NotificationService', notificationService),
  42 + helpers.createProviderToValue('EnvironmentService', helpers.mocks.environmentService)
  43 + ]
  44 + });
  45 + helper = new ComponentTestHelper<RegisterComponent>(cls, done);
  46 + });
  47 +
  48 + it('register page was rendered', () => {
  49 + expect(helper.debugElement.query('div.register-page').length).toEqual(1);
  50 + });
  51 +
  52 +});
... ...
src/app/account/register.component.ts 0 → 100644
... ... @@ -0,0 +1,66 @@
  1 +import { Inject, Input, Component, Output, EventEmitter, provide } from 'ng-forward';
  2 +import { RegisterService } from "./../../lib/ng-noosfero-api/http/register.service";
  3 +import { NotificationService } from "./../shared/services/notification.service";
  4 +import { EnvironmentService } from "../../lib/ng-noosfero-api/http/environment.service";
  5 +import { RegisterController } from "./register.controller";
  6 +import { IModalComponent } from "../shared/components/interfaces";
  7 +
  8 +@Component({
  9 + selector: 'noosfero-register',
  10 + templateUrl: 'app/account/register-component.html',
  11 + providers: [
  12 + provide('registerService', { useClass: RegisterService })
  13 + ]
  14 +})
  15 +
  16 +@Inject('$state', '$uibModal', '$scope', RegisterService, NotificationService, EnvironmentService)
  17 +export class RegisterComponent {
  18 + @Input() account: any;
  19 + environment: noosfero.Environment;
  20 +
  21 + modalInstance: ng.ui.bootstrap.IModalServiceInstance;
  22 +
  23 + constructor(
  24 + private $state: ng.ui.IStateService,
  25 + private $uibModal: ng.ui.bootstrap.IModalService,
  26 + private $scope: ng.IScope,
  27 + public registerService: RegisterService,
  28 + private notificationService: NotificationService,
  29 + private environmentService: EnvironmentService
  30 + ) {
  31 + this.account = {};
  32 + this.environment = environmentService.getCurrentEnvironment();
  33 + }
  34 +
  35 + signup() {
  36 + if (this.account.password === this.account.passwordConfirmation) {
  37 + this.registerService.createAccount(this.account).then((response) => {
  38 +
  39 + if (response.status === 201) {
  40 + this.$state.transitionTo('main.environment');
  41 + this.notificationService.success({ title: "account.register.success.title", message: "account.register.success.message" });
  42 + } else {
  43 + throw new Error('Invalid attributes');
  44 + }
  45 + });
  46 + } else {
  47 + this.notificationService.error({ message: "account.register.passwordConfirmation.failed" });
  48 + }
  49 + }
  50 +
  51 + isInvalid(field: any): any {
  52 + return { 'has-error': field['$touched'] && field['$invalid'] };
  53 + }
  54 +
  55 + openTerms() {
  56 +
  57 + this.modalInstance = this.$uibModal.open({
  58 + templateUrl: 'app/account/register-terms.html',
  59 + size: 'lg',
  60 + controller: RegisterController,
  61 + controllerAs: 'vm',
  62 + bindToController: true,
  63 + scope: this.$scope
  64 + });
  65 + }
  66 +}
... ...
src/app/account/register.controller.ts 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +import { Input } from "ng-forward";
  2 +import { IModalComponent } from "../shared/components/interfaces";
  3 +
  4 +export class RegisterController {
  5 +
  6 + static $inject = ["$log", "$stateParams", "$scope"];
  7 + ctrl: IModalComponent;
  8 +
  9 + constructor(
  10 + private $log: ng.ILogService,
  11 + private $stateParams: any
  12 + ) { }
  13 +
  14 + closeTerms() {
  15 + this.ctrl.modalInstance.dismiss('ok');
  16 + }
  17 +}
... ...
src/app/account/register.html 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<div class="row">
  2 + <div class="col-md-3"></div>
  3 +
  4 + <!-- component at center of page -->
  5 + <div class="col-md-6">
  6 + <noosfero-register></noosfero-register>
  7 + </div>
  8 +
  9 + <div class="col-md-3"></div>
  10 +</div>
... ...
src/app/account/scss/register.scss 0 → 100644
... ... @@ -0,0 +1,44 @@
  1 +.modal .modal-body-overflow {
  2 + max-height: 420px;
  3 + overflow-y: auto;
  4 +}
  5 +
  6 +.register-page button {
  7 + width: 100%;
  8 + text-transform: uppercase;
  9 + font-weight: 600;
  10 +}
  11 +
  12 +.register-page .light-text {
  13 + color: #BBB;
  14 + margin-top: 0px;
  15 + margin-bottom: 0px;
  16 + font-weight: 600;
  17 +}
  18 +
  19 +.register-page .terms-info {
  20 + margin-top: 30px;
  21 + margin-bottom: 30px;
  22 + font-weight: bold;
  23 +}
  24 +
  25 +.register-page .welcome-message {
  26 + text-align: center;
  27 +}
  28 +
  29 +.register-page .already-registered-message {
  30 + margin-top: 60px;
  31 + text-align: center;
  32 +}
  33 +
  34 +.register-page input {
  35 + height: 40px;
  36 +}
  37 +
  38 +.register-page .register-field {
  39 + margin-bottom: 25px;
  40 +}
  41 +
  42 +.register-page input:focus {
  43 + border: 2px solid #bbb;
  44 +}
... ...
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/index.scss
... ... @@ -39,6 +39,12 @@ $break-sm-min: 992px;
39 39 $break-xxs-max: ($break-xxs-min - 1);
40 40 $break-sm-max: ($break-sm-min - 1);
41 41 $break-xs-max: ($break-xs-min - 1);
  42 +// TODO: improve this method to collect all the paddings with a single var
  43 +$wrapper-padding: 15px 15px 35px 15px;
  44 +$wrapper-padding-top: 15px;
  45 +$wrapper-padding-right: 15px;
  46 +$wrapper-padding-bottom: 35px;
  47 +$wrapper-padding-left: 15px;
42 48  
43 49  
44 50 @import "../../bower_components/bootswatch/flatly/_variables.scss";
... ...
src/app/index.ts
1   -import {bootstrap} from "ng-forward";
  1 +import {bootstrap, provide} from "ng-forward";
2 2 import {noosferoModuleConfig} from "./index.config";
3 3 import {noosferoAngularRunBlock} from "./index.run";
4 4 import {MainComponent} from "./main/main.component";
... ... @@ -22,4 +22,13 @@ angular.module(&#39;noosfero.init&#39;, [&#39;noosfero.templates.app&#39;, &#39;noosfero.templates.p
22 22 run(noosferoAngularRunBlock).
23 23 constant("moment", moment).
24 24 constant("AuthEvents", AuthEvents);
25   -bootstrap(MainComponent);
  25 +
  26 +
  27 +import { EVENTS_HUB_KNOW_EVENT_NAMES } from './shared/services/events-hub.service';
  28 +import { NoosferoKnownEvents } from './known-events';
  29 +
  30 +bootstrap(MainComponent,
  31 + [
  32 + provide(EVENTS_HUB_KNOW_EVENT_NAMES, { useClass: NoosferoKnownEvents })
  33 + ]
  34 +);
... ...
src/app/known-events.ts 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +import { EventsHubKnownEventNames } from './shared/services/events-hub.service';
  2 +
  3 +export class NoosferoKnownEvents implements EventsHubKnownEventNames {
  4 + IMAGE_PROFILE_UPDATED: string = 'IMAGE_PROFILE_UPDATED';
  5 + PROFILE_INFO_UPDATED: string = 'PROFILE_INFO_UPDATED';
  6 + ARTICLE_UPDATED: string = 'ARTICLE_UPDATED';
  7 +
  8 + constructor() {
  9 + }
  10 +
  11 + getNames() {
  12 + return Object.getOwnPropertyNames(this);
  13 + }
  14 +}
0 15 \ No newline at end of file
... ...
src/app/layout/blocks/block.component.ts
... ... @@ -18,7 +18,7 @@ export class BlockComponent {
18 18 @Input() block: noosfero.Block;
19 19 @Input() owner: noosfero.Profile | noosfero.Environment;
20 20  
21   - private modalInstance: any = null;
  21 + private modalInstance: ng.ui.bootstrap.IModalServiceInstance;
22 22 originalBlock: noosfero.Block;
23 23  
24 24 currentUser: noosfero.User;
... ... @@ -26,7 +26,8 @@ export class BlockComponent {
26 26 editionMode = false;
27 27 designMode = false;
28 28  
29   - constructor(private $uibModal: any,
  29 + constructor(
  30 + private $uibModal: ng.ui.bootstrap.IModalService,
30 31 private $scope: ng.IScope,
31 32 private $state: ng.ui.IStateService,
32 33 private $rootScope: ng.IRootScopeService,
... ...
src/app/layout/blocks/communities/communities-block.html
1   -<div class="communities-block">
2   - <a ng-repeat="profile in ctrl.profiles" ui-sref="main.profile.home({profile: profile.identifier})" class="profile">
  1 +<div ng-repeat="profile in ctrl.profiles" class="col-xs-2 col-md-4 block-item">
  2 + <a ui-sref="main.profile.home({profile: profile.identifier})">
3 3 <noosfero-profile-image [profile]="profile"></noosfero-profile-image>
4 4 </a>
  5 + <p>{{profile.name}}</p>
5 6 </div>
... ...
src/app/layout/blocks/communities/communities-block.scss
1   -.communities-block {
2   - .profile {
  1 +.communitiesblock {
  2 + .panel-body {
  3 + text-align: center;
3 4 margin: 10px;
4   - img, i.profile-image {
5   - width: 60px;
6   - }
7 5 img {
8 6 display: inline-block;
9 7 vertical-align: top;
10 8 }
11 9 i.profile-image {
12 10 text-align: center;
13   - font-size: 4.5em;
  11 + font-size: 7rem;
  12 + }
  13 + p {
  14 + overflow: hidden;
  15 + font-size: 1rem;
  16 + }
  17 + .block-item {
  18 + padding: 0px;
14 19 }
15 20 }
16 21 }
... ...
src/app/layout/blocks/container-block-plugin-container-block/container-block-plugin-container-block.component.ts 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +import { Input, Inject, Component } 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-container-block-plugin-container-block',
  7 + templateUrl: 'app/layout/blocks/container-block-plugin-container-block/container-block-plugin-container-block.html'
  8 +})
  9 +@Inject(BlockService, "$state")
  10 +export class ContainerBlockPluginContainerBlockComponent {
  11 +
  12 + @Input() block: any;
  13 + @Input() owner: any;
  14 +
  15 + profile: any;
  16 + blocks: any;
  17 +
  18 + constructor(private blockService: BlockService, private $state: any) { }
  19 +
  20 + ngOnInit() {
  21 + this.profile = this.owner;
  22 + this.blocks = [];
  23 + this.blockService.getApiContent(this.block).then((content: any) => {
  24 + this.blocks = content.blocks;
  25 + });
  26 + }
  27 +}
... ...
src/app/layout/blocks/container-block-plugin-container-block/container-block-plugin-container-block.html 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<div>
  2 + <noosfero-block ng-repeat="block in ctrl.blocks | orderBy: 'position'"
  3 + [block]="block" [owner]="ctrl.owner">
  4 + </noosfero-block>
  5 +</div>
  6 +
... ...
src/app/layout/blocks/container-block-plugin-container-block/container-block-plugin-container-block.scss 0 → 100644
src/app/layout/blocks/container-block-plugin-container-block/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./container-block-plugin-container-block.component";
... ...
src/app/layout/blocks/profile-image/profile-image-block.component.spec.ts
... ... @@ -15,11 +15,18 @@ describe(&quot;Components&quot;, () =&gt; {
15 15  
16 16 beforeEach(angular.mock.module("templates"));
17 17  
  18 + let profileService = jasmine.createSpyObj("ProfileService", ["isMember", "addMember", "removeMember"]);
  19 + profileService.isMember = jasmine.createSpy("isMember").and.returnValue(Promise.resolve(false));
  20 +
18 21 @Component({
19 22 selector: 'test-container-component',
20 23 template: htmlTemplate,
21 24 directives: [ProfileImageBlockComponent],
22   - providers: helpers.provideFilters("translateFilter")
  25 + providers: [
  26 + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})),
  27 + helpers.createProviderToValue('ProfileService', profileService),
  28 + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService)
  29 + ].concat(helpers.provideFilters("translateFilter"))
23 30 })
24 31 class BlockContainerComponent {
25 32 block = { type: 'Block' };
... ... @@ -42,5 +49,42 @@ describe(&quot;Components&quot;, () =&gt; {
42 49 });
43 50 });
44 51  
  52 + it("display button to join community", (done: Function) => {
  53 + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => {
  54 + let elProfile = fixture.debugElement.componentViewChildren[0];
  55 + expect(elProfile.query('.actions .join').length).toEqual(1);
  56 + done();
  57 + });
  58 + });
  59 +
  60 + it("display button to leave community", (done: Function) => {
  61 + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => {
  62 + let elProfile = fixture.debugElement.componentViewChildren[0];
  63 + elProfile.componentInstance['isMember'] = true;
  64 + fixture.detectChanges();
  65 + expect(elProfile.query('.actions .leave').length).toEqual(1);
  66 + done();
  67 + });
  68 + });
  69 +
  70 + it("join community", (done: Function) => {
  71 + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => {
  72 + let elProfile = fixture.debugElement.componentViewChildren[0];
  73 + profileService.addMember = jasmine.createSpy("addMember").and.returnValue(Promise.resolve({ data: {} }));
  74 + elProfile.componentInstance.join();
  75 + expect(profileService.addMember).toHaveBeenCalled();
  76 + done();
  77 + });
  78 + });
  79 +
  80 + it("leave community", (done: Function) => {
  81 + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => {
  82 + let elProfile = fixture.debugElement.componentViewChildren[0];
  83 + profileService.removeMember = jasmine.createSpy("removeMember").and.returnValue(Promise.resolve({ data: {} }));
  84 + elProfile.componentInstance.leave();
  85 + expect(profileService.removeMember).toHaveBeenCalled();
  86 + done();
  87 + });
  88 + });
45 89 });
46 90 });
... ...
src/app/layout/blocks/profile-image/profile-image-block.component.ts
1 1 import {Inject, Input, Component} from "ng-forward";
2 2 import {ProfileImageComponent} from "./../../../profile/image/image.component";
  3 +import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service";
  4 +import {SessionService} from "./../../../login";
  5 +import {NotificationService} from "../../../shared/services/notification.service";
3 6  
4 7 @Component({
5 8 selector: "noosfero-profile-image-block",
6 9 templateUrl: 'app/layout/blocks/profile-image/profile-image-block.html',
7 10 directives: [ProfileImageComponent]
8 11 })
  12 +@Inject(ProfileService, SessionService, NotificationService)
9 13 export class ProfileImageBlockComponent {
10 14  
11 15 @Input() block: noosfero.Block;
12 16 @Input() owner: noosfero.Profile;
13 17  
  18 + private isMember: boolean;
  19 +
  20 + constructor(private profileService: ProfileService, private session: SessionService, private notificationService: NotificationService) {
  21 + }
  22 +
  23 + ngOnInit() {
  24 + this.loadMembership();
  25 + }
  26 +
  27 + loadMembership() {
  28 + let person = this.session.currentUser() ? this.session.currentUser().person : null;
  29 + this.profileService.isMember(person, this.owner).then((val: boolean) => {
  30 + this.isMember = val;
  31 + });
  32 + }
  33 +
  34 + join() {
  35 + let person = this.session.currentUser() ? this.session.currentUser().person : null;
  36 + this.profileService.addMember(person, this.owner).then((result: any) => {
  37 + if (result.data.pending) {
  38 + this.notificationService.success({ title: "blocks.profile_image.join.moderation.title", message: "blocks.profile_image.join.moderation.message" });
  39 + } else {
  40 + this.loadMembership();
  41 + }
  42 + });
  43 + }
  44 +
  45 + leave() {
  46 + let person = this.session.currentUser() ? this.session.currentUser().person : null;
  47 + this.profileService.removeMember(person, this.owner).then(() => {
  48 + this.loadMembership();
  49 + });
  50 + }
14 51 }
... ...
src/app/layout/blocks/profile-image/profile-image-block.html
... ... @@ -3,4 +3,8 @@
3 3 <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image>
4 4 </a>
5 5 <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a>
  6 + <div class="actions" ng-show="ctrl.isMember!=null">
  7 + <a ng-if="!ctrl.isMember" ng-click="ctrl.join()" class="btn btn-primary btn-sm join" href="#">{{"blocks.profile_image.join" | translate}}</a>
  8 + <a ng-if="ctrl.isMember" ng-click="ctrl.leave()" class="btn btn-warning btn-sm leave" href="#">{{"blocks.profile_image.leave" | translate}}</a>
  9 + </div>
6 10 </div>
... ...
src/app/layout/blocks/profile-images-plugin-profile-images/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./profile-images-plugin-profile-images-block.component";
... ...
src/app/layout/blocks/profile-images-plugin-profile-images/profile-images-plugin-profile-images-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,65 @@
  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 {ProfileImagesPluginProfileImagesBlockComponent} from './profile-images-plugin-profile-images-block.component';
  5 +import * as helpers from "./../../../../spec/helpers";
  6 +
  7 +const htmlTemplate: string = '<noosfero-profile-images-plugin-profile-images-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-profile-images-plugin-profile-images-block>';
  8 +
  9 +const tcb = new TestComponentBuilder();
  10 +
  11 +const images = [
  12 + {
  13 + title: 'Test',
  14 + id: 1,
  15 + view_url: { host: 'localhost', page: ['image'] },
  16 + path: '/articles/0000/0001/test.png'
  17 + }
  18 +];
  19 +
  20 +describe("Components", () => {
  21 + describe("Profile Images Block Component", () => {
  22 +
  23 + let settingsObj = {};
  24 + let person = <noosfero.Person>{ name: "Person" };
  25 + let mockedService = {
  26 + getApiContent: (block: noosfero.Block): any => {
  27 + return Promise.resolve({ images: images, headers: (name: string) => { return name; } });
  28 + }
  29 + };
  30 + beforeEach(angular.mock.module("templates"));
  31 +
  32 + let state = jasmine.createSpyObj("state", ["go"]);
  33 +
  34 +
  35 + function getProviders() {
  36 + return [
  37 + new Provider('$state', { useValue: state }),
  38 + new Provider('BlockService', {
  39 + useValue: mockedService
  40 + })
  41 + ].concat(provideFilters("truncateFilter", "stripTagsFilter"));
  42 + }
  43 + let componentClass: any = null;
  44 +
  45 + function getComponent() {
  46 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ProfileImagesPluginProfileImagesBlockComponent], providers: getProviders() })
  47 + class BlockContainerComponent {
  48 + block = { type: 'Block', settings: settingsObj };
  49 + owner = person;
  50 + constructor() {
  51 + }
  52 + }
  53 + return BlockContainerComponent;
  54 + }
  55 +
  56 + it("get images from the block service", done => {
  57 + tcb.createAsync(getComponent()).then(fixture => {
  58 + let ProfileImagesPluginProfileImagesBlock: ProfileImagesPluginProfileImagesBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  59 + expect(ProfileImagesPluginProfileImagesBlock.images).toEqual(images);
  60 + done();
  61 + });
  62 + });
  63 +
  64 + });
  65 +});
... ...
src/app/layout/blocks/profile-images-plugin-profile-images/profile-images-plugin-profile-images-block.component.ts 0 → 100644
... ... @@ -0,0 +1,39 @@
  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-profile-images-plugin-profile-images-block",
  7 + templateUrl: 'app/layout/blocks/profile-images-plugin-profile-images/profile-images-plugin-profile-images-block.html'
  8 +})
  9 +@Inject(BlockService, "$state")
  10 +export class ProfileImagesPluginProfileImagesBlockComponent {
  11 +
  12 + @Input() block: any;
  13 + @Input() owner: any;
  14 +
  15 + profile: any;
  16 + images: any;
  17 +
  18 + constructor(private blockService: BlockService, private $state: any) { }
  19 +
  20 + urlFor(params: any) {
  21 + let url = '//' + params.host;
  22 + if (params.port) {
  23 + url += ':' + params.port;
  24 + }
  25 + url += '/' + params.profile + '/';
  26 + if (params.page) {
  27 + url += params.page.join('/');
  28 + }
  29 + return url;
  30 + }
  31 +
  32 + ngOnInit() {
  33 + this.profile = this.owner;
  34 + this.images = [];
  35 + this.blockService.getApiContent(this.block).then((content: any) => {
  36 + this.images = content.images;
  37 + });
  38 + }
  39 +}
... ...
src/app/layout/blocks/profile-images-plugin-profile-images/profile-images-plugin-profile-images-block.html 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<ul class="profile-images-plugin-profile-images">
  2 + <li ng-repeat="image in ctrl.images">
  3 + <a ng-href="{{ctrl.urlFor(image.view_url)}}" ng-style="{'background-image': 'url(' + image.path + ')'}">
  4 + <span>{{image.title}}</span>
  5 + </a>
  6 + </li>
  7 +</ul>
  8 +<br style="clear: both;" />
... ...
src/app/layout/blocks/profile-images-plugin-profile-images/profile-images-plugin-profile-images-block.scss 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +.profile-images-plugin-profile-images {
  2 + padding: 0;
  3 + margin: 0;
  4 + display: block;
  5 + width: 100%;
  6 +
  7 + li {
  8 + list-style: none;
  9 + }
  10 +
  11 + a {
  12 + display: block;
  13 + width: 53px;
  14 + height: 53px;
  15 + float: left;
  16 + padding: 1px;
  17 + border: 1px solid #ccc;
  18 + margin: 4px;
  19 + background-size: cover;
  20 +
  21 + &:hover {
  22 + border: 1px solid #000;
  23 + }
  24 +
  25 + span {
  26 + display: none;
  27 + }
  28 + }
  29 +}
... ...
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/recent-activities-plugin-activities-block.component.ts
... ... @@ -18,7 +18,12 @@ export class RecentActivitiesPluginActivitiesBlockComponent {
18 18 constructor(private blockService: BlockService, private $state: any) { }
19 19  
20 20 getActivityTemplate(activity: any) {
21   - return 'app/layout/blocks/recent-activities-plugin-activities/activities/' + activity.verb + '.html';
  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 + }
22 27 }
23 28  
24 29 urlFor(params: any) {
... ...
src/app/layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.html
1 1 <div class="deckgrid recent-activities-block">
2 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 +
3 14 <div class="header media-body">
4 15 <h5 class="title media-heading">
5   - <a ng-href="/{{activity.user.identifier}}">{{activity.user.name}}</a>&nbsp;<ng-include src="ctrl.getActivityTemplate(activity)"></ng-include>
  16 + <ng-include src="ctrl.getActivityTemplate(activity)"></ng-include>
6 17 </h5>
7 18 </div>
8   - <div class="subheader">
9   - <span class="time">
10   - <i class="fa fa-clock-o"></i> <span am-time-ago="activity.created_at | dateFormat"></span>
11   - </span>
12   - </div>
  19 +
  20 + <hr />
13 21 </div>
14 22 </div>
... ...
src/app/layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.scss
... ... @@ -20,4 +20,53 @@
20 20 display: none;
21 21 }
22 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 + }
23 72 }
... ...
src/app/layout/boxes/display-boxes.filter.ts
... ... @@ -11,6 +11,8 @@ export class DisplayBoxes {
11 11  
12 12 if (layout === "rightbar") {
13 13 valid_boxes = this.visible_on_right_bar();
  14 + }else if(layout === "topleft") {
  15 + valid_boxes = this.visible_on_top_right_bar();
14 16 }else {
15 17 valid_boxes = this.visible_on_default();
16 18 }
... ... @@ -31,5 +33,9 @@ export class DisplayBoxes {
31 33 return [1, 3];
32 34 }
33 35  
  36 + private visible_on_top_right_bar() {
  37 + return [1, 2, 3];
  38 + }
  39 +
34 40 }
35 41  
... ...
src/app/layout/boxes/set-box-layout.filter.ts
... ... @@ -6,6 +6,8 @@ export class SetBoxLayout {
6 6 transform(pos: number, layout: string) {
7 7 if (layout === "rightbar") {
8 8 return this.right_bar(pos);
  9 + }else if(layout === "topleft") {
  10 + return this.top_right_bar(pos);
9 11 }else {
10 12 return this.default(pos);
11 13 }
... ... @@ -29,4 +31,14 @@ export class SetBoxLayout {
29 31 }
30 32 }
31 33  
  34 + private top_right_bar(position: number) {
  35 + if (position === 1) {
  36 + return "col-sm-12 col-md-8";
  37 + }else if(position == 2) {
  38 + return "col-sm-12 col-md-4";
  39 + }else {
  40 + return "col-sm-12 col-md-12";
  41 + }
  42 + }
  43 +
32 44 }
... ...
src/app/layout/navbar/navbar.html
... ... @@ -20,6 +20,12 @@
20 20 <a ng-href="#" ng-click="ctrl.openLogin()">{{"navbar.login" | translate}}</a>
21 21 </li>
22 22  
  23 + <li ng-show="!ctrl.currentUser">
  24 + <a ui-sref="main.register">
  25 + Sign Up
  26 + </a>
  27 + </li>
  28 +
23 29 <li class="dropdown profile-menu" ng-show="ctrl.currentUser" uib-dropdown>
24 30 <a href="#" class="dropdown-toggle" aria-expanded="false" uib-dropdown-toggle>
25 31 <noosfero-profile-image [profile]="ctrl.currentUser.person" class="profile-image"></noosfero-profile-image>
... ...
src/app/layout/navbar/navbar.ts
1   -import {Component, Inject, EventEmitter, Input} from "ng-forward";
2   -import {LanguageSelectorComponent} from "../language-selector/language-selector.component";
3   -import {SessionService, AuthService, AuthController, AuthEvents} from "./../../login";
4   -import {EnvironmentService} from "./../../../lib/ng-noosfero-api/http/environment.service";
5   -import {SidebarNotificationService} from "../sidebar/sidebar.notification.service";
6   -import {BodyStateClassesService} from '../services/body-state-classes.service';
7   -import {DesignModeTogglerComponent} from './../../admin/layout-edit/designModeToggler.component';
8   -import {BootstrapSwitcherComponent, BootstrapSwitcherItem} from './../../shared/components/bootstrap-switcher/bootstrap-switcher.component';
  1 +import { Component, Inject, EventEmitter, Input } from "ng-forward";
  2 +import { LanguageSelectorComponent } from "../language-selector/language-selector.component";
  3 +import { SessionService, AuthService, AuthController, AuthEvents } from "./../../login";
  4 +import { EnvironmentService } from "./../../../lib/ng-noosfero-api/http/environment.service";
  5 +import { SidebarNotificationService } from "../sidebar/sidebar.notification.service";
  6 +import { BodyStateClassesService } from '../services/body-state-classes.service';
  7 +import { DesignModeTogglerComponent } from './../../admin/layout-edit/designModeToggler.component';
  8 +import { BootstrapSwitcherComponent, BootstrapSwitcherItem } from './../../shared/components/bootstrap-switcher/bootstrap-switcher.component';
9 9  
10 10 @Component({
11 11 selector: "acme-navbar",
... ... @@ -17,14 +17,14 @@ import {BootstrapSwitcherComponent, BootstrapSwitcherItem} from &#39;./../../shared/
17 17 export class Navbar {
18 18  
19 19 private currentUser: noosfero.User;
20   - private modalInstance: any = null;
  20 + private modalInstance: ng.ui.bootstrap.IModalServiceInstance;
21 21 public showHamburger: boolean = false;
22 22 public currentEnvironment: noosfero.Environment = <any>{ name: '' };
23 23 /**
24 24 *
25 25 */
26 26 constructor(
27   - private $uibModal: any,
  27 + private $uibModal: ng.ui.bootstrap.IModalService,
28 28 public authService: AuthService,
29 29 private session: SessionService,
30 30 private $state: ng.ui.IStateService,
... ...
src/app/layout/scss/_forms.scss
... ... @@ -2,6 +2,10 @@ label {
2 2 cursor: pointer;
3 3 }
4 4  
  5 +.has-error .help-block {
  6 + font-weight: bold;
  7 +}
  8 +
5 9 .checkbox-nice {
6 10 position: relative;
7 11 padding-left: 15px;
... ...
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
... ... @@ -122,7 +122,7 @@ $whbl-font-color: #16191c;
122 122 margin-bottom: 0;
123 123 position: relative;
124 124 min-height: 1200px;
125   - padding: 15px 15px 35px 15px;
  125 + padding: $wrapper-padding;
126 126 margin-left: 220px;
127 127 // border-left: 2px solid #e7ebee;
128 128 }
... ... @@ -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/login/login.html
... ... @@ -21,4 +21,7 @@
21 21 </div>
22 22 <button type="submit" class="btn btn-default btn-block" ng-click="vm.login()">{{"auth.form.login_button" | translate}}</button>
23 23 </form>
  24 + <div class="text-center">
  25 + <a ui-sref="main.register" ng-click="$close()">{{"auth.createAccount" | translate}}</a>
  26 + </div>
24 27 </div>
... ...
src/app/login/session.service.ts
... ... @@ -23,4 +23,4 @@ export class SessionService {
23 23 return this.$localStorage.currentUser;
24 24 };
25 25  
26   -}
27 26 \ No newline at end of file
  27 +}
... ...
src/app/main/main.component.spec.ts
... ... @@ -4,8 +4,9 @@ import {TestComponentBuilder, ComponentFixture} from &quot;ng-forward/cjs/testing/tes
4 4  
5 5 import {quickCreateComponent} from "../../spec/helpers";
6 6 import {getAngularServiceFactory} from "../../spec/helpers";
  7 +import { EVENTS_HUB_KNOW_EVENT_NAMES } from "../shared/services/events-hub.service";
7 8  
8   -describe("MainComponent", function() {
  9 +describe("MainComponent", function () {
9 10  
10 11 let localFixture: ComponentFixture;
11 12 let $state: angular.ui.IStateService;
... ... @@ -34,6 +35,14 @@ describe(&quot;MainComponent&quot;, function() {
34 35 {
35 36 useValue: environmentService
36 37 }),
  38 + provide(EVENTS_HUB_KNOW_EVENT_NAMES,
  39 + {
  40 + useValue: [
  41 + 'IMAGE_PROFILE_UPDATED',
  42 + 'PROFILE_INFO_UPDATED',
  43 + 'ARTICLE_UPDATED'
  44 + ]
  45 + }),
37 46 ]
38 47 })
39 48 class MainComponentParent {
... ... @@ -57,7 +66,7 @@ describe(&quot;MainComponent&quot;, function() {
57 66 // navigates to the environment home
58 67 $state.go("main.environment.home");
59 68 localFixture.detectChanges();
60   - // after changes were detected it checks the current $state route
  69 + // after changes were detected it checks the current $state route
61 70 expect($state.current.name).toEqual("main.environment.home");
62 71 done();
63 72 });
... ...
src/app/main/main.component.ts
1 1 import * as plugins from "../../plugins";
2   -import {bundle, Component, StateConfig, Inject} from "ng-forward";
3   -import {ArticleBlogComponent} from "./../article/types/blog/blog.component";
4   -
5   -import {ArticleViewComponent} from "./../article/article-default-view.component";
6   -
7   -import {ProfileComponent} from "../profile/profile.component";
8   -import {BoxesComponent} from "../layout/boxes/boxes.component";
9   -import {BlockContentComponent} from "../layout/blocks/block-content.component";
10   -import {BlockComponent} from "../layout/blocks/block.component";
11   -import {EnvironmentComponent} from "../environment/environment.component";
12   -import {EnvironmentHomeComponent} from "../environment/environment-home.component";
13   -import {PeopleBlockComponent} from "../layout/blocks/people/people-block.component";
14   -import {DisplayContentBlockComponent} from "../layout/blocks/display-content/display-content-block.component";
15   -import {LinkListBlockComponent} from "../layout/blocks/link-list/link-list-block.component";
16   -import {RecentDocumentsBlockComponent} from "../layout/blocks/recent-documents/recent-documents-block.component";
17   -import {ProfileImageBlockComponent} from "../layout/blocks/profile-image/profile-image-block.component";
18   -import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html-block.component";
19   -import {StatisticsBlockComponent} from "../layout/blocks/statistics/statistics-block.component";
20   -import {PersonTagsPluginInterestsBlockComponent} from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component";
21   -import {TagsBlockComponent} from "../layout/blocks/tags/tags-block.component";
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";
24   -
25   -import {MembersBlockComponent} from "../layout/blocks/members/members-block.component";
26   -import {CommunitiesBlockComponent} from "../layout/blocks/communities/communities-block.component";
27   -
28   -import {LoginBlockComponent} from "../layout/blocks/login-block/login-block.component";
29   -
30   -import {NoosferoTemplate} from "../shared/pipes/noosfero-template.filter";
31   -import {DateFormat} from "../shared/pipes/date-format.filter";
32   -
33   -import {AuthService} from "../login/auth.service";
34   -import {SessionService} from "../login/session.service";
35   -import {EnvironmentService} from "./../../lib/ng-noosfero-api/http/environment.service";
36   -import {NotificationService} from "../shared/services/notification.service";
37   -
38   -import {BodyStateClassesService} from "./../layout/services/body-state-classes.service";
39   -
40   -import {Navbar} from "../layout/navbar/navbar";
41   -
42   -import {SidebarComponent} from "../layout/sidebar/sidebar.component";
43   -
44   -import {MainBlockComponent} from "../layout/blocks/main/main-block.component";
45   -import {HtmlEditorComponent} from "../shared/components/html-editor/html-editor.component";
46   -import {PermissionDirective} from "../shared/components/permission/permission.directive";
47   -import {SearchComponent} from "../search/search.component";
48   -import {SearchFormComponent} from "../search/search-form/search-form.component";
49   -
  2 +import { bundle, Component, StateConfig, Inject } from "ng-forward";
  3 +import { ArticleBlogComponent } from "./../article/types/blog/blog.component";
  4 +
  5 +import { ArticleViewComponent } from "./../article/article-default-view.component";
  6 +
  7 +import { ProfileComponent } from "../profile/profile.component";
  8 +import { BoxesComponent } from "../layout/boxes/boxes.component";
  9 +import { BlockContentComponent } from "../layout/blocks/block-content.component";
  10 +import { BlockComponent } from "../layout/blocks/block.component";
  11 +import { EnvironmentComponent } from "../environment/environment.component";
  12 +import { EnvironmentHomeComponent } from "../environment/environment-home.component";
  13 +import { PeopleBlockComponent } from "../layout/blocks/people/people-block.component";
  14 +import { DisplayContentBlockComponent } from "../layout/blocks/display-content/display-content-block.component";
  15 +import { LinkListBlockComponent } from "../layout/blocks/link-list/link-list-block.component";
  16 +import { RecentDocumentsBlockComponent } from "../layout/blocks/recent-documents/recent-documents-block.component";
  17 +import { ProfileImageBlockComponent } from "../layout/blocks/profile-image/profile-image-block.component";
  18 +import { RawHTMLBlockComponent } from "../layout/blocks/raw-html/raw-html-block.component";
  19 +import { StatisticsBlockComponent } from "../layout/blocks/statistics/statistics-block.component";
  20 +import { PersonTagsPluginInterestsBlockComponent } from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component";
  21 +import { TagsBlockComponent } from "../layout/blocks/tags/tags-block.component";
  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";
  24 +import { ContainerBlockPluginContainerBlockComponent } from "../layout/blocks/container-block-plugin-container-block/container-block-plugin-container-block.component";
  25 +import { ProfileImagesPluginProfileImagesBlockComponent } from "../layout/blocks/profile-images-plugin-profile-images/profile-images-plugin-profile-images-block.component";
  26 +import { RegisterComponent } from "../account/register.component";
  27 +
  28 +import { MembersBlockComponent } from "../layout/blocks/members/members-block.component";
  29 +import { CommunitiesBlockComponent } from "../layout/blocks/communities/communities-block.component";
  30 +
  31 +import { LoginBlockComponent } from "../layout/blocks/login-block/login-block.component";
  32 +
  33 +import { NoosferoTemplate } from "../shared/pipes/noosfero-template.filter";
  34 +import { DateFormat } from "../shared/pipes/date-format.filter";
  35 +
  36 +import { AuthService } from "../login/auth.service";
  37 +import { SessionService } from "../login/session.service";
  38 +import { EnvironmentService } from "./../../lib/ng-noosfero-api/http/environment.service";
  39 +import { NotificationService } from "../shared/services/notification.service";
  40 +import { RegisterService } from "./../../lib/ng-noosfero-api/http/register.service";
  41 +
  42 +import { BodyStateClassesService } from "./../layout/services/body-state-classes.service";
  43 +
  44 +import { Navbar } from "../layout/navbar/navbar";
  45 +
  46 +import { SidebarComponent } from "../layout/sidebar/sidebar.component";
  47 +
  48 +import { MainBlockComponent } from "../layout/blocks/main/main-block.component";
  49 +import { HtmlEditorComponent } from "../shared/components/html-editor/html-editor.component";
  50 +import { PermissionDirective } from "../shared/components/permission/permission.directive";
  51 +import { SearchComponent } from "../search/search.component";
  52 +import { SearchFormComponent } from "../search/search-form/search-form.component";
  53 +
  54 +import { EVENTS_HUB_KNOW_EVENT_NAMES, EventsHubService } from "../shared/services/events-hub.service";
  55 +import { NoosferoKnownEvents } from "../known-events";
50 56 /**
51 57 * @ngdoc controller
52 58 * @name main.MainContentComponent
... ... @@ -62,12 +68,16 @@ import {SearchFormComponent} from &quot;../search/search-form/search-form.component&quot;;
62 68 templateUrl: "app/main/main.html",
63 69 providers: [AuthService, SessionService]
64 70 })
65   -@Inject(BodyStateClassesService)
  71 +@Inject(BodyStateClassesService, EVENTS_HUB_KNOW_EVENT_NAMES)
66 72 export class MainContentComponent {
67 73  
68 74 public themeSkin: string = 'skin-whbl';
69 75  
70   - constructor(private bodyStateClassesService: BodyStateClassesService) {
  76 + constructor(
  77 + private bodyStateClassesService: BodyStateClassesService,
  78 + eventsNames: NoosferoKnownEvents,
  79 + eventsHubService: EventsHubService
  80 + ) {
71 81 bodyStateClassesService.start({
72 82 skin: this.themeSkin
73 83 });
... ... @@ -88,7 +98,8 @@ export class EnvironmentContent {
88 98 * @name main.Main
89 99 * @requires AuthService, Session, Notification, ArticleBlog, ArticleView, Boxes, Block, LinkListBlock,
90 100 * MainBlock, RecentDocumentsBlock, Navbar, ProfileImageBlock, MembersBlock,
91   - * NoosferoTemplate, DateFormat, RawHTMLBlock, PersonTagsPluginInterestsBlock, RecentActivitiesPluginActivitiesBlock,
  101 + * NoosferoTemplate, DateFormat, RawHTMLBlock, PersonTagsPluginInterestsBlock,
  102 + * RecentActivitiesPluginActivitiesBlock, ContainerBlockPluginContainerBlockComponent, ProfileImagesPluginProfileImages
92 103 * @description
93 104 * The Main controller for the Noosfero Angular Theme application.
94 105 *
... ... @@ -107,16 +118,18 @@ export class EnvironmentContent {
107 118 MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent,
108 119 MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent,
109 120 LoginBlockComponent, CustomContentComponent, PermissionDirective, SearchFormComponent, SearchComponent,
110   - PersonTagsPluginInterestsBlockComponent, TagsBlockComponent, RecentActivitiesPluginActivitiesBlockComponent, BlockComponent
  121 + PersonTagsPluginInterestsBlockComponent, TagsBlockComponent, RecentActivitiesPluginActivitiesBlockComponent,
  122 + ContainerBlockPluginContainerBlockComponent,
  123 + ProfileImagesPluginProfileImagesBlockComponent, BlockComponent, RegisterComponent
111 124 ].concat(plugins.mainComponents).concat(plugins.hotspots),
112   - providers: [AuthService, SessionService, NotificationService, BodyStateClassesService,
  125 + providers: [AuthService, SessionService, NotificationService, BodyStateClassesService, RegisterService,
113 126 "ngAnimate", "ngCookies", "ngStorage", "ngTouch",
114 127 "ngSanitize", "ngMessages", "ngAria", "restangular",
115 128 "ui.router", "ui.bootstrap", "toastr", "ngCkeditor",
116 129 "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid",
117 130 "angular-timeline", "duScroll", "oitozero.ngSweetAlert",
118 131 "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad",
119   - "angular-click-outside", "toggle-switch", "ngTagCloud", "noosfero.init"]
  132 + "angular-click-outside", "ngTagCloud", "noosfero.init", "uiSwitch", "ngPassword"]
120 133 })
121 134 @StateConfig([
122 135 {
... ... @@ -137,7 +150,6 @@ export class EnvironmentContent {
137 150 url: '/',
138 151 component: EnvironmentComponent,
139 152 name: 'main.environment',
140   - abstract: true,
141 153 views: {
142 154 "content": {
143 155 templateUrl: "app/environment/environment.html",
... ... @@ -147,6 +159,18 @@ export class EnvironmentContent {
147 159 }
148 160 },
149 161 {
  162 + url: '/account/signup',
  163 + component: RegisterComponent,
  164 + name: 'main.register',
  165 + views: {
  166 + "content": {
  167 + templateUrl: "app/account/register.html",
  168 + controller: RegisterComponent,
  169 + controllerAs: "vm"
  170 + }
  171 + }
  172 + },
  173 + {
150 174 url: "^/:profile",
151 175 abstract: true,
152 176 component: ProfileComponent,
... ...
src/app/profile/activities/activities.html
1   -<timeline>
2   - <timeline-event ng-repeat="activity in ctrl.activities | orderBy: 'created_at':true">
3   - <noosfero-activity [activity]="activity"></noosfero-activity>
4   - </timeline-event>
5   -</timeline>
  1 +<div class="row">
  2 + <hr class="divider"/>
  3 + <div id="atividades">
  4 + <h4>Atividades Recentes</h4>
  5 + <timeline>
  6 + <timeline-event ng-repeat="activity in vm.activities | orderBy: 'created_at':true">
  7 + <hr class="divider"/>
  8 + <noosfero-activity [activity]="activity"></noosfero-activity>
  9 + </timeline-event>
  10 + </timeline>
  11 + <button type="button" class="btn btn-sm btn-block">Carregar mais</button>
  12 + </div>
  13 +</div>
... ...
src/app/profile/activities/activity/activity.component.spec.ts
... ... @@ -12,6 +12,7 @@ const htmlTemplate: string = &#39;&lt;noosfero-activity [activity]=&quot;ctrl.activity&quot;&gt;&lt;/no
12 12 describe("Components", () => {
13 13  
14 14 describe("Noosfero Activity", () => {
  15 + let activity = { name: "activity1", verb: "create_article" };
15 16  
16 17 beforeEach(angular.mock.module("templates"));
17 18  
... ... @@ -21,18 +22,54 @@ describe(&quot;Components&quot;, () =&gt; {
21 22 directives: [ActivityComponent],
22 23 providers: provideFilters("truncateFilter", "stripTagsFilter", "translateFilter")
23 24 })
  25 +
24 26 class BlockContainerComponent {
25   - activity = { name: "activity1", verb: "create_article" };
  27 + activity = activity;
26 28 }
27 29  
28 30 it("render the specific template for an activity verb", done => {
29 31 tcb.createAsync(BlockContainerComponent).then(fixture => {
30 32 let component: ActivityComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
31 33 expect(component.getActivityTemplate()).toEqual('app/profile/activities/activity/create_article.html');
  34 + done();
  35 + });
  36 + });
  37 +
  38 + it("render create article template correctly", done => {
  39 + activity = { name: "activity1", verb: "create_article" };
  40 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  41 + let component: ActivityComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
32 42 expect(fixture.debugElement.queryAll(".activity.create_article").length).toEqual(1);
33 43 done();
34 44 });
35 45 });
  46 +
  47 + it("render add_member_in_community template correctly", done => {
  48 + activity = { name: "add_member_in_community1", verb: "add_member_in_community" };
  49 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  50 + let component: ActivityComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  51 + expect(fixture.debugElement.queryAll(".activity.add_member_in_community").length).toEqual(1);
  52 + done();
  53 + });
  54 + });
  55 +
  56 + it("render new_friendship template correctly", done => {
  57 + activity = { name: "new_friendship1", verb: "new_friendship" };
  58 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  59 + let component: ActivityComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  60 + expect(fixture.debugElement.queryAll(".activity.new_friendship").length).toEqual(1);
  61 + done();
  62 + });
  63 + });
  64 +
  65 + it("render leave scrap template correctly", done => {
  66 + activity = { name: "scrap1", verb: "leave_scrap" };
  67 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  68 + let component: ActivityComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  69 + expect(fixture.debugElement.queryAll(".activity.leave_scrap").length).toEqual(1);
  70 + done();
  71 + });
  72 + });
36 73 });
37 74  
38 75 });
... ...
src/app/profile/activities/activity/leave_scrap.html 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +<timeline-badge class="success">
  2 + <i class="fa fa-commenting-o"></i>
  3 +</timeline-badge>
  4 +<timeline-panel>
  5 + <timeline-heading>
  6 + <h4 class="timeline-title">
  7 + <a ui-sref="main.profile.info({profile: ctrl.activity.user.identifier})"><strong ng-bind="ctrl.activity.user.name"></strong></a>
  8 + <span> {{"activities.scrap.description" | translate}} </span>
  9 + </h4>
  10 + <p><small class="text-muted"><i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.activity.created_at | dateFormat"></span></small></p>
  11 + </timeline-heading>
  12 + <div class="timeline-body">
  13 + <div class="scrap">
  14 + <div ng-bind-html="ctrl.activity.content | stripTags | truncate: 100 : '...': true"></div>
  15 + </div>
  16 + </div>
  17 +</timeline-panel>
... ...
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/custom-content/custom-content.component.ts
1   -import {Component, Input, Inject} from 'ng-forward';
2   -import {ProfileService} from '../../../lib/ng-noosfero-api/http/profile.service';
3   -import {NotificationService} from '../../shared/services/notification.service';
4   -import {PermissionDirective} from '../../shared/components/permission/permission.directive';
5   -import {DesignModeService} from '../../admin/layout-edit/designMode.service';
  1 +import { Component, Input, Inject } from 'ng-forward';
  2 +import { ProfileService } from '../../../lib/ng-noosfero-api/http/profile.service';
  3 +import { NotificationService } from '../../shared/services/notification.service';
  4 +import { PermissionDirective } from '../../shared/components/permission/permission.directive';
  5 +import { DesignModeService } from '../../admin/layout-edit/designMode.service';
6 6  
7 7 @Component({
8 8 selector: 'custom-content',
... ... @@ -21,9 +21,10 @@ export class CustomContentComponent {
21 21  
22 22 content: string;
23 23 originalContent: string;
24   - private modalInstance: any = null;
  24 + private modalInstance: ng.ui.bootstrap.IModalServiceInstance;
25 25  
26   - constructor(private $uibModal: any,
  26 + constructor(
  27 + private $uibModal: ng.ui.bootstrap.IModalService,
27 28 private $scope: ng.IScope,
28 29 private profileService: ProfileService,
29 30 private notificationService: NotificationService,
... ...
src/app/profile/image/image.html
1 1 <span class="profile-image-wrap" title="{{ctrl.profile.name}}">
2   - <img ng-if="ctrl.profile.image" ng-src="{{ctrl.profile.image.url}}" class="img-responsive profile-image">
3   - <i ng-if="!ctrl.profile.image" class="fa {{ctrl.defaultIcon}} fa-5x profile-image"></i>
  2 + <img ng-if="ctrl.profile.image" ng-src="{{ctrl.profile.image.url}}" class="">
  3 + <i ng-if="!ctrl.profile.image" class="fa {{ctrl.defaultIcon}} fa-5x profile-image img-avatar"></i>
4 4 </span>
... ...
src/app/profile/info/profile-info.component.ts
... ... @@ -16,17 +16,23 @@ export class ProfileInfoComponent {
16 16  
17 17 activities: any;
18 18 profile: noosfero.Profile;
  19 + showInformation: boolean = false;
19 20  
20 21 constructor(private profileService: ProfileService, private amDateFormatFilter: any) {
21 22 this.init();
  23 + this.showInformation = false;
  24 + }
  25 +
  26 + toggleInformation() {
  27 + console.log(this.showInformation);
  28 + console.log("argila");
  29 + this.showInformation = !this.showInformation;
  30 + console.log(this.showInformation);
22 31 }
23 32  
24 33 init() {
25 34 this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
26 35 this.profile = profile;
27   - return this.profileService.getActivities(<number>this.profile.id);
28   - }).then((response: restangular.IResponse) => {
29   - this.activities = response.data.activities;
30 36 });
31 37 }
32 38 }
... ...
src/app/profile/info/profile-info.html
1   -<div class="profile-wall">
  1 +<!-- <div class="profile-wall mainbox clearfix">
2 2  
3 3 <div class="col-lg-3 col-md-4 col-sm-4">
4 4 <div class="main-box clearfix">
... ... @@ -8,6 +8,7 @@
8 8 <div id="profile-left" class="main-box-body clearfix">
9 9 <noosfero-profile-image [profile]="vm.profile" class="img-responsive center-block"></noosfero-profile-image>
10 10 <span class="label" ng-class="{'label-danger': vm.profile.type == 'Community', 'label-info': vm.profile.type == 'Person'}">{{vm.profile | translateProfile}}</span>
  11 +
11 12 <div class="profile-since">
12 13 {{"profile.member_since" | translate}}: {{vm.profile.created_at | amDateFormat:'MMMM YYYY'}}
13 14 </div>
... ... @@ -27,4 +28,38 @@
27 28 </uib-tabset>
28 29 </div>
29 30 </div>
  31 +</div> -->
  32 +
  33 +<div class="bg-image"></div>
  34 +
  35 +<div class="container" id="noosfero-profile">
  36 + <div class="col-sm-2">
  37 + <noosfero-profile-image [profile]="vm.profile" class="img-responsive center-block" data-pin-nopin="true"></noosfero-profile-image>
  38 + <div class="social-links">
  39 + <ul>
  40 + <li>Facebook</li>
  41 + <li>Linkdin</li>
  42 + </ul>
  43 + </div>
  44 + </div>
  45 + <div class="col-sm-10">
  46 + <h3>{{ vm.profile.name }}</h3>
  47 + <div ng-switch="description">
  48 + <p ng-if="vm.profile.additional_data['description']">
  49 + {{ 'profile.person.description' | translate }}: {{ vm.profile.additional_data['description'] }}
  50 + </p>
  51 + <p class="pull-left">www.noosfero.gov.br/<strong>{{ vm.profile.identifier }}</strong></p>
  52 + <div class="pull-right info-contato">
  53 + <button ng-click="vm.toggleInformation()" class="btn btn-sm">{{ 'profile.person.contact_info' | translate }}</button>
  54 + <div clas="profile-contact" ng-show="vm.showInformation">
  55 + <p class="email" ng-if="vm.profile.additional_data['contact_email']">Email: {{ vm.profile.additional_data['contact_email'] }}</p>
  56 + <p class="phone" ng-if="vm.profile.additional_data['contact_phone']">
  57 + {{ 'profile.person.contact_phone' | translate }}: {{ vm.profile.additional_data['contact_phone'] }}
  58 + </p>
  59 + <p class="phone" ng-if="(!vm.profile.additional_data['contact_phone'] && !vm.profile.additional_data['contact_email'])">
  60 + {{ 'profile.person.no_contact_info' | translate }}
  61 + </p>
  62 + </div>
  63 + </div>
  64 + </div>
30 65 </div>
... ...
src/app/profile/info/profile-info.scss 0 → 100644
src/app/profile/profile.component.ts
... ... @@ -6,22 +6,22 @@ import {CmsComponent} from &#39;../article/cms/cms.component&#39;;
6 6 import {ContentViewerComponent} from "../article/content-viewer/content-viewer.component";
7 7 import {ContentViewerActionsComponent} from "../article/content-viewer/content-viewer-actions.component";
8 8 import {ActivitiesComponent} from "./activities/activities.component";
  9 +import {ActivityComponent} from "./activities/activity/activity.component";
9 10 import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service";
10 11 import {NotificationService} from "../shared/services/notification.service";
11 12 import {MyProfileComponent} from "./myprofile.component";
12 13 import {ProfileActionsComponent} from "./profile-actions.component";
13   -import {ProfileToolbarComponent} from "./profile-toolbar.component";
  14 +import {ConfigBarComponent} from "./config-bar.component";
14 15 /**
15 16 * @ngdoc controller
16   - * @name profile.Profile
17   - * @description
  17 + * @name profile.Profile * @description
18 18 * This is the profile controller. It provide routes to supported Noosfero Profiles.
19 19 */
20 20  
21 21 @Component({
22 22 selector: 'profile',
23 23 templateUrl: "app/profile/profile.html",
24   - directives: [ActivitiesComponent],
  24 + directives: [ActivityComponent],
25 25 providers: [
26 26 provide('profileService', { useClass: ProfileService }),
27 27 provide('notificationService', { useClass: NotificationService })
... ... @@ -33,19 +33,19 @@ import {ProfileToolbarComponent} from &quot;./profile-toolbar.component&quot;;
33 33 url: "^/profile/:profile",
34 34 component: ProfileInfoComponent,
35 35 views: {
36   - "mainBlockContent": {
37   - templateUrl: "app/profile/info/profile-info.html",
38   - controller: ProfileInfoComponent,
39   - controllerAs: "vm"
40   - },
41 36 "actions@main": {
42 37 templateUrl: "app/profile/navbar-actions.html",
43 38 controller: ProfileActionsComponent,
44 39 controllerAs: "vm"
45 40 },
46 41 "toolbar@main": {
47   - templateUrl: "app/profile/toolbar.html",
48   - controller: ProfileToolbarComponent,
  42 + templateUrl: "app/profile/configbar.html",
  43 + controller: ConfigBarComponent,
  44 + controllerAs: "vm"
  45 + },
  46 + "mainBlockContent": {
  47 + templateUrl: "app/profile/activities/activities.html",
  48 + controller: ProfileInfoComponent,
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 }
... ... @@ -142,4 +142,6 @@ export class ProfileComponent {
142 142 notificationService.error({ message: "notification.profile.not_found" });
143 143 });
144 144 }
  145 +
  146 +
145 147 }
... ...
src/app/profile/profile.html
... ... @@ -4,10 +4,14 @@
4 4 [attribute]="'custom_header'"
5 5 [profile]="vm.profile">
6 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>
  7 + <div class="row" ui-view="profile-info"></div>
  8 +
  9 + <div class="wrapper-container">
  10 + <noosfero-boxes ng-if="vm.boxes"
  11 + [layout]="vm.profile.layout_template"
  12 + [boxes]="vm.boxes"
  13 + [owner]="vm.profile" class="row">
  14 + </noosfero-boxes>
  15 + </div>
12 16 <custom-content class="profile-footer" [label]="'profile.custom_footer.label'" [attribute]="'custom_footer'" [profile]="vm.profile"></custom-content>
13 17 </div>
... ...
src/app/profile/profile.scss
  1 +
  2 +@mixin padding-sides {
  3 + padding-right: 15px;
  4 + padding-left: 15px;
  5 +}
  6 +
  7 +@mixin profile-img {
  8 + text-align: center;
  9 + margin: 10px;
  10 + border: 1px solid #eee;
  11 +}
  12 +
  13 +@media (min-width: 768px) {
  14 + .container {max-width: 980px;}
  15 +}
  16 +
1 17 .profile-container {
2 18 @extend .container-fluid;
3   - padding: 0 1%;
4   - @media (max-width: 978px) {
5   - padding: 0 2%;
  19 + margin-top: -$wrapper-padding-top;
  20 + margin-left: -$wrapper-padding-left;
  21 + margin-bottom: -$wrapper-padding-bottom;
  22 + margin-right: -$wrapper-padding-right;
  23 + @media (max-width: 978px) {padding: 0 2%;}
  24 +}
  25 +
  26 +.wrapper-container {
  27 + margin: auto;
  28 + max-width: 980px;
  29 +}
  30 +
  31 +.dropdown-menu {
  32 + >li {
  33 + margin:5px 0;
  34 + >a {
  35 + padding:3px 10px 3px 0 !important;
  36 + &:hover {background-color:transparent !important;}
  37 + }
6 38 }
7 39 }
8 40  
9   -#profile-left {
10   - text-align: center;
11   - margin-top: 15px;
  41 +.bg-image {
  42 + background-color:#eee;
  43 + height: 260px;
  44 +}
  45 +
  46 +.social-links {
  47 + ul {
  48 + list-style:none;
  49 + text-align:center;
  50 + padding:0;
  51 + margin:0;
  52 + }
  53 +}
  54 +
  55 +#noosfero-profile {
  56 + background:#fff;
  57 + margin: -200px auto auto;
  58 + z-index:9999;
  59 + left:0;
  60 + right:0;
  61 + border:1px solid #eee;
  62 + padding:20px 0;
  63 + font-size: 14px;
  64 +
  65 + img,
  66 + i {@include profile-img;}
  67 +
  68 + .col-sm-2 {text-align: center;}
  69 +
  70 + h3 {margin-top: 16px;}
  71 +
  72 + .btn {
  73 + font-size: 12px;
  74 + padding: 5px 10px;
  75 + }
  76 +
  77 + .profile-contact {
  78 + padding-top: 10px;
  79 + p {margin: 0px;}
  80 + }
12 81 }
... ...
src/app/shared/components/interfaces.ts
1   -
2   -
3 1 export interface IComponentWithPermissions {
4 2 permissions: () => string[];
5   -}
6 3 \ No newline at end of file
  4 +}
  5 +
  6 +export interface IModalComponent {
  7 + $uibModal: ng.ui.bootstrap.IModalService;
  8 + modalInstance: ng.ui.bootstrap.IModalServiceInstance;
  9 +}
... ...
src/app/shared/services/events-hub.service.spec.ts 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +import { OpaqueToken } from 'ng-forward';
  2 +import { EventsHubService, EventsHubKnownEventNames } from './events-hub.service';
  3 +
  4 +
  5 +describe("EventsHubService", () => {
  6 + let eventsHubService: EventsHubService;
  7 + let event1 = 'Event 1';
  8 + let eventsHubKnownEventNames = <EventsHubKnownEventNames>{ getNames: () => { return [ event1]; }};
  9 + it("emits events for the known events", (done) => {
  10 +
  11 + let eventListener = () => {
  12 + };
  13 + // creates the events hub service which known the event "Event1"
  14 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  15 + // subscribe to the event passing the done Function as the eventListener
  16 + // if the event emits works the done function is called and the
  17 + // test will pass
  18 + eventsHubService.subscribeToEvent<any>(event1, done);
  19 + // emits the event
  20 + eventsHubService.emitEvent(event1, null);
  21 + });
  22 +
  23 + it("throws error when trying to emit an unknow event", () => {
  24 + let eventListener = () => {
  25 + };
  26 + // creates the events hub service which known the event "Event1"
  27 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  28 +
  29 + // emits the event
  30 + expect(
  31 + () => { eventsHubService.emitEvent('NotKnownEvent', null); }
  32 + ).toThrowError('Unknown event named NotKnownEvent');
  33 + });
  34 +
  35 + it("throws error when trying to subscribe to an unknow event", () => {
  36 + let eventListener = () => {
  37 + };
  38 + // creates the events hub service which known the event "Event1"
  39 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  40 +
  41 + // emits the event
  42 + expect(
  43 + () => { eventsHubService.subscribeToEvent<void>('NotKnownEvent', () => {}); }
  44 + ).toThrowError('Unknown event named NotKnownEvent');
  45 + });
  46 +});
0 47 \ No newline at end of file
... ...
src/app/shared/services/events-hub.service.ts 0 → 100644
... ... @@ -0,0 +1,56 @@
  1 +import { Injectable, Inject, OpaqueToken, EventEmitter } from 'ng-forward';
  2 +
  3 +export const EVENTS_HUB_KNOW_EVENT_NAMES = new OpaqueToken('EVENTS_HUB_KNOW_EVENT_NAMES');
  4 +
  5 +export interface EventsHubKnownEventNames {
  6 + getNames(): string[];
  7 +}
  8 +
  9 +function isEventsHubKnownEventNames(object: any): object is EventsHubKnownEventNames {
  10 + return 'getNames' in object;
  11 +}
  12 +
  13 +@Injectable()
  14 +@Inject(EVENTS_HUB_KNOW_EVENT_NAMES)
  15 +export class EventsHubService {
  16 +
  17 + private emitters: Map<string, EventEmitter<any>>;
  18 + private knownEvents: string[] = [];
  19 +
  20 + constructor(private eventsHubKnownEventNames: EventsHubKnownEventNames | string[]) {
  21 + if (isEventsHubKnownEventNames(eventsHubKnownEventNames)) {
  22 + this.knownEvents = eventsHubKnownEventNames.getNames();
  23 + } else if (Array.isArray(eventsHubKnownEventNames)) {
  24 + this.knownEvents = eventsHubKnownEventNames;
  25 + }
  26 +
  27 + this.emitters = new Map<string, EventEmitter<any>>();
  28 + this.setupEmitters();
  29 + }
  30 +
  31 + emitEvent(eventType: string, payload?: any) {
  32 + this.checkKnownEvent(eventType);
  33 + let event = this.emitters.get(eventType);
  34 + if ( event ) this.emitters.get(eventType).next(payload);
  35 + }
  36 +
  37 + subscribeToEvent<T>(eventType: string, generatorOrNext?: ((p?: T) => void), error?: any, complete?: any) {
  38 + this.checkKnownEvent(eventType);
  39 + let event = this.emitters.get(eventType);
  40 + if (event) event.subscribe(generatorOrNext, error, complete);
  41 + }
  42 +
  43 + private setupEmitters() {
  44 + for (let i: number = 0; i < this.knownEvents.length; i++) {
  45 + this.emitters.set(this.knownEvents[i], new EventEmitter<any>());
  46 + }
  47 + }
  48 +
  49 + private checkKnownEvent(eventType: string) {
  50 + if (!this.emitters.has(eventType)) {
  51 + throw new Error('Unknown event named ' + eventType.toString());
  52 + }
  53 + }
  54 +
  55 +
  56 +}
0 57 \ No newline at end of file
... ...
src/languages/en.json
1 1 {
2 2 "noosfero.name" : "Noosfero",
3 3 "blocks.profile_image.control_panel": "Control Panel",
  4 + "blocks.profile_image.join": "Join community",
  5 + "blocks.profile_image.leave": "Leave community",
  6 + "blocks.profile_image.join.moderation.title": "Good job!",
  7 + "blocks.profile_image.join.moderation.message": "Your membership is waiting for approval",
4 8 "navbar.profile": "Profile",
5 9 "navbar.settings": "Settings",
6 10 "navbar.logout": "Log Out",
7 11 "navbar.login": "Login",
8 12 "navbar.toggle_menu": "Toggle navigation",
  13 + "configbar.label": "Configuration",
  14 + "configbar.section.layout": "Layout",
9 15 "language.all": "All languages",
10 16 "language.en": "English",
11 17 "language.pt": "Portuguese",
... ... @@ -17,8 +23,13 @@
17 23 "profile.others_info": "Others",
18 24 "profile.community.title": "Community",
19 25 "profile.person.title": "Person",
  26 + "profile.person.description": "Description",
  27 + "profile.person.contact_info": "Contact Informations",
  28 + "profile.person.contact_phone": "Phone",
  29 + "profile.person.no_contact_info": "No contact informations",
20 30 "activities.title": "Activities",
21 31 "activities.create_article.description": "has published on",
  32 + "activities.scrap.description": "wrote in its timeline",
22 33 "activities.add_member_in_community.description": "has joined the community",
23 34 "activities.new_friendship.description": "has made {friends, plural, one{one new friend} other{# new friends}}:",
24 35 "auth.title": "Great to have you back!",
... ... @@ -26,6 +37,7 @@
26 37 "auth.form.password": "Password",
27 38 "auth.form.keepLoggedIn": "Keep me logged in",
28 39 "auth.form.login_button": "Login",
  40 + "auth.createAccount": "Create account",
29 41 "navbar.content_viewer_actions.new_item": "New Item",
30 42 "navbar.profile_actions.new_item": "New Item",
31 43 "navbar.content_viewer_actions.new_post": "New Post",
... ... @@ -77,9 +89,7 @@
77 89 "custom_content.title": "Edit content",
78 90 "profile.custom_header.label": "Header",
79 91 "profile.custom_footer.label": "Footer",
80   - "designMode.label": "In Design",
81   - "designMode.toggle.ON": "ON",
82   - "designMode.toggle.OFF": "OFF",
  92 + "designMode.label": "Design mode",
83 93 "search.results.summary": "{results, plural, one{result} other{# results}}",
84 94 "search.results.query.label": "Search therm:",
85 95 "search.label": "Search",
... ... @@ -97,5 +107,26 @@
97 107 "block.edition.display_user.all": "All users",
98 108 "block.edition.display_user.logged": "Logged",
99 109 "block.edition.display_user.not_logged": "Not logged",
100   - "block.edition.language.label": "Show for:"
  110 + "block.edition.language.label": "Show for:",
  111 + "activities.event.description": "Event on",
  112 + "time.at": "at",
  113 + "date.on": "On",
  114 + "account.register.welcomeMessageTitle": "Nice to have you there!",
  115 + "account.register.fullNameLabel": "Full name",
  116 + "account.register.usernameLabel": "Username",
  117 + "account.register.emailLabel": "Email",
  118 + "account.register.passwordLabel": "Password",
  119 + "account.register.passwordConfirmationLabel": "Password confirmation",
  120 + "account.register.accountCreatingMessage": "By creating an account, you are agreeing with the ",
  121 + "account.register.signupMessage": "Register",
  122 + "account.register.haveAccountMessage": "Already have an account?",
  123 + "account.register.termsOfUseMessage": "terms of use",
  124 + "account.register.success.title": "Good job!",
  125 + "account.register.success.message": "Account created!",
  126 + "account.register.passwordConfirmation.failed": "Wrong password confirmation",
  127 + "messages.invalid.required": "This field is required",
  128 + "messages.invalid.maxlength": "This field is too long",
  129 + "messages.invalid.minlength": "This field is too short",
  130 + "messages.invalid.email": "This needs to be a valid email",
  131 + "messages.invalid.passwordMatch": "Your passwords did not match"
101 132 }
... ...
src/languages/messages.html 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +<li ng-message="required" translate="messages.invalid.required"></li>
  2 +<li ng-message="minlength" translate="messages.invalid.minlength"></li>
  3 +<li ng-message="maxlength" translate="messages.invalid.maxlength"></li>
  4 +<li ng-message="email" translate="messages.invalid.email"></li>
... ...
src/languages/pt.json
1 1 {
2 2 "noosfero.name" : "Noosfero",
3 3 "blocks.profile_image.control_panel": "Painel de Controle",
  4 + "blocks.profile_image.join": "Entrar na comunidade",
  5 + "blocks.profile_image.leave": "Sair da comunidade",
  6 + "blocks.profile_image.join.moderation.title": "Bom trabalho!",
  7 + "blocks.profile_image.join.moderation.message": "Sua participação está pendente de aprovação",
4 8 "navbar.profile": "Perfil",
5 9 "navbar.settings": "Configurações",
6 10 "navbar.logout": "Sair",
7 11 "navbar.login": "Login",
8 12 "navbar.toggle_menu": "Abrir Menu",
  13 + "configbar.label": "Configurações",
  14 + "configbar.section.layout": "Visual",
9 15 "language.all": "Todos os idiomas",
10 16 "language.en": "Inglês",
11 17 "language.pt": "Português",
... ... @@ -17,8 +23,13 @@
17 23 "profile.others_info": "Outras informações",
18 24 "profile.community.title": "Comunidade",
19 25 "profile.person.title": "Pessoa",
  26 + "profile.person.description": "Descrição",
  27 + "profile.person.contact_info": "Informações de Contato",
  28 + "profile.person.contact_phone": "Telefone",
  29 + "profile.person.no_contact_info": "Sem informações de contato",
20 30 "activities.title": "Atividades",
21 31 "activities.create_article.description": "publicou em",
  32 + "activities.scrap.description": "escreveu em sua linha do tempo",
22 33 "activities.add_member_in_community.description": "entrou na comunidade",
23 34 "activities.new_friendship.description": "fez {friends, plural, one{um novo amigo} other{# novos amigos}}:",
24 35 "auth.title": "Legal ter você de volta!",
... ... @@ -26,10 +37,11 @@
26 37 "auth.form.password": "Senha",
27 38 "auth.form.keepLoggedIn": "Continuar logado",
28 39 "auth.form.login_button": "Login",
  40 + "auth.createAccount": "Criar conta",
29 41 "navbar.content_viewer_actions.new_item": "Novo Item",
30 42 "navbar.profile_actions.new_item": "Novo Item",
31   - "navbar.content_viewer_actions.new_post": "Novo Artigo",
32   - "navbar.content_viewer_actions.new_discussion": "Nova Discussão",
  43 + "navbar.content_viewer_actions.new_post": "Novo Post",
  44 + "navbar.content_viewer_actions.new_discussion": "Nova Consulta",
33 45 "navbar.profile_actions.new_discussion": "Nova Discussão",
34 46 "notification.error.default.message": "Algo deu errado!",
35 47 "notification.error.default.title": "Oops...",
... ... @@ -77,9 +89,7 @@
77 89 "custom_content.title": "Editar conteúdo",
78 90 "profile.custom_header.label": "Cabeçalho",
79 91 "profile.custom_footer.label": "Rodapé",
80   - "designMode.label": "Modo de Edição",
81   - "designMode.toggle.ON": "Ligado",
82   - "designMode.toggle.OFF": "Desligado",
  92 + "designMode.label": "Alterar design",
83 93 "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}",
84 94 "search.results.query.label": "Termo da busca:",
85 95 "search.label": "Pesquisar",
... ... @@ -97,5 +107,29 @@
97 107 "block.edition.display_user.all": "Todos os usuários",
98 108 "block.edition.display_user.logged": "Logados",
99 109 "block.edition.display_user.not_logged": "Não logados",
100   - "block.edition.language.label": "Exibir para:"
  110 + "block.edition.language.label": "Exibir para:",
  111 + "activities.event.description": "Evento em",
  112 + "time.at": "às",
  113 + "date.on": "Em",
  114 + "account.register.welcomeMessageTitle": "Ótimo ter você aqui!",
  115 + "account.register.seeMoreMessage": "Saiba mais ",
  116 + "account.register.informationsMessage": "informações",
  117 + "account.register.fullNameLabel": "Nome completo",
  118 + "account.register.lastNameLabel": "Sobrenome",
  119 + "account.register.usernameLabel": "Nome de usuário",
  120 + "account.register.emailLabel": "Email",
  121 + "account.register.passwordLabel": "Senha",
  122 + "account.register.passwordConfirmationLabel": "Confirmar senha",
  123 + "account.register.accountCreatingMessage": "Ao criar uma conta, você está concordando com os ",
  124 + "account.register.termsOfUseMessage": "termos de uso",
  125 + "account.register.signupMessage": "Criar Conta",
  126 + "account.register.haveAccountMessage": "Já possui uma conta?",
  127 + "account.register.success.title": "Bom trabalho!",
  128 + "account.register.success.message": "Conta criada com sucesso!",
  129 + "account.register.passwordConfirmation.failed": "A confirmação de senha não corresponde à senha",
  130 + "messages.invalid.required": "Campo obrigatório",
  131 + "messages.invalid.maxlength": "O valor é muito longo",
  132 + "messages.invalid.minlength": "O valor é muito curto",
  133 + "messages.invalid.email": "Informe um email válido",
  134 + "messages.invalid.passwordMatch": "As senhas não coincidem"
101 135 }
... ...
src/lib/ng-noosfero-api/http/article.service.spec.ts
... ... @@ -17,7 +17,6 @@ describe(&quot;Services&quot;, () =&gt; {
17 17 articleService = _ArticleService_;
18 18 }));
19 19  
20   -
21 20 describe("Succesfull requests", () => {
22 21  
23 22 it("should remove article", (done) => {
... ... @@ -92,6 +91,5 @@ describe(&quot;Services&quot;, () =&gt; {
92 91 });
93 92 });
94 93  
95   -
96 94 });
97 95 });
... ...
src/lib/ng-noosfero-api/http/profile.service.spec.ts
... ... @@ -111,6 +111,56 @@ describe(&quot;Services&quot;, () =&gt; {
111 111 });
112 112 $httpBackend.flush();
113 113 });
  114 +
  115 + it("should return the profile members", (done) => {
  116 + let profileId = 1;
  117 + $httpBackend.expectGET(`/api/v1/profiles/${profileId}/members`).respond(200, { people: [{ id: 2 }] });
  118 + profileService.getMembers(<any>{ id: profileId }).then((response: restangular.IResponse) => {
  119 + expect(response.data.people).toEqual([{ id: 2 }]);
  120 + done();
  121 + });
  122 + $httpBackend.flush();
  123 + });
  124 +
  125 + it("should return true if the person is a profile member", (done) => {
  126 + let profileId = 1;
  127 + $httpBackend.expectGET(`/api/v1/profiles/${profileId}/members`).respond(200, { people: [{ id: 2 }] });
  128 + profileService.isMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => {
  129 + expect(response).toEqual(true);
  130 + done();
  131 + });
  132 + $httpBackend.flush();
  133 + });
  134 +
  135 + it("should return false if the person is a profile member", (done) => {
  136 + let profileId = 1;
  137 + $httpBackend.expectGET(`/api/v1/profiles/${profileId}/members`).respond(200, { people: [] });
  138 + profileService.isMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => {
  139 + expect(response).toEqual(false);
  140 + done();
  141 + });
  142 + $httpBackend.flush();
  143 + });
  144 +
  145 + it("should add member to profile", (done) => {
  146 + let profileId = 1;
  147 + $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/members`).respond(200, { pending: false });
  148 + profileService.addMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => {
  149 + expect(response.data.pending).toEqual(false);
  150 + done();
  151 + });
  152 + $httpBackend.flush();
  153 + });
  154 +
  155 + it("should remove member from profile", (done) => {
  156 + let profileId = 1;
  157 + $httpBackend.expectDELETE(`/api/v1/profiles/${profileId}/members`).respond(200, { person: { id: 2 } });
  158 + profileService.removeMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => {
  159 + expect(response.data.person).toEqual({ id: 2 });
  160 + done();
  161 + });
  162 + $httpBackend.flush();
  163 + });
114 164 });
115 165  
116 166  
... ...
src/lib/ng-noosfero-api/http/profile.service.ts
... ... @@ -64,4 +64,29 @@ export class ProfileService {
64 64 let headers = { 'Content-Type': 'application/json' };
65 65 return this.get(profile.id).customPOST({ profile: profile }, null, null, headers);
66 66 }
  67 +
  68 + getMembers(profile: noosfero.Profile, params?: any) {
  69 + let p = this.get(profile.id);
  70 + return p.customGET('members', params);
  71 + }
  72 +
  73 + isMember(person: noosfero.Person, profile: noosfero.Profile) {
  74 + let deferred = this.$q.defer();
  75 + if (person) {
  76 + this.getMembers(profile, { identifier: person.identifier }).then((result: any) => {
  77 + deferred.resolve(result.data.people.length > 0);
  78 + });
  79 + } else {
  80 + deferred.resolve(false);
  81 + }
  82 + return deferred.promise;
  83 + }
  84 +
  85 + addMember(person: noosfero.Person, profile: noosfero.Profile) {
  86 + return this.get(profile.id).customPOST({}, "members", null, null);
  87 + }
  88 +
  89 + removeMember(person: noosfero.Person, profile: noosfero.Profile) {
  90 + return this.get(profile.id).customDELETE("members", null, null);
  91 + }
67 92 }
... ...
src/lib/ng-noosfero-api/http/register.service.spec.ts 0 → 100644
... ... @@ -0,0 +1,39 @@
  1 +import { RegisterService } from "./register.service";
  2 +
  3 +describe("Services", () => {
  4 +
  5 + describe("Register Service", () => {
  6 +
  7 + let $httpBackend: ng.IHttpBackendService;
  8 + let registerService: RegisterService;
  9 + let $rootScope: ng.IRootScopeService;
  10 + let user: any = {
  11 + id: 1,
  12 + login: 'test',
  13 + email: 'test@email.com'
  14 + };
  15 +
  16 + beforeEach(angular.mock.module("main", ($translateProvider: angular.translate.ITranslateProvider) => {
  17 + $translateProvider.translations('en', {});
  18 + }));
  19 +
  20 + beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _RegisterService_: RegisterService, _$rootScope_: ng.IRootScopeService) => {
  21 + $httpBackend = _$httpBackend_;
  22 + registerService = _RegisterService_;
  23 + $rootScope = _$rootScope_;
  24 + }));
  25 +
  26 + describe("Succesfull requests", () => {
  27 +
  28 + it("should creaet a new account", (done) => {
  29 +
  30 + $httpBackend.expectPOST(`/api/v1/register?email=${user.email}&id=${user.id}&login=${user.login}`).respond(201, [{ login: "test" }]);
  31 + registerService.createAccount(user).then((response: restangular.IResponse) => {
  32 + expect(response.data[0].login).toEqual("test");
  33 + done();
  34 + });
  35 + $httpBackend.flush();
  36 + });
  37 + });
  38 + });
  39 +});
... ...
src/lib/ng-noosfero-api/http/register.service.ts 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +import { Injectable, Inject } from "ng-forward";
  2 +import { RestangularService } from "./restangular_service";
  3 +
  4 +@Injectable()
  5 +@Inject("Restangular")
  6 +export class RegisterService {
  7 + constructor(private Restangular: restangular.IService) {
  8 + this.Restangular = Restangular;
  9 + }
  10 +
  11 + createAccount(user: noosfero.User): ng.IPromise<noosfero.RestResult<noosfero.User>> {
  12 + return this.Restangular.all("").customPOST(user, "register", user);
  13 + }
  14 +}
... ...
src/lib/ng-noosfero-api/http/restangular_service.ts
... ... @@ -80,14 +80,16 @@ export abstract class RestangularService&lt;T extends noosfero.RestModel&gt; {
80 80 }
81 81 return {
82 82 data: (response.data[dataKey] || response.data),
83   - headers: response.headers
  83 + headers: response.headers,
  84 + status: response.status
84 85 };
85 86 };
86 87  
87 88 protected buildResult(response: restangular.IResponse): noosfero.RestResult<T> {
88 89 return {
89 90 data: response.data,
90   - headers: response.headers
  91 + headers: response.headers,
  92 + status: response.status
91 93 };
92 94 };
93 95 /**
... ...
src/lib/ng-noosfero-api/interfaces/environment.ts
... ... @@ -5,7 +5,7 @@ namespace noosfero {
5 5 * @name noofero.Environment
6 6 * @description
7 7 * A representation of a Noosfero Environment.
8   - */
  8 + */
9 9 export interface Environment extends RestModel {
10 10 /**
11 11 * @ngdoc property
... ... @@ -23,6 +23,21 @@ namespace noosfero {
23 23 * @returns {string} The Environment layout (e.g. default, rightbar)
24 24 */
25 25 layout_template: string;
  26 +
  27 + /**
  28 + * @ngdoc property
  29 + * @name signup_intro
  30 + * @propertyOf noofero.Environment
  31 + * @returns {string} The Environment signup introduction HTML (e.g. Welcome to Noosfero...!!)
  32 + */
  33 + signup_intro: string;
  34 +
  35 + /**
  36 + * @ngdoc property
  37 + * @name host
  38 + * @propertyOf noofero.Environment
  39 + * @returns {string} The Environment default domain address with 'http://' prefix (e.g. http://localhost)
  40 + */
  41 + host: string;
26 42 }
27 43 }
28   -
... ...
src/lib/ng-noosfero-api/interfaces/rest_result.ts
... ... @@ -3,5 +3,6 @@ namespace noosfero {
3 3 export interface RestResult<T> {
4 4 data: T;
5 5 headers: Function;
  6 + status: Number;
6 7 }
7   -}
8 8 \ No newline at end of file
  9 +}
... ...
src/spec/mocks.ts
... ... @@ -40,6 +40,11 @@ export var mocks: any = {
40 40 return this.modalInstance;
41 41 }
42 42 },
  43 + registerService: {
  44 + createAccount: (user: noosfero.User) => {
  45 + return Promise.resolve({ status: 201 });
  46 + }
  47 + },
43 48 authService: {
44 49 loginSuccess: {
45 50 event: Function,
... ... @@ -145,6 +150,15 @@ export var mocks: any = {
145 150 return mocks.promiseResultTemplate({
146 151 people: {}
147 152 });
  153 + },
  154 + getCurrentEnvironment: (): any => {
  155 + return {
  156 + id: 1,
  157 + settings: {},
  158 + layout_template: '',
  159 + signup_intro: 'Welcome to Noosfero',
  160 + host: 'http://localhost'
  161 + };
148 162 }
149 163 },
150 164 profileService: {
... ... @@ -240,6 +254,7 @@ export var mocks: any = {
240 254 },
241 255 notificationService: {
242 256 success: () => { },
243   - confirmation: () => { }
  257 + confirmation: () => { },
  258 + error: () => { }
244 259 }
245 260 };
... ...
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 }
... ...
typings.json
... ... @@ -8,6 +8,7 @@
8 8 "angular-mocks": "github:DefinitelyTyped/DefinitelyTyped/angularjs/angular-mocks.d.ts",
9 9 "angular-translate": "github:DefinitelyTyped/DefinitelyTyped/angular-translate/angular-translate.d.ts#19850bf86c876e0c2544842114878ece4664941a",
10 10 "angular-ui-router": "github:DefinitelyTyped/DefinitelyTyped/angular-ui-router/angular-ui-router.d.ts#655f8c1bf3c71b0e1ba415b36309604f79326ac8",
  11 + "angular-ui-bootstrap": "github:DefinitelyTyped/DefinitelyTyped/angular-ui-bootstrap/angular-ui-bootstrap.d.ts",
11 12 "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#4de74cb527395c13ba20b438c3a7a419ad931f1c",
12 13 "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#dd638012d63e069f2c99d06ef4dcc9616a943ee4",
13 14 "jquery": "github:DefinitelyTyped/DefinitelyTyped/jquery/jquery.d.ts#470954c4f427e0805a2d633636a7c6aa7170def8",
... ...