Commit d930c28d68b0e0c8309095002cbe6a1f3f9368d8

Authored by Victor Costa
2 parents 2d6e6299 628d8b3b
Exists in staging

Merge branch 'master' into staging

Showing 44 changed files with 831 additions and 93 deletions   Show diff stats
bower.json
... ... @@ -36,14 +36,21 @@
36 36 "angular-load": "^0.4.1",
37 37 "angular-translate-interpolation-messageformat": "^2.10.0",
38 38 "angular-bind-html-compile": "^1.2.1",
39   - "angular-click-outside": "^2.7.1"
  39 + "angular-click-outside": "^2.7.1",
  40 + "ng-ckeditor": "^0.2.1"
40 41 },
41 42 "devDependencies": {
42   - "angular-mocks": "~1.5.0",
43   - "ng-ckeditor": "^0.2.1",
44   - "ckeditor": "^4.5.8"
  43 + "angular-mocks": "~1.5.0"
45 44 },
46 45 "overrides": {
  46 + "ng-ckeditor": {
  47 + "main": [
  48 + "ng-ckeditor.js",
  49 + "libs/ckeditor/lang",
  50 + "libs/ckeditor/ckeditor.js",
  51 + "libs/ckeditor/config.js"
  52 + ]
  53 + },
47 54 "bootstrap-sass": {
48 55 "main": [
49 56 "assets/stylesheets/_bootstrap.scss",
... ...
gulp/build.js
... ... @@ -103,6 +103,7 @@ gulp.task('fonts', function () {
103 103 });
104 104  
105 105 gulp.task('ckeditor', function () {
  106 + conf.wiredep.exclude.push(/ckeditor/); // exclude ckeditor from build to improve performance
106 107 return gulp.src(['bower_components/ng-ckeditor/**/*']).pipe(gulp.dest(path.join(conf.paths.dist, '/ng-ckeditor')));
107 108 });
108 109  
... ... @@ -153,4 +154,4 @@ gulp.task('noosfero', ['html'], function () {
153 154 return merge(layouts, theme, index);
154 155 });
155 156  
156   -gulp.task('build', ['html', 'fonts', 'other', 'locale', 'ckeditor', 'plugin-languages', 'noosfero']);
  157 +gulp.task('build', ['ckeditor', 'html', 'fonts', 'other', 'locale', 'plugin-languages', 'noosfero']);
... ...
gulp/conf.js
... ... @@ -36,7 +36,7 @@ exports.configTheme(argv.theme);
36 36 * to inject css preprocessor deps and js files in karma
37 37 */
38 38 exports.wiredep = {
39   - exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/, /ckeditor/],
  39 + exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/],
40 40 directory: 'bower_components'
41 41 };
42 42  
... ...
karma.conf.js
... ... @@ -126,6 +126,10 @@ module.exports = function (config) {
126 126 };
127 127  
128 128  
  129 + if(config.grep) {
  130 + configuration.client = { args: ['--grep', config.grep] };
  131 + }
  132 +
129 133 if (coverage) {
130 134  
131 135 /*configuration.webpack = {
... ...
src/app/article/cms/article-editor/article-editor.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +import {ArticleEditorComponent} from './article-editor.component';
  2 +import {BasicEditorComponent} from "../basic-editor/basic-editor.component";
  3 +import {ComponentTestHelper, createClass} from '../../../../spec/component-test-helper';
  4 +import * as helpers from "../../../../spec/helpers";
  5 +
  6 +const htmlTemplate: string = '<article-editor [article]="ctrl.article"></article-editor>';
  7 +
  8 +describe("Components", () => {
  9 + describe("Article Editor Component", () => {
  10 +
  11 + let helper: ComponentTestHelper<ArticleEditorComponent>;
  12 + beforeEach(angular.mock.module("templates"));
  13 +
  14 + beforeEach((done) => {
  15 + let properties = { article: { type: "TextArticle" } };
  16 + let cls = createClass({
  17 + template: htmlTemplate,
  18 + directives: [ArticleEditorComponent, BasicEditorComponent],
  19 + properties: properties
  20 + });
  21 + helper = new ComponentTestHelper<ArticleEditorComponent>(cls, done);
  22 + });
  23 +
  24 + it("replace element with article basic editor when type is TextArticle", () => {
  25 + expect(helper.find("article-basic-editor").length).toEqual(1);
  26 + });
  27 + });
  28 +});
... ...
src/app/article/cms/article-editor/article-editor.component.ts
... ... @@ -16,10 +16,10 @@ export class ArticleEditorComponent {
16 16 private $compile: ng.ICompileService) { }
17 17  
18 18 ngOnInit() {
19   - let articleType = this.article.type.replace(/::/, '');
  19 + let articleType = this.article && this.article.type ? this.article.type.replace(/::/, '') : "TextArticle";
20 20 let specificDirective = `${articleType.charAt(0).toLowerCase()}${articleType.substring(1)}Editor`;
21 21 let directiveName = "article-basic-editor";
22   - if (this.$injector.has(specificDirective + 'Directive')) {
  22 + if (specificDirective !== "articleEditor" && this.$injector.has(specificDirective + 'Directive')) {
23 23 directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
24 24 }
25 25 this.$element.replaceWith(this.$compile('<' + directiveName + ' [article]="ctrl.article"></' + directiveName + '>')(this.$scope));
... ...
src/app/article/cms/cms.component.spec.ts
... ... @@ -55,6 +55,7 @@ describe(&quot;Article Cms&quot;, () =&gt; {
55 55 });
56 56  
57 57 it("got to the new article page and display an alert when saving sucessfully", done => {
  58 + $stateParams['parent_id'] = 1;
58 59 let component: CmsComponent = new CmsComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
59 60 component.save();
60 61 $rootScope.$apply();
... ...
src/app/article/comment/comment.component.spec.ts
... ... @@ -7,16 +7,23 @@ const htmlTemplate: string = &#39;&lt;noosfero-comment [article]=&quot;ctrl.article&quot; [commen
7 7 describe("Components", () => {
8 8 describe("Comment Component", () => {
9 9  
10   - beforeEach(angular.mock.module("templates"));
  10 + let properties: any;
11 11  
  12 + beforeEach(angular.mock.module("templates"));
  13 + beforeEach(() => {
  14 + properties = {
  15 + article: { id: 1, accept_comments: true },
  16 + comment: { title: "title", body: "body" }
  17 + };
  18 + });
12 19  
13 20 function createComponent() {
14 21 let providers = helpers.provideFilters("translateFilter");
15 22  
16 23 @Component({ selector: 'test-container-component', directives: [CommentComponent], template: htmlTemplate, providers: providers })
17 24 class ContainerComponent {
18   - article = { id: 1 };
19   - comment = { title: "title", body: "body" };
  25 + article = properties['article'];
  26 + comment = properties['comment'];
20 27 }
21 28 return helpers.createComponentFromClass(ContainerComponent);
22 29 }
... ... @@ -52,5 +59,20 @@ describe(&quot;Components&quot;, () =&gt; {
52 59 done();
53 60 });
54 61 });
  62 +
  63 + it("display reply button", done => {
  64 + createComponent().then(fixture => {
  65 + expect(fixture.debugElement.queryAll(".comment .actions .reply").length).toEqual(1);
  66 + done();
  67 + });
  68 + });
  69 +
  70 + it("not display reply button when accept_comments is false", done => {
  71 + properties['article']['accept_comments'] = false;
  72 + createComponent().then(fixture => {
  73 + expect(fixture.debugElement.queryAll(".comment .actions .reply").length).toEqual(0);
  74 + done();
  75 + });
  76 + });
55 77 });
56 78 });
... ...
src/app/article/comment/comment.html
... ... @@ -18,7 +18,7 @@
18 18 <div class="title">{{ctrl.comment.title}}</div>
19 19 <div class="body">{{ctrl.comment.body}}</div>
20 20 <div class="actions" ng-if="ctrl.displayActions">
21   - <a href="#" (click)="ctrl.reply()" class="small text-muted" ng-if="ctrl.article.accept_comments">
  21 + <a href="#" (click)="ctrl.reply()" class="small text-muted reply" ng-if="ctrl.article.accept_comments">
22 22 {{"comment.reply" | translate}}
23 23 </a>
24 24 </div>
... ...
src/app/layout/blocks/display-content/display-content-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,75 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Provider, provide} from 'ng-forward';
  3 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
  4 +import {providers} from 'ng-forward/cjs/testing/providers';
  5 +import {DisplayContentBlockComponent} from './display-content-block.component';
  6 +import * as helpers from './../../../../spec/helpers';
  7 +
  8 +const htmlTemplate: string = '<noosfero-display-content-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-display-content-block>';
  9 +
  10 +describe("Components", () => {
  11 +
  12 + describe("Display Content Block Component", () => {
  13 + let state = jasmine.createSpyObj("state", ["go"]);
  14 + let providers = [
  15 + provide('ArticleService', {
  16 + useValue: helpers.mocks.articleService
  17 + }),
  18 + provide('$state', { useValue: state })
  19 + ].concat(helpers.provideFilters("translateFilter"));
  20 +
  21 + let sections: noosfero.Section[] = [
  22 + { value: 'abstract', checked: 'abstract'},
  23 + { value: 'title', checked: 'title' }
  24 + ];
  25 + let settings: noosfero.Settings = {
  26 + limit: 6,
  27 + sections: sections
  28 + };
  29 +
  30 + let helper: ComponentTestHelper<DisplayContentBlockComponent>;
  31 +
  32 + beforeEach(angular.mock.module("templates"));
  33 +
  34 + /**
  35 + * The beforeEach procedure will initialize the helper and parse
  36 + * the component according to the given providers. Unfortunetly, in
  37 + * this mode, the providers and properties given to the construtor
  38 + * can't be overriden.
  39 + */
  40 + beforeEach((done) => {
  41 + // Create the component bed for the test. Optionally, this could be done
  42 + // in each test if one needs customization of these parameters per test
  43 + let cls = createClass({
  44 + template: htmlTemplate,
  45 + directives: [DisplayContentBlockComponent],
  46 + providers: providers,
  47 + properties: {
  48 + block: {
  49 + settings: settings
  50 + }
  51 + }
  52 + });
  53 + helper = new ComponentTestHelper<DisplayContentBlockComponent>(cls, done);
  54 + });
  55 +
  56 + it("verify settings is injected", () => {
  57 + expect(helper.component.block).not.toBeNull;
  58 + expect(helper.component.block.settings).not.toBeNull;
  59 + expect(helper.component.block.settings.limit).toEqual(6);
  60 + expect(helper.component.block.settings.sections.length).toEqual(3);
  61 + });
  62 +
  63 + it("verify abstract is displayed", () => {
  64 + expect(helper.all("div[ng-bind-html|='article.abstract']")[0]).not.toBeNull;
  65 + });
  66 +
  67 + it("verify title is displayed", () => {
  68 + expect(helper.all("div > h5")[0]).not.toBeNull;
  69 + });
  70 +
  71 + it("verify body is not displayed", () => {
  72 + expect(helper.all("div[ng-bind-html|='article.body']")[0]).toBeNull;
  73 + });
  74 + });
  75 +});
... ...
src/app/layout/blocks/display-content/display-content-block.component.ts 0 → 100644
... ... @@ -0,0 +1,54 @@
  1 +import {Input, Inject, Component} from "ng-forward";
  2 +import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service";
  3 +
  4 +@Component({
  5 + selector: "noosfero-display-content-block",
  6 + templateUrl: 'app/layout/blocks/display-content/display-content-block.html',
  7 +})
  8 +@Inject(ArticleService, "$state")
  9 +export class DisplayContentBlockComponent {
  10 +
  11 + @Input() block: noosfero.Block;
  12 + @Input() owner: noosfero.Profile;
  13 +
  14 + profile: noosfero.Profile;
  15 + articles: noosfero.Article[];
  16 + sections: noosfero.Section[];
  17 +
  18 + documentsLoaded: boolean = false;
  19 +
  20 + constructor(private articleService: ArticleService, private $state: ng.ui.IStateService) {}
  21 +
  22 + ngOnInit() {
  23 + this.profile = this.owner;
  24 + let limit = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5;
  25 + this.articleService.getByProfile(this.profile, { content_type: 'TinyMceArticle', per_page: limit })
  26 + .then((result: noosfero.RestResult<noosfero.Article[]>) => {
  27 + this.articles = <noosfero.Article[]>result.data;
  28 + this.sections = this.block.settings.sections;
  29 + // Add sections not defined by Noosfero API
  30 + this.addDefaultSections();
  31 + this.documentsLoaded = true;
  32 + });
  33 + }
  34 +
  35 + /**
  36 + * This configuration doesn't exists on Noosfero. Statically typing here.
  37 + */
  38 + private addDefaultSections() {
  39 + let author: noosfero.Section = <noosfero.Section>{ value: 'author', checked: 'author' };
  40 + this.sections.push(author);
  41 + }
  42 +
  43 + /**
  44 + * Returns whether a settings section should be displayed.
  45 + *
  46 + */
  47 + private display(section_name: string): boolean {
  48 + let section: noosfero.Section = this.sections.find( function(section: noosfero.Section) {
  49 + return section.value === section_name;
  50 + });
  51 + return section !== undefined && section.checked !== undefined;
  52 + }
  53 +
  54 +}
... ...
src/app/layout/blocks/display-content/display-content-block.html 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +<div class="{{ctrl.type}}-block">
  2 + <div ng-repeat="article in ctrl.articles" ui-sref="main.profile.page({profile: ctrl.profile.identifier, page: article.path})"" class="article">
  3 + <!-- Article Title -->
  4 + <div class="page-header" ng-if="ctrl.display('title')">
  5 + <h5 class="title media-heading" ng-bind="article.title"></h3>
  6 + </div>
  7 +
  8 + <div class="sub-header clearfix">
  9 + <!-- Article Abstract and Read More Link -->
  10 + <div class="post-lead" ng-if="ctrl.display('abstract')">
  11 + <div ng-bind-html="article.abstract"></div>
  12 + <a href="#" ui-sref="main.profile.page({profile: ctrl.profile.identifier, page: article.path})">
  13 + <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.read_more" | translate}}
  14 + </a>
  15 + </div>
  16 + <div class="page-info pull-right small text-muted" ng-if="ctrl.display('publish_date')">
  17 + <!-- Article Published Date -->
  18 + <span class="time">
  19 + <i class="fa fa-clock-o"></i> <span am-time-ago="article.created_at | dateFormat"></span>
  20 + </span>
  21 + <!-- Article Author -->
  22 + <span class="author" ng-if="ctrl.display('author')">
  23 + <i class="fa fa-user"></i>
  24 + <a ui-sref="main.profile.home({profile: article.author.identifier})" ng-if="article.author">
  25 + <span class="author-name" ng-bind="article.author.name"></span>
  26 + </a>
  27 + </span>
  28 + </div>
  29 + </div>
  30 +
  31 + <div class="post-lead">
  32 + <!-- Article Image -->
  33 + <img ng-show="ctrl.display('image')" ng-src="{{article.image.url}}" class="img-responsive article-image">
  34 + <!-- Article Body -->
  35 + <div ng-bind-html="article.body" ng-show="ctrl.display('body')"></div>
  36 + </div>
  37 +
  38 + <!-- Article Tags -->
  39 + <div ng-if="ctrl.display('tags')" class="post-lead">
  40 + <div class="label" ng-repeat="tag in article.tag_list">
  41 + <span class="badge" ng-bind="tag"></span>
  42 + </div>
  43 + </div>
  44 +
  45 + </div>
  46 +</div>
... ...
src/app/layout/blocks/display-content/display-content-block.scss 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +.members-block {
  2 + .member {
  3 + img, i.profile-image {
  4 + width: 60px;
  5 + }
  6 + img {
  7 + display: inline-block;
  8 + vertical-align: top;
  9 + }
  10 + i.profile-image {
  11 + text-align: center;
  12 + background-color: #889DB1;
  13 + color: #F1F1F1;
  14 + font-size: 4.5em;
  15 + }
  16 + }
  17 +}
... ...
src/app/layout/blocks/display-content/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./display-content-block.component";
... ...
src/app/layout/blocks/login-block/index.ts 0 → 100644
src/app/layout/blocks/login-block/login-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,110 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Injectable, Provider, provide} from "ng-forward";
  3 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
  4 +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
  5 +import {providers} from 'ng-forward/cjs/testing/providers';
  6 +import {LoginBlockComponent} from './login-block.component';
  7 +import * as helpers from "./../../../../spec/helpers";
  8 +import {SessionService, AuthService, AuthController, AuthEvents} from "./../../../login";
  9 +
  10 +const htmlTemplate: string = '<noosfero-login-block></noosfero-login-block>';
  11 +
  12 +describe("Components", () => {
  13 +
  14 + describe("Login Block Component", () => {
  15 + let helper: ComponentTestHelper<LoginBlockComponent>;
  16 + let person: any = null;
  17 +
  18 + /**
  19 + * Mock objects
  20 + */
  21 + let authService: any = helpers.mocks.authService;
  22 + let user = <noosfero.User>{ person: person };
  23 + let sessionService: any = <any>helpers.mocks.sessionWithCurrentUser(user);
  24 + let state = jasmine.createSpyObj("$state", ["go"]);
  25 + let scope = helpers.mocks.scopeWithEvents;
  26 +
  27 + let providers = [
  28 + new Provider('SessionService', { useValue: sessionService }),
  29 + new Provider('$state', { useValue: state }),
  30 + new Provider('AuthService', { useValue: authService }),
  31 + new Provider('$scope', { useValue: scope })
  32 + ];
  33 +
  34 + beforeEach( angular.mock.module("templates") );
  35 +
  36 + beforeEach( (done: Function) => {
  37 + let cls = createClass({
  38 + template: htmlTemplate,
  39 + directives: [LoginBlockComponent],
  40 + providers: providers,
  41 + properties: {}
  42 + });
  43 + helper = new ComponentTestHelper<LoginBlockComponent>(cls, done);
  44 + });
  45 +
  46 + it("expect person to be null with no logged in user", () => {
  47 + expect(helper.component.currentUser).toBeNull;
  48 + });
  49 +
  50 + it("expect person to be defined when user login", () => {
  51 + // Executes the login method on the component
  52 + doComponentLogin();
  53 + expect(helper.component.currentUser.person).toBe(person);
  54 + });
  55 +
  56 + it("expect person to be null when user logout", () => {
  57 + // First do a login
  58 + doComponentLogin();
  59 + // The logout the user
  60 + doComponentLogout();
  61 + // Check if the current user was cleared
  62 + expect(helper.component.currentUser).toBeNull;
  63 + });
  64 +
  65 + /**
  66 + * Execute the logout method on the target component
  67 + */
  68 + function doComponentLogout() {
  69 + // Create a mock for the AuthService logout method
  70 + spyOn(authService, "logout");
  71 + helper.component.logout();
  72 + expect(authService.logout).toHaveBeenCalled();
  73 + // After the component logout method execution, fire the
  74 + // AuthService event
  75 + simulateLogoutEvent();
  76 + }
  77 +
  78 + /**
  79 + * Execute the login method on the target component
  80 + */
  81 + function doComponentLogin() {
  82 + // Create a mock for the AuthService login method
  83 + spyOn(authService, "login");
  84 + helper.component.login();
  85 + expect(authService.login).toHaveBeenCalled();
  86 + // After the component login method execution, fire the
  87 + // AuthService event
  88 + simulateLoginEvent();
  89 + }
  90 +
  91 + /**
  92 + * Simulate the AuthService loginSuccess event
  93 + */
  94 + function simulateLoginEvent() {
  95 + let successEvent: string = AuthEvents[AuthEvents.loginSuccess];
  96 +
  97 + (<any>helper.component.authService)[successEvent].next(user);
  98 + }
  99 +
  100 + /**
  101 + * Simulate the AuthService logoutSuccess event
  102 + */
  103 + function simulateLogoutEvent() {
  104 + let successEvent: string = AuthEvents[AuthEvents.logoutSuccess];
  105 +
  106 + (<any>helper.component.authService)[successEvent].next(user);
  107 + }
  108 + });
  109 +
  110 +});
0 111 \ No newline at end of file
... ...
src/app/layout/blocks/login-block/login-block.component.ts 0 → 100644
... ... @@ -0,0 +1,73 @@
  1 +import {Input, Inject, Component} from "ng-forward";
  2 +import {SessionService, AuthService, AuthEvents} from "./../../../login";
  3 +
  4 +/**
  5 + * @ngdoc controller
  6 + * @name layout.blocks.LoginBlockComponent
  7 + * @description
  8 + * The Noosfero block responible for presenting a login form and user status
  9 + */
  10 +@Component({
  11 + selector: "noosfero-login-block",
  12 + templateUrl: 'app/layout/blocks/login-block/login-block.html',
  13 +})
  14 +@Inject("SessionService", "$state", 'AuthService', "$scope")
  15 +export class LoginBlockComponent {
  16 +
  17 + /**
  18 + * @ngdoc property
  19 + * @name currentUser
  20 + * @propertyOf layout.blocks.LoginBlockComponent
  21 + * @description
  22 + * The current loged in user
  23 + */
  24 + currentUser: noosfero.User;
  25 +
  26 + /**
  27 + * @ngdoc property
  28 + * @name credentials
  29 + * @propertyOf layout.blocks.LoginBlockComponent
  30 + * @description
  31 + * The credentials of the currentUser
  32 + */
  33 + credentials: noosfero.Credentials;
  34 +
  35 + constructor(
  36 + private session: SessionService,
  37 + private $state: ng.ui.IStateService,
  38 + public authService: AuthService,
  39 + private $scope: ng.IScope) {
  40 + this.currentUser = this.session.currentUser();
  41 +
  42 + this.authService.subscribe(AuthEvents[AuthEvents.loginSuccess], () => {
  43 + this.currentUser = this.session.currentUser();
  44 + });
  45 +
  46 + this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => {
  47 + this.currentUser = this.session.currentUser();
  48 + });
  49 +
  50 + }
  51 +
  52 + /**
  53 + * @ngdoc method
  54 + * @name login
  55 + * @methodOf layout.blocks.LoginBlockComponent
  56 + * @description
  57 + * Logs in the user using its credentials
  58 + */
  59 + login() {
  60 + this.authService.login(this.credentials);
  61 + }
  62 +
  63 + /**
  64 + * @ngdoc method
  65 + * @name logout
  66 + * @methodOf layout.blocks.LoginBlockComponent
  67 + * @description
  68 + * Logout the user
  69 + */
  70 + logout() {
  71 + this.authService.logout();
  72 + };
  73 +}
... ...
src/app/layout/blocks/login-block/login-block.html 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +<div class="logged-user-info" ng-show="ctrl.currentUser">
  2 + <h4>Logged in as {{ctrl.currentUser.person.identifier}}</h4>
  3 + <ul>
  4 + <li>User since
  5 + <span class="time">
  6 + <span am-time-ago="ctrl.currentUser.person.created_at | dateFormat"></span>
  7 + </span>
  8 + </li>
  9 + <li><a ui-sref="main.profile.info({profile: ctrl.currentUser.person.identifier})">Profile Homepage</a></li>
  10 + </ul>
  11 + <div class="user-actions">
  12 + <a href="#" ng-click="ctrl.logout()"><i class="fa fa-fw fa-power-off"></i> {{"navbar.logout" | translate}}</a>
  13 + </div>
  14 +</div>
  15 +<div class="logged-user-info" ng-show="!ctrl.currentUser">
  16 + <form>
  17 + <div class="form-group">
  18 + <label for="exampleInputEmail1">{{"auth.form.login" | translate}}</label>
  19 + <input type="text" class="form-control" id="exampleInputEmail1" placeholder="Login / Email" ng-model="ctrl.credentials.username">
  20 + </div>
  21 + <div class="form-group">
  22 + <label for="exampleInputPassword1">{{"auth.form.password" | translate}}</label>
  23 + <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="ctrl.credentials.password">
  24 + </div>
  25 + <button type="submit" class="btn btn-default" ng-click="ctrl.login()">{{"auth.form.login_button" | translate}}</button>
  26 + </form>
  27 +</div>
0 28 \ No newline at end of file
... ...
src/app/layout/blocks/recent-documents/recent-documents-block.component.spec.ts
... ... @@ -11,9 +11,9 @@ describe(&quot;Components&quot;, () =&gt; {
11 11 describe("Recent Documents Block Component", () => {
12 12  
13 13 let settingsObj = {};
14   - let mockedArticleService = {
15   - getByProfile: (profile: noosfero.Profile, filters: any): any => {
16   - return Promise.resolve({ data: [{ name: "article1" }], headers: (name: string) => { return name; } });
  14 + let mockedBlockService = {
  15 + getApiContent: (block: noosfero.Block): any => {
  16 + return Promise.resolve({ articles: [{ name: "article1" }], headers: (name: string) => { return name; } });
17 17 }
18 18 };
19 19 let profile = { name: 'profile-name' };
... ... @@ -25,8 +25,8 @@ describe(&quot;Components&quot;, () =&gt; {
25 25 function getProviders() {
26 26 return [
27 27 new Provider('$state', { useValue: state }),
28   - new Provider('ArticleService', {
29   - useValue: mockedArticleService
  28 + new Provider('BlockService', {
  29 + useValue: mockedBlockService
30 30 }),
31 31 ].concat(provideFilters("truncateFilter", "stripTagsFilter"));
32 32 }
... ... @@ -44,7 +44,7 @@ describe(&quot;Components&quot;, () =&gt; {
44 44 }
45 45  
46 46  
47   - it("get recent documents from the article service", done => {
  47 + it("get recent documents from the block service", done => {
48 48 tcb.createAsync(getComponent()).then(fixture => {
49 49 let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
50 50 expect(recentDocumentsBlock.documents).toEqual([{ name: "article1" }]);
... ... @@ -61,20 +61,5 @@ describe(&quot;Components&quot;, () =&gt; {
61 61 });
62 62 });
63 63  
64   - it("it uses default limit 5 if not defined on block", done => {
65   - settingsObj = null;
66   - mockedArticleService = jasmine.createSpyObj("mockedArticleService", ["getByProfile"]);
67   - (<any>mockedArticleService).mocked = true;
68   - let thenMocked = jasmine.createSpy("then");
69   - mockedArticleService.getByProfile = jasmine.createSpy("getByProfile").and.returnValue({then: thenMocked});
70   - let getByProfileFunct = mockedArticleService.getByProfile;
71   - tcb.createAsync(getComponent()).then(fixture => {
72   - let recentDocumentsBlock: RecentDocumentsBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
73   - recentDocumentsBlock.openDocument({ path: "path", profile: { identifier: "identifier" } });
74   - expect(getByProfileFunct).toHaveBeenCalledWith(profile, { content_type: 'TinyMceArticle', per_page: 5 });
75   - done();
76   - });
77   - });
78   -
79 64 });
80 65 });
... ...
src/app/layout/blocks/recent-documents/recent-documents-block.component.ts
1 1 import {Component, Inject, Input} from "ng-forward";
2   -import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service";
  2 +import {BlockService} from "../../../../lib/ng-noosfero-api/http/block.service";
3 3  
4 4 @Component({
5 5 selector: "noosfero-recent-documents-block",
6 6 templateUrl: 'app/layout/blocks/recent-documents/recent-documents-block.html'
7 7 })
8   -@Inject(ArticleService, "$state")
  8 +@Inject(BlockService, "$state")
9 9 export class RecentDocumentsBlockComponent {
10 10  
11 11 @Input() block: any;
... ... @@ -13,23 +13,15 @@ export class RecentDocumentsBlockComponent {
13 13  
14 14 profile: any;
15 15 documents: any;
16   -
17 16 documentsLoaded: boolean = false;
18 17  
19   - constructor(private articleService: ArticleService, private $state: any) {
20   - }
  18 + constructor(private blockService: BlockService, private $state: any) { }
21 19  
22 20 ngOnInit() {
23 21 this.profile = this.owner;
24 22 this.documents = [];
25   -
26   - let limit = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5;
27   - // FIXME get all text articles
28   - // FIXME make the getByProfile a generic method where we tell the type passing a class TinyMceArticle
29   - // and the promise should be of type TinyMceArticle[], per example
30   - this.articleService.getByProfile(this.profile, { content_type: 'TinyMceArticle', per_page: limit })
31   - .then((result: noosfero.RestResult<noosfero.Article[]>) => {
32   - this.documents = <noosfero.Article[]>result.data;
  23 + this.blockService.getApiContent(this.block).then((content: any) => {
  24 + this.documents = content.articles;
33 25 this.documentsLoaded = true;
34 26 });
35 27 }
... ...
src/app/layout/services/body-state-classes.service.spec.ts
... ... @@ -152,7 +152,7 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
152 152 it("add a css class to content wrapper element", () => {
153 153 let service = getService();
154 154  
155   - let contentWrapperMock = jasmine.createSpyObj("contentWrapperMock", ["addClass", "removeClass"])
  155 + let contentWrapperMock = jasmine.createSpyObj("contentWrapperMock", ["addClass", "removeClass"]);
156 156 service["getContentWrapper"] = jasmine.createSpy("getContentWrapper").and.returnValue(contentWrapperMock);
157 157 service.addContentClass(true);
158 158  
... ... @@ -162,7 +162,7 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
162 162 it("remove a css class from content wrapper element", () => {
163 163 let service = getService();
164 164  
165   - let contentWrapperMock = jasmine.createSpyObj("contentWrapperMock", ["addClass", "removeClass"])
  165 + let contentWrapperMock = jasmine.createSpyObj("contentWrapperMock", ["addClass", "removeClass"]);
166 166 service["getContentWrapper"] = jasmine.createSpy("getContentWrapper").and.returnValue(contentWrapperMock);
167 167 service.addContentClass(false);
168 168  
... ...
src/app/layout/services/body-state-classes.service.ts
1 1 import {Directive, Inject, Injectable} from "ng-forward";
2   -import {AuthEvents} from "./../../login/auth-events";
  2 +import {AuthEvents} from "../../login/auth-events";
3 3 import {AuthService} from "./../../login/auth.service";
4 4 import {HtmlUtils} from "../html-utils";
5 5 import {INgForwardJQuery} from 'ng-forward/cjs/util/jqlite-extensions';
... ...
src/app/login/session.service.ts
... ... @@ -11,13 +11,11 @@ export class SessionService {
11 11  
12 12 create(data: UserResponse): noosfero.User {
13 13 this.$localStorage.currentUser = data.user;
14   - this.$log.debug('User session created.', this.$localStorage.currentUser);
15 14 return this.$localStorage.currentUser;
16 15 };
17 16  
18 17 destroy() {
19 18 delete this.$localStorage.currentUser;
20   - this.$log.debug('User session destroyed.');
21 19 };
22 20  
23 21 currentUser(): noosfero.User {
... ...
src/app/main/main.component.ts
... ... @@ -10,15 +10,17 @@ import {BlockComponent} from &quot;../layout/blocks/block.component&quot;;
10 10 import {EnvironmentComponent} from "../environment/environment.component";
11 11 import {EnvironmentHomeComponent} from "../environment/environment-home.component";
12 12 import {PeopleBlockComponent} from "../layout/blocks/people/people-block.component";
13   -import {LinkListBlockComponent} from "./../layout/blocks/link-list/link-list-block.component";
  13 +import {DisplayContentBlockComponent} from "../layout/blocks/display-content/display-content-block.component";
  14 +import {LinkListBlockComponent} from "../layout/blocks/link-list/link-list-block.component";
14 15 import {RecentDocumentsBlockComponent} from "../layout/blocks/recent-documents/recent-documents-block.component";
15 16 import {ProfileImageBlockComponent} from "../layout/blocks/profile-image/profile-image-block.component";
16 17 import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html-block.component";
17 18 import {StatisticsBlockComponent} from "../layout/blocks/statistics/statistics-block.component";
18 19  
19   -import {MembersBlockComponent} from "./../layout/blocks/members/members-block.component";
20   -import {CommunitiesBlockComponent} from "./../layout/blocks/communities/communities-block.component";
  20 +import {MembersBlockComponent} from "../layout/blocks/members/members-block.component";
  21 +import {CommunitiesBlockComponent} from "../layout/blocks/communities/communities-block.component";
21 22  
  23 +import {LoginBlockComponent} from "../layout/blocks/login-block/login-block.component";
22 24  
23 25 import {NoosferoTemplate} from "../shared/pipes/noosfero-template.filter";
24 26 import {DateFormat} from "../shared/pipes/date-format.filter";
... ... @@ -93,10 +95,11 @@ export class EnvironmentContent {
93 95 template: '<div ng-view></div>',
94 96 directives: [
95 97 ArticleBlogComponent, ArticleViewComponent, BoxesComponent, BlockComponent,
96   - EnvironmentComponent, PeopleBlockComponent,
  98 + EnvironmentComponent, PeopleBlockComponent, DisplayContentBlockComponent,
97 99 LinkListBlockComponent, CommunitiesBlockComponent, HtmlEditorComponent,
98 100 MainBlockComponent, RecentDocumentsBlockComponent, Navbar, SidebarComponent, ProfileImageBlockComponent,
99   - MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent
  101 + MembersBlockComponent, NoosferoTemplate, DateFormat, RawHTMLBlockComponent, StatisticsBlockComponent,
  102 + LoginBlockComponent
100 103 ].concat(plugins.mainComponents).concat(plugins.hotspots),
101 104  
102 105 providers: [AuthService, SessionService, NotificationService, BodyStateClassesService]
... ...
src/languages/en.json
... ... @@ -37,6 +37,7 @@
37 37 "comment.post.success.message": "Comment saved!",
38 38 "comment.reply": "reply",
39 39 "article.actions.edit": "Edit",
  40 + "article.actions.read_more": "Read More",
40 41 "article.basic_editor.title": "Title",
41 42 "article.basic_editor.body": "Body",
42 43 "article.basic_editor.save": "Save",
... ...
src/languages/pt.json
... ... @@ -37,6 +37,7 @@
37 37 "comment.post.success.message": "Comentário salvo com sucesso!",
38 38 "comment.reply": "responder",
39 39 "article.actions.edit": "Editar",
  40 + "article.actions.read_more": "Ler mais",
40 41 "article.basic_editor.title": "Título",
41 42 "article.basic_editor.body": "Corpo",
42 43 "article.basic_editor.save": "Salvar",
... ...
src/lib/ng-noosfero-api/http/block.service.spec.ts 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +import {BlockService} from "./block.service";
  2 +
  3 +
  4 +describe("Services", () => {
  5 +
  6 + describe("Block Service", () => {
  7 +
  8 + let $httpBackend: ng.IHttpBackendService;
  9 + let blockService: BlockService;
  10 +
  11 + beforeEach(angular.mock.module("noosferoApp", ($translateProvider: angular.translate.ITranslateProvider) => {
  12 + $translateProvider.translations('en', {});
  13 + }));
  14 +
  15 + beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _BlockService_: BlockService) => {
  16 + $httpBackend = _$httpBackend_;
  17 + blockService = _BlockService_;
  18 + }));
  19 +
  20 +
  21 + describe("Succesfull requests", () => {
  22 +
  23 + it("should return api content of a block", (done) => {
  24 + let blockId = 1;
  25 + $httpBackend.expectGET(`/api/v1/blocks/${blockId}`).respond(200, { block: { api_content: [{ name: "article1" }] } });
  26 + blockService.getApiContent(<noosfero.Block>{ id: blockId }).then((content: any) => {
  27 + expect(content).toEqual([{ name: "article1" }]);
  28 + done();
  29 + });
  30 + $httpBackend.flush();
  31 + });
  32 + });
  33 +
  34 +
  35 + });
  36 +});
... ...
src/lib/ng-noosfero-api/http/block.service.ts 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +import { Injectable, Inject } from "ng-forward";
  2 +import {RestangularService} from "./restangular_service";
  3 +import {ProfileService} from "./profile.service";
  4 +
  5 +@Injectable()
  6 +@Inject("Restangular", "$q", "$log")
  7 +export class BlockService extends RestangularService<noosfero.Block> {
  8 +
  9 + constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService) {
  10 + super(Restangular, $q, $log);
  11 + }
  12 +
  13 + getResourcePath() {
  14 + return "blocks";
  15 + }
  16 +
  17 + getDataKeys() {
  18 + return {
  19 + singular: 'block',
  20 + plural: 'blocks'
  21 + };
  22 + }
  23 +
  24 + getApiContent(block: noosfero.Block) {
  25 + let apiContentPromise = this.$q.defer();
  26 + if (block) {
  27 + if (block.api_content) {
  28 + apiContentPromise.resolve(block.api_content);
  29 + } else {
  30 + this.get(block.id)
  31 + .then((result: noosfero.RestResult<noosfero.Block>) => {
  32 + block = result.data;
  33 + apiContentPromise.resolve(block.api_content);
  34 + });
  35 + }
  36 + }
  37 + return apiContentPromise.promise;
  38 + }
  39 +
  40 +}
... ...
src/lib/ng-noosfero-api/interfaces/article.ts
1 1  
2 2 namespace noosfero {
3 3 export interface Article extends RestModel {
  4 + abstract: string;
4 5 path: string;
5 6 profile: Profile;
6 7 type: string;
... ...
src/lib/ng-noosfero-api/interfaces/block.ts
1 1 namespace noosfero {
2   - export interface Block {
  2 + export interface Block extends RestModel {
3 3 id: number;
4   - settings: any;
  4 + settings: Settings;
  5 + limit: number;
  6 + api_content: any;
5 7 }
6 8 }
... ...
src/lib/ng-noosfero-api/interfaces/person.ts
... ... @@ -6,6 +6,5 @@ namespace noosfero {
6 6 * A representation of a Person in Noosfero.
7 7 */
8 8 export interface Person extends Profile {
9   -
10 9 }
11 10 }
12 11 \ No newline at end of file
... ...
src/lib/ng-noosfero-api/interfaces/profile.ts
... ... @@ -22,6 +22,14 @@ namespace noosfero {
22 22 * @returns {string} The unque identifier for the Profile
23 23 */
24 24 identifier: string;
  25 +
  26 + /**
  27 + * @ngdoc property
  28 + * @name created_at
  29 + * @propertyOf noofero.Profile
  30 + * @returns {string} The timestamp this object was created
  31 + */
  32 + created_at: string;
25 33  
26 34 /**
27 35 * @ngdoc property
... ...
src/lib/ng-noosfero-api/interfaces/section.ts 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +namespace noosfero {
  2 + /**
  3 + * @ngdoc interface
  4 + * @name noosfero.Section
  5 + * @description
  6 + * Represents a block settings section. A Section has a value property,
  7 + * which represents the Section name, and an optinally checked property which
  8 + * has the same value as the value property indicating that this property is
  9 + * selected in the block configuration.
  10 + */
  11 + export interface Section {
  12 +
  13 + value: string;
  14 + checked: string;
  15 + }
  16 +}
0 17 \ No newline at end of file
... ...
src/lib/ng-noosfero-api/interfaces/settings.ts 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +namespace noosfero {
  2 + /**
  3 + * Represents a noosfero block settings.
  4 + */
  5 + export interface Settings {
  6 + sections: noosfero.Section[];
  7 + limit: number;
  8 + }
  9 +}
0 10 \ No newline at end of file
... ...
src/plugins/comment_paragraph/article/cms/discussion-editor/discussion-editor.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +import {DiscussionEditorComponent} from './discussion-editor.component';
  2 +import {ComponentTestHelper, createClass} from './../../../../../spec/component-test-helper';
  3 +
  4 +const htmlTemplate: string = '<comment-paragraph-plugin-discussion-editor [article]="ctrl.article"></comment-paragraph-plugin-discussion-editor>';
  5 +
  6 +describe("Components", () => {
  7 + describe("Discussion Editor Component", () => {
  8 +
  9 + let helper: ComponentTestHelper<DiscussionEditorComponent>;
  10 + beforeEach(angular.mock.module("templates"));
  11 +
  12 + beforeEach((done) => {
  13 + let properties = { article: {} };
  14 + let cls = createClass({
  15 + template: htmlTemplate,
  16 + directives: [DiscussionEditorComponent],
  17 + properties: properties
  18 + });
  19 + helper = new ComponentTestHelper<DiscussionEditorComponent>(cls, done);
  20 + });
  21 +
  22 + it("set start_date as article start_date when it was defined", () => {
  23 + let article = {start_date: new Date()};
  24 + helper.changeProperties({article: article});
  25 + helper.component.ngOnInit();
  26 + expect(helper.component.start_date.getTime()).toEqual(article.start_date.getTime());
  27 + });
  28 +
  29 + it("set start_date as current date when it was not defined", () => {
  30 + helper.changeProperties({article: {}});
  31 + helper.component.ngOnInit();
  32 + expect(helper.component.start_date.getTime()).toBeDefined();
  33 + });
  34 +
  35 + it("set end_date as article end_date when it was defined", () => {
  36 + let article = {end_date: new Date()};
  37 + helper.changeProperties({article: article});
  38 + helper.component.ngOnInit();
  39 + expect(helper.component.end_date.getTime()).toEqual(article.end_date.getTime());
  40 + });
  41 + });
  42 +});
... ...
src/plugins/comment_paragraph/block/discussion/discussion-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,54 @@
  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 {DiscussionBlockComponent} from './discussion-block.component';
  5 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
  6 +
  7 +const htmlTemplate: string = '<noosfero-comment-paragraph-plugin-discussion-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-comment-paragraph-plugin-discussion-block>';
  8 +
  9 +const tcb = new TestComponentBuilder();
  10 +
  11 +describe("Components", () => {
  12 + describe("Discussion Block Component", () => {
  13 +
  14 + let helper: ComponentTestHelper<DiscussionBlockComponent>;
  15 + let settingsObj = {};
  16 + let mockedBlockService = {
  17 + getApiContent: (block: noosfero.Block): any => {
  18 + return Promise.resolve({ articles: [{ name: "article1" }], headers: (name: string) => { return name; } });
  19 + }
  20 + };
  21 + let profile = { name: 'profile-name' };
  22 +
  23 + let state = jasmine.createSpyObj("state", ["go"]);
  24 +
  25 + let providers = [
  26 + new Provider('$state', { useValue: state }),
  27 + new Provider('BlockService', {
  28 + useValue: mockedBlockService
  29 + }),
  30 + ].concat(provideFilters("truncateFilter", "stripTagsFilter", "translateFilter", "amDateFormatFilter"));
  31 +
  32 + beforeEach(angular.mock.module("templates"));
  33 +
  34 + beforeEach((done) => {
  35 + let cls = createClass({
  36 + template: htmlTemplate,
  37 + directives: [DiscussionBlockComponent],
  38 + providers: providers,
  39 + properties: {}
  40 + });
  41 + helper = new ComponentTestHelper<DiscussionBlockComponent>(cls, done);
  42 + });
  43 +
  44 + it("get discussions from the block service", () => {
  45 + expect(helper.component.documents).toEqual([{ name: "article1" }]);
  46 + });
  47 +
  48 + it("go to article page when open a document", () => {
  49 + let block = helper.component;
  50 + block.openDocument({ path: "path", profile: { identifier: "identifier" } });
  51 + expect(state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "identifier" });
  52 + });
  53 + });
  54 +});
... ...
src/plugins/comment_paragraph/block/discussion/discussion-block.component.ts
1 1 import {Component, Inject, Input} from "ng-forward";
2   -import {ArticleService} from "../../../../lib/ng-noosfero-api/http/article.service";
  2 +import {BlockService} from "../../../../lib/ng-noosfero-api/http/block.service";
3 3  
4 4 @Component({
5 5 selector: "noosfero-comment-paragraph-plugin-discussion-block",
6 6 templateUrl: 'plugins/comment_paragraph/block/discussion/discussion-block.html'
7 7 })
8   -@Inject(ArticleService, "$state")
  8 +@Inject(BlockService, "$state")
9 9 export class DiscussionBlockComponent {
10 10  
11 11 @Input() block: any;
12 12 @Input() owner: any;
13 13  
14   - profile: any;
15   - documents: any;
  14 + profile: noosfero.Profile;
  15 + documents: Array<noosfero.Article>;
16 16  
17   - documentsLoaded: boolean = false;
18   -
19   - constructor(private articleService: ArticleService, private $state: any) { }
  17 + constructor(private blockService: BlockService, private $state: any) { }
20 18  
21 19 ngOnInit() {
22 20 this.profile = this.owner;
23   - this.documents = [];
24   -
25   - let limit = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 50;
26   - let params: any = { content_type: 'CommentParagraphPlugin::Discussion', per_page: limit, order: 'start_date DESC' };
27   - let now = new Date().toISOString();
28   - switch (this.block.settings['discussion_status']) {
29   - case 0:
30   - params['from_start_date'] = now;
31   - break;
32   - case 1:
33   - params['until_start_date'] = now;
34   - params['from_end_date'] = now;
35   - break;
36   - case 2:
37   - params['until_end_date'] = now;
38   - break;
39   - }
40   - console.log(this.block.settings['discussion_status']);
41   - this.articleService.getByProfile(this.profile, params)
42   - .then((result: noosfero.RestResult<noosfero.Article[]>) => {
43   - this.documents = <noosfero.Article[]>result.data;
44   - this.documentsLoaded = true;
45   - });
  21 + this.blockService.getApiContent(this.block).then((content: any) => {
  22 + this.documents = content.articles;
  23 + });
46 24 }
47 25  
48 26 openDocument(article: any) {
... ...
src/plugins/comment_paragraph/hotspot/article-content/article-content.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +import {CommentParagraphArticleContentHotspotComponent} from './article-content.component';
  2 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
  3 +
  4 +const htmlTemplate: string = '<comment-paragraph-article-content-hotspot [article]="ctrl.article"></comment-paragraph-article-content-hotspot>';
  5 +
  6 +describe("Components", () => {
  7 + describe("Article Content Hotspot Component", () => {
  8 +
  9 + let helper: ComponentTestHelper<CommentParagraphArticleContentHotspotComponent>;
  10 + beforeEach(angular.mock.module("templates"));
  11 +
  12 + beforeEach((done) => {
  13 + let properties = { article: {} };
  14 + let cls = createClass({
  15 + template: htmlTemplate,
  16 + directives: [CommentParagraphArticleContentHotspotComponent],
  17 + properties: properties
  18 + });
  19 + helper = new ComponentTestHelper<CommentParagraphArticleContentHotspotComponent>(cls, done);
  20 + });
  21 +
  22 + it("return false in isDiscussion when no type was specified", () => {
  23 + expect(helper.component.isDiscussion()).toBeFalsy();
  24 + });
  25 +
  26 + it("return false in isDiscussion when other type was specified", () => {
  27 + helper.changeProperties({article: {type: "TextArticle"}});
  28 + expect(helper.component.isDiscussion()).toBeFalsy();
  29 + });
  30 +
  31 + it("return true in isDiscussion when discussion type was specified", () => {
  32 + helper.changeProperties({article: {type: "CommentParagraphPlugin::Discussion"}});
  33 + expect(helper.component.isDiscussion()).toBeTruthy();
  34 + });
  35 + });
  36 +});
... ...
src/spec/component-test-helper.ts
... ... @@ -2,6 +2,7 @@ import { Component } from &quot;ng-forward&quot;;
2 2 import { TestComponentBuilder, ngClass } from 'ng-forward/cjs/testing/test-component-builder';
3 3 import { INgForwardJQuery } from "ng-forward/cjs/util/jqlite-extensions";
4 4 import { ComponentFixture } from 'ng-forward/cjs/testing/test-component-builder';
  5 +import * as helpers from './helpers';
5 6  
6 7 /**
7 8 * @ngdoc object
... ... @@ -87,16 +88,23 @@ export class ComponentTestHelper&lt;T extends any&gt; {
87 88 this.debugElement = fixture.debugElement;
88 89 this.component = <T>this.debugElement.componentViewChildren[0].componentInstance;
89 90 let mockObj = new this.mockComponent();
90   - Object.keys(mockObj).forEach((key: any) => {
91   - (<any>this.component)[key] = <any>mockObj[key];
92   - });
93   -
  91 + if (this.component) {
  92 + Object.keys(mockObj).forEach((key: any) => {
  93 + (<any>this.component)[key] = <any>mockObj[key];
  94 + });
  95 + }
94 96 }).then(() => {
95 97 // Force the resolution of components and sync
96 98 done();
97 99 });
98 100 }
99 101  
  102 + changeProperties(properties: any) {
  103 + Object.keys(properties).forEach((key: any) => {
  104 + this.component[key] = properties[key];
  105 + });
  106 + }
  107 +
100 108 /**
101 109 * @ngdoc method
102 110 * @name detectChanges
... ... @@ -149,6 +157,7 @@ export function createClass({
149 157 providers = <any[]>[],
150 158 properties = <any>{}
151 159 }): any {
  160 + providers = providers.concat(helpers.provideFilters("translateFilter"));
152 161 @Component({ selector: 'component-test-helper-container', template, directives, providers })
153 162 class Test {
154 163 constructor() {
... ...
src/spec/mocks.ts
... ... @@ -68,6 +68,7 @@ export var mocks: any = {
68 68 mocks.authService['logoutSuccess'].event(param);
69 69 }
70 70 },
  71 + login: () => { },
71 72 logout: () => { },
72 73 subscribe: (eventName: string, fn: Function) => {
73 74 mocks.authService[eventName].subscribe(fn);
... ...
themes/participa-consulta/app/participa-consulta.scss
... ... @@ -5,3 +5,63 @@ body &gt; .ng-scope {
5 5 html, body {
6 6 height:100%;
7 7 }
  8 +
  9 +.box-default{
  10 + border: none;
  11 + border-radius: 3px 3px 0 0;
  12 + font-family: "Ubuntu Mediun";
  13 + font-size: 15px;
  14 + font-variant: normal;
  15 + font-weight: normal;
  16 + line-height: 30px;
  17 + padding: 15px 15px 15px 55px;
  18 + text-transform: capitalize;
  19 + background-size: 30px 30px;
  20 +}
  21 +
  22 +.skin-whbl .membersblock > .panel-heading{
  23 + @extend .box-default;
  24 + background: #DAE1C4 url("../assets/images/participa-consulta/pessoas.png") no-repeat 15px center;
  25 + color: #4F9CAC;
  26 +}
  27 +
  28 +.skin-whbl .statisticsblock > .panel-heading{
  29 + @extend .box-default;
  30 + background: #69677C url("../assets/images/participa-consulta/indicadores.png") no-repeat 15px center;
  31 + color: #DAE1C4;
  32 +}
  33 +
  34 +.link-list-block{
  35 + width: 900px; //Apagar depois!!!!
  36 + font-family: "Ubuntu";
  37 + font-size: 12px;
  38 + background: #69677C;
  39 + border-radius: 0 0 3px 3px;
  40 + margin-bottom: 30px;
  41 + padding: 20px;
  42 + margin: 0 0 30px 0;
  43 + position: relative;
  44 +}
  45 +
  46 +.link-list-block div, .link-list-block a{
  47 + display: inline-block;
  48 + background: #69677C;
  49 + border-radius: 0;
  50 + color: #FFF;
  51 + font-family: "Ubuntu Mediun";
  52 + font-size: 16px;
  53 + margin: 0;
  54 + padding: 0;
  55 + font-weight: bold;
  56 +}
  57 +
  58 +.link-list-block div:first-child{
  59 + padding: 15px 15px 15px 55px;
  60 + background: #69677C url("../assets/images/participa-consulta/home.png") no-repeat 15px center;
  61 + color: #FFF;
  62 + background-size: 30px 30px;
  63 +}
  64 +
  65 +.profile-header{
  66 + text-align: center;
  67 +}
... ...
themes/participa-consulta/assets/images/participa-consulta/home.png 0 → 100644

777 Bytes

themes/participa-consulta/assets/images/participa-consulta/indicadores.png 0 → 100644

1004 Bytes

themes/participa-consulta/assets/images/participa-consulta/pessoas.png 0 → 100644

1.27 KB