Commit b9abd2b869a1c1b804344ed3d057ba7682ad51e4

Authored by Leandro Santos
2 parents 960cb3cb 5e8f7a89

Merge branch 'master' into staging

Showing 193 changed files with 4023 additions and 1233 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 193 files displayed.

bower.json
... ... @@ -35,6 +35,8 @@
35 35 "angular-i18n": "^1.5.0",
36 36 "angular-load": "^0.4.1",
37 37 "angular-translate-interpolation-messageformat": "^2.10.0",
  38 + "angular-bind-html-compile": "^1.2.1",
  39 + "angular-click-outside": "^2.7.1",
38 40 "ng-ckeditor": "^0.2.1",
39 41 "ckeditor": "^4.5.8"
40 42 },
... ...
gulp/build.js
... ... @@ -6,6 +6,7 @@ var rename = require('gulp-rename');
6 6 var insert = require('gulp-insert');
7 7 var merge = require('merge-stream');
8 8 var conf = require('./conf');
  9 +var languages = require('./languages');
9 10  
10 11 var themeName = conf.paths.theme.replace('-', ' ');
11 12 themeName = themeName.charAt(0).toUpperCase() + themeName.slice(1);
... ... @@ -16,25 +17,31 @@ var $ = require('gulp-load-plugins')({
16 17 });
17 18  
18 19 gulp.task('partials', function () {
19   - var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')];
20   - conf.paths.allSources.forEach(function(src) {
21   - srcPaths.push(path.join(src, '/app/**/*.html'));
  20 + var merged = merge();
  21 + ['app', conf.paths.plugins].forEach(function(partialPath) {
  22 + var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')];
  23 + conf.paths.allSources.forEach(function(src) {
  24 + srcPaths.push(path.join(src, partialPath, '/**/*.html'));
  25 + });
  26 + merged.add(gulp.src(srcPaths)
  27 + .pipe($.minifyHtml({
  28 + empty: true,
  29 + spare: true,
  30 + quotes: true
  31 + }))
  32 + .pipe($.angularTemplatecache('templateCacheHtml-'+partialPath+'.js', {
  33 + module: 'noosferoApp',
  34 + root: partialPath
  35 + }))
  36 + .pipe(gulp.dest(conf.paths.tmp + '/partials/')));
22 37 });
23   - return gulp.src(srcPaths)
24   - .pipe($.minifyHtml({
25   - empty: true,
26   - spare: true,
27   - quotes: true
28   - }))
29   - .pipe($.angularTemplatecache('templateCacheHtml.js', {
30   - module: 'noosferoApp',
31   - root: 'app'
32   - }))
33   - .pipe(gulp.dest(conf.paths.tmp + '/partials/'));
  38 + return merged;
34 39 });
35 40  
36 41 gulp.task('html', ['inject', 'partials'], function () {
37   - var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false });
  42 + var partialsInjectFile = gulp.src([
  43 + path.join(conf.paths.tmp, '/partials/templateCacheHtml-app.js'),
  44 + path.join(conf.paths.tmp, '/partials/templateCacheHtml-plugins.js')], { read: false });
38 45 var partialsInjectOptions = {
39 46 starttag: '<!-- inject:partials -->',
40 47 ignorePath: path.join(conf.paths.tmp, '/partials'),
... ... @@ -124,6 +131,10 @@ gulp.task(&#39;clean-docs&#39;, [], function() {
124 131 return $.del([path.join(conf.paths.docs, '/')]);
125 132 });
126 133  
  134 +gulp.task('plugin-languages', ['locale'], function() {
  135 + return languages.pluginLanguages(conf.paths.dist);
  136 +});
  137 +
127 138 gulp.task('noosfero', ['html'], function () {
128 139 var layouts = gulp.src('layouts/**/*')
129 140 .pipe(gulp.dest(path.join(conf.paths.dist, "layouts")));
... ... @@ -136,4 +147,4 @@ gulp.task(&#39;noosfero&#39;, [&#39;html&#39;], function () {
136 147 return merge(layouts, theme, index);
137 148 });
138 149  
139   -gulp.task('build', ['html', 'fonts', 'other', 'locale', 'noosfero']);
  150 +gulp.task('build', ['html', 'fonts', 'other', 'locale', 'plugin-languages', 'noosfero']);
... ...
gulp/conf.js
... ... @@ -15,11 +15,13 @@ var path = require(&#39;path&#39;);
15 15 */
16 16 exports.paths = {
17 17 src: 'src',
  18 + plugins: 'plugins',
18 19 dist: 'dist',
19 20 tmp: '.tmp',
20 21 e2e: 'e2e',
21 22 docs: 'docs',
22   - themes: 'themes'
  23 + themes: 'themes',
  24 + languages: 'languages'
23 25 };
24 26 exports.configTheme = function(theme) {
25 27 exports.paths.theme = theme || "angular-default";
... ...
gulp/languages.js 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +'use strict';
  2 +
  3 +var path = require('path');
  4 +var gulp = require('gulp');
  5 +var merge = require('merge-stream');
  6 +var conf = require('./conf');
  7 +var mergeJson = require('gulp-merge-json');
  8 +var glob = require("glob");
  9 +
  10 +exports.pluginLanguages = function(dest) {
  11 + var merged = merge();
  12 + glob(path.join(conf.paths.src, conf.paths.languages, "*.json"), function (er, files) {
  13 + files.forEach(function(file) {
  14 + merged.add(exports.pluginLanguage(file, dest));
  15 + });
  16 + });
  17 + return merged;
  18 +}
  19 +
  20 +exports.pluginLanguage = function(file, dest) {
  21 + var language = file.split('/').pop().replace('\.json','');
  22 + return gulp.src(path.join(conf.paths.src, '**', conf.paths.languages, language+'.json'))
  23 + .pipe(mergeJson(path.join(conf.paths.languages, language+'.json')))
  24 + .pipe(gulp.dest(dest))
  25 +}
  26 +
  27 +gulp.task('serve-languages', function() {
  28 + return exports.pluginLanguages(path.join(conf.paths.tmp, '/serve'));
  29 +});
... ...
gulp/server.js
... ... @@ -45,7 +45,7 @@ browserSync.use(browserSyncSpa({
45 45 selector: '[ng-app]'// Only needed for angular apps
46 46 }));
47 47  
48   -gulp.task('serve', ['watch'], function () {
  48 +gulp.task('serve', ['serve-languages', 'watch'], function () {
49 49 var srcPaths = [path.join(conf.paths.tmp, '/serve')];
50 50 conf.paths.allSources.reverse().forEach(function(src) {
51 51 srcPaths.push(src);
... ...
gulp/styles.js
... ... @@ -31,6 +31,7 @@ var buildStyles = function() {
31 31 ];
32 32 conf.paths.allSources.forEach(function(src) {
33 33 srcPaths.push(path.join(src, '/app/**/*.scss'));
  34 + srcPaths.push(path.join(src, conf.paths.plugins, '/**/*.scss'));
34 35 });
35 36 var injectFiles = gulp.src(srcPaths, { read: false });
36 37  
... ...
gulp/watch.js
... ... @@ -3,6 +3,7 @@
3 3 var path = require('path');
4 4 var gulp = require('gulp');
5 5 var conf = require('./conf');
  6 +var languages = require('./languages');
6 7  
7 8 var browserSync = require('browser-sync');
8 9  
... ... @@ -16,7 +17,8 @@ gulp.task(&#39;watch&#39;, [&#39;inject&#39;], function () {
16 17  
17 18 gulp.watch([
18 19 path.join(conf.paths.src, '/app/**/*.css'),
19   - path.join(conf.paths.src, '/app/**/*.scss')
  20 + path.join(conf.paths.src, '/app/**/*.scss'),
  21 + path.join(conf.paths.src, conf.paths.plugins, '/**/*.scss')
20 22 ], function(event) {
21 23 if(isOnlyChange(event)) {
22 24 gulp.start('styles-reload');
... ... @@ -33,9 +35,14 @@ gulp.task(&#39;watch&#39;, [&#39;inject&#39;], function () {
33 35 }
34 36 });
35 37  
  38 + gulp.watch(path.join(conf.paths.src, '**', conf.paths.languages, '*.json'), function(event) {
  39 + languages.pluginLanguage(event.path, path.join(conf.paths.tmp, '/serve'));
  40 + });
  41 +
36 42 var watchPaths = [];
37 43 conf.paths.allSources.forEach(function(src) {
38 44 watchPaths.push(path.join(src, '/app/**/*.html'));
  45 + watchPaths.push(path.join(src, conf.paths.plugins, '/**/*.html'));
39 46 });
40 47 gulp.watch(watchPaths, function(event) {
41 48 browserSync.reload(event.path);
... ...
karma.conf.js
... ... @@ -49,7 +49,7 @@ var _ = require(&#39;lodash&#39;);
49 49 var wiredep = require('wiredep');
50 50  
51 51 var pathSrcHtml = [
52   - path.join('./src/app/**/*.html')
  52 + path.join('./src/**/*.html')
53 53 ];
54 54  
55 55 var glob = require("glob");
... ...
package.json
... ... @@ -49,6 +49,7 @@
49 49 "gulp-insert": "^0.5.0",
50 50 "gulp-inject": "~3.0.0",
51 51 "gulp-load-plugins": "~0.10.0",
  52 + "gulp-merge-json": "^0.4.0",
52 53 "gulp-minify-css": "~1.2.1",
53 54 "gulp-minify-html": "~1.0.4",
54 55 "gulp-ng-annotate": "~1.1.0",
... ...
src/app/article/article-default-view.component.ts
1 1 import { bundle, Input, Inject, Component, Directive } from 'ng-forward';
2 2 import {ArticleBlogComponent} from "./types/blog/blog.component";
3 3 import {CommentsComponent} from "./comment/comments.component";
  4 +import {MacroDirective} from "./macro/macro.directive";
  5 +import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot.component";
  6 +import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot.component";
4 7  
5 8 /**
6 9 * @ngdoc controller
... ... @@ -30,7 +33,9 @@ export class ArticleDefaultViewComponent {
30 33 @Component({
31 34 selector: 'noosfero-article',
32 35 template: 'not-used',
33   - directives: [ArticleDefaultViewComponent, ArticleBlogComponent, CommentsComponent]
  36 + directives: [ArticleDefaultViewComponent, ArticleBlogComponent,
  37 + CommentsComponent, MacroDirective, ArticleToolbarHotspotComponent,
  38 + ArticleContentHotspotComponent]
34 39 })
35 40 @Inject("$element", "$scope", "$injector", "$compile")
36 41 export class ArticleViewComponent {
... ... @@ -40,7 +45,8 @@ export class ArticleViewComponent {
40 45 directiveName: string;
41 46  
42 47 ngOnInit() {
43   - let specificDirective = 'noosfero' + this.article.type;
  48 + let articleType = this.article.type.replace(/::/, '');
  49 + let specificDirective = 'noosfero' + articleType;
44 50 this.directiveName = "noosfero-default-article";
45 51 if (this.$injector.has(specificDirective + 'Directive')) {
46 52 this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
... ... @@ -53,6 +59,5 @@ export class ArticleViewComponent {
53 59 private $scope: ng.IScope,
54 60 private $injector: ng.auto.IInjectorService,
55 61 private $compile: ng.ICompileService) {
56   -
57 62 }
58 63 }
... ...
src/app/article/article.html
... ... @@ -7,6 +7,7 @@
7 7 <a href="#" class="btn btn-default btn-xs" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})">
8 8 <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}}
9 9 </a>
  10 + <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar>
10 11 <div class="page-info pull-right small text-muted">
11 12 <span class="time">
12 13 <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span>
... ... @@ -19,9 +20,9 @@
19 20 </span>
20 21 </div>
21 22 </div>
22   -
  23 + <noosfero-hotspot-article-content [article]="ctrl.article"></noosfero-hotspot-article-content>
23 24 <div class="page-body">
24   - <div ng-bind-html="ctrl.article.body"></div>
  25 + <div bind-html-compile="ctrl.article.body"></div>
25 26 </div>
26 27  
27 28 <noosfero-comments [article]="ctrl.article"></noosfero-comments>
... ...
src/app/article/basic-editor/basic-editor.component.spec.ts
... ... @@ -1,84 +0,0 @@
1   -import {quickCreateComponent} from "../../../spec/helpers";
2   -import {BasicEditorComponent} from "./basic-editor.component";
3   -
4   -
5   -describe("Article BasicEditor", () => {
6   -
7   - let $rootScope: ng.IRootScopeService;
8   - let $q: ng.IQService;
9   - let articleServiceMock: any;
10   - let profileServiceMock: any;
11   - let $state: any;
12   - let $stateParams: any;
13   - let $window: any;
14   - let profile = { id: 1 };
15   - let notification: any;
16   -
17   -
18   - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => {
19   - $rootScope = _$rootScope_;
20   - $q = _$q_;
21   - }));
22   -
23   - beforeEach(() => {
24   - $window = jasmine.createSpyObj("$window", ["back"]);
25   - $state = jasmine.createSpyObj("$state", ["go"]);
26   - notification = jasmine.createSpyObj("notification", ["success"]);
27   - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["setCurrentProfileByIdentifier"]);
28   - articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInParent", "updateArticle", "get"]);
29   -
30   - $stateParams = { profile: "profile" };
31   -
32   - let setCurrentProfileByIdentifierResponse = $q.defer();
33   - setCurrentProfileByIdentifierResponse.resolve(profile);
34   -
35   - let articleCreate = $q.defer();
36   - articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } });
37   -
38   - let articleGet = $q.defer();
39   - articleGet.resolve({ data: { path: "parent-path", profile: { identifier: "profile" } } });
40   -
41   - profileServiceMock.setCurrentProfileByIdentifier = jasmine.createSpy("setCurrentProfileByIdentifier").and.returnValue(setCurrentProfileByIdentifierResponse.promise);
42   - articleServiceMock.createInParent = jasmine.createSpy("createInParent").and.returnValue(articleCreate.promise);
43   - articleServiceMock.updateArticle = jasmine.createSpy("updateArticle").and.returnValue(articleCreate.promise);
44   - articleServiceMock.get = jasmine.createSpy("get").and.returnValue(articleGet.promise);
45   - });
46   -
47   - it("create an article in the current profile when save", done => {
48   - $stateParams['parent_id'] = 1;
49   - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
50   - component.save();
51   - $rootScope.$apply();
52   - expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled();
53   - expect(articleServiceMock.createInParent).toHaveBeenCalledWith(1, component.article);
54   - done();
55   - });
56   -
57   - it("got to the new article page and display an alert when saving sucessfully", done => {
58   - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
59   - component.save();
60   - $rootScope.$apply();
61   - expect($state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" });
62   - expect(notification.success).toHaveBeenCalled();
63   - done();
64   - });
65   -
66   - it("go back when cancel article edition", done => {
67   - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
68   - $window.history = { back: jasmine.createSpy('back') };
69   - component.cancel();
70   - expect($window.history.back).toHaveBeenCalled();
71   - done();
72   - });
73   -
74   - it("edit existing article when save", done => {
75   - $stateParams['parent_id'] = null;
76   - $stateParams['id'] = 2;
77   - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
78   - component.save();
79   - $rootScope.$apply();
80   - expect(articleServiceMock.updateArticle).toHaveBeenCalledWith(component.article);
81   - done();
82   - });
83   -
84   -});
src/app/article/basic-editor/basic-editor.component.ts
... ... @@ -1,69 +0,0 @@
1   -import {StateConfig, Component, Inject, provide} from 'ng-forward';
2   -import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service";
3   -import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service";
4   -import {NotificationService} from "../../shared/services/notification.service.ts";
5   -
6   -@Component({
7   - selector: 'article-basic-editor',
8   - templateUrl: "app/article/basic-editor/basic-editor.html",
9   - providers: [
10   - provide('articleService', { useClass: ArticleService }),
11   - provide('profileService', { useClass: ProfileService }),
12   - provide('notification', { useClass: NotificationService })
13   - ]
14   -})
15   -@Inject(ArticleService, ProfileService, "$state", NotificationService, "$stateParams", "$window")
16   -export class BasicEditorComponent {
17   -
18   - article: noosfero.Article = <noosfero.Article>{ type: "TextArticle" };
19   - parent: noosfero.Article = <noosfero.Article>{};
20   -
21   - id: number;
22   - parentId: number;
23   - profileIdentifier: string;
24   -
25   - constructor(private articleService: ArticleService,
26   - private profileService: ProfileService,
27   - private $state: ng.ui.IStateService,
28   - private notification: NotificationService,
29   - private $stateParams: ng.ui.IStateParamsService,
30   - private $window: ng.IWindowService) {
31   -
32   - this.parentId = this.$stateParams['parent_id'];
33   - this.profileIdentifier = this.$stateParams["profile"];
34   - this.id = this.$stateParams['id'];
35   -
36   - if (this.parentId) {
37   - this.articleService.get(this.parentId).then((result: noosfero.RestResult<noosfero.Article>) => {
38   - this.parent = result.data;
39   - });
40   - }
41   - if (this.id) {
42   - this.articleService.get(this.id).then((result: noosfero.RestResult<noosfero.Article>) => {
43   - this.article = result.data;
44   - this.article.name = this.article.title; // FIXME
45   - });
46   - }
47   - }
48   -
49   - save() {
50   - this.profileService.setCurrentProfileByIdentifier(this.profileIdentifier).then((profile: noosfero.Profile) => {
51   - if (this.id) {
52   - return this.articleService.updateArticle(this.article);
53   - } else {
54   - return this.articleService.createInParent(this.parentId, this.article);
55   - }
56   - }).then((response: noosfero.RestResult<noosfero.Article>) => {
57   - let article = (<noosfero.Article>response.data);
58   - this.$state.go('main.profile.page', { page: article.path, profile: article.profile.identifier });
59   - this.notification.success({ title: "article.basic_editor.success.title", message: "article.basic_editor.success.message" });
60   - }).catch(() => {
61   - this.notification.error({ message: "article.basic_editor.save.failed" });
62   - });
63   - }
64   -
65   - cancel() {
66   - this.$window.history.back();
67   - }
68   -
69   -}
src/app/article/basic-editor/basic-editor.html
... ... @@ -1,34 +0,0 @@
1   -<div class="basic-editor">
2   - <div class="row">
3   - <div class="col-md-1"></div>
4   - <div class="col-md-8">
5   - <form>
6   - <div class="form-group">
7   - <label for="titleInput">{{"article.basic_editor.title" | translate}}</label>
8   - <input type="text" class="form-control" id="titleInput" placeholder="{{'article.basic_editor.title' | translate}}" ng-model="vm.article.name">
9   - </div>
10   - <div class="form-group">
11   - <label for="bodyInput">{{"article.basic_editor.body" | translate}}</label>
12   - <html-editor [(value)]="vm.article.body"></html-editor>
13   - </div>
14   - <button type="submit" class="btn btn-default" ng-click="vm.save()">{{"article.basic_editor.save" | translate}}</button>
15   - <button type="button" class="btn btn-danger" ng-click="vm.cancel()">{{"article.basic_editor.cancel" | translate}}</button>
16   - </form>
17   - </div>
18   - <div class="side-options">
19   - <div class="visibility panel panel-default">
20   - <div class="panel-heading">{{"article.basic_editor.visibility" | translate}}</div>
21   - <div class="panel-body">
22   - <div>
23   - <input type="radio" ng-model="vm.article.published" ng-value="true">
24   - <i class="fa fa-unlock fa-fw"></i> {{"article.basic_editor.visibility.public" | translate}}
25   - </div>
26   - <div>
27   - <input type="radio" ng-model="vm.article.published" ng-value="false">
28   - <i class="fa fa-lock fa-fw"></i> {{"article.basic_editor.visibility.private" | translate}}
29   - </div>
30   - </div>
31   - </div>
32   - </div>
33   - </div>
34   -</div>
src/app/article/basic-editor/basic-editor.scss
... ... @@ -1,21 +0,0 @@
1   -.basic-editor {
2   - @extend .container-fluid;
3   - padding: 0 1%;
4   -
5   - .side-options {
6   - @extend .col-md-3;
7   - margin-top: 25px;
8   -
9   - .visibility {
10   - .panel-heading {
11   - background-color: transparent;
12   - font-weight: bold;
13   - }
14   - .panel-body {
15   - i {
16   - color: #A5A5A5;
17   - }
18   - }
19   - }
20   - }
21   -}
src/app/article/cms/article-editor/article-editor.component.ts 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +import {Component, Input, Inject} from 'ng-forward';
  2 +
  3 +@Component({
  4 + selector: 'article-editor',
  5 + template: "not-used"
  6 +})
  7 +@Inject("$element", "$scope", "$injector", "$compile")
  8 +export class ArticleEditorComponent {
  9 +
  10 + @Input() article: noosfero.Article;
  11 +
  12 + constructor(
  13 + private $element: any,
  14 + private $scope: ng.IScope,
  15 + private $injector: ng.auto.IInjectorService,
  16 + private $compile: ng.ICompileService) { }
  17 +
  18 + ngOnInit() {
  19 + let articleType = this.article.type.replace(/::/, '');
  20 + let specificDirective = `${articleType.charAt(0).toLowerCase()}${articleType.substring(1)}Editor`;
  21 + let directiveName = "article-basic-editor";
  22 + if (this.$injector.has(specificDirective + 'Directive')) {
  23 + directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
  24 + }
  25 + this.$element.replaceWith(this.$compile('<' + directiveName + ' [article]="ctrl.article"></' + directiveName + '>')(this.$scope));
  26 + }
  27 +}
... ...
src/app/article/cms/basic-editor/basic-editor.component.ts 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +import {Component, Input} from 'ng-forward';
  2 +
  3 +@Component({
  4 + selector: 'article-basic-editor',
  5 + templateUrl: "app/article/cms/basic-editor/basic-editor.html"
  6 +})
  7 +export class BasicEditorComponent {
  8 +
  9 + @Input() article: noosfero.Article;
  10 +}
... ...
src/app/article/cms/basic-editor/basic-editor.html 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<form>
  2 + <div class="form-group">
  3 + <label for="titleInput">{{"article.basic_editor.title" | translate}}</label>
  4 + <input type="text" class="form-control" id="titleInput" placeholder="{{'article.basic_editor.title' | translate}}" ng-model="ctrl.article.name">
  5 + </div>
  6 + <div class="form-group">
  7 + <label for="bodyInput">{{"article.basic_editor.body" | translate}}</label>
  8 + <html-editor [(value)]="ctrl.article.body"></html-editor>
  9 + </div>
  10 +</form>
... ...
src/app/article/cms/basic-editor/basic-editor.scss 0 → 100644
src/app/article/cms/basic-options/basic-options.component.ts 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +import {Component, Input} from 'ng-forward';
  2 +
  3 +@Component({
  4 + selector: 'article-basic-options',
  5 + templateUrl: "app/article/cms/basic-options/basic-options.html"
  6 +})
  7 +export class BasicOptionsComponent {
  8 +
  9 + @Input() article: noosfero.Article;
  10 +
  11 +}
... ...
src/app/article/cms/basic-options/basic-options.html 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +<div class="side-options">
  2 + <div class="visibility panel panel-default">
  3 + <div class="panel-heading">{{"article.basic_editor.visibility" | translate}}</div>
  4 + <div class="panel-body">
  5 + <div>
  6 + <input type="radio" ng-model="ctrl.article.published" ng-value="true">
  7 + <i class="fa fa-unlock fa-fw"></i> {{"article.basic_editor.visibility.public" | translate}}
  8 + </div>
  9 + <div>
  10 + <input type="radio" ng-model="ctrl.article.published" ng-value="false">
  11 + <i class="fa fa-lock fa-fw"></i> {{"article.basic_editor.visibility.private" | translate}}
  12 + </div>
  13 + </div>
  14 + </div>
  15 +</div>
... ...
src/app/article/cms/basic-options/basic-options.scss 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +.side-options {
  2 + margin-top: 25px;
  3 +
  4 + .visibility {
  5 + .panel-heading {
  6 + background-color: transparent;
  7 + font-weight: bold;
  8 + }
  9 + .panel-body {
  10 + i {
  11 + color: #A5A5A5;
  12 + }
  13 + }
  14 + }
  15 +}
... ...
src/app/article/cms/cms.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,84 @@
  1 +import {quickCreateComponent} from "../../../spec/helpers";
  2 +import {CmsComponent} from "./cms.component";
  3 +
  4 +
  5 +describe("Article Cms", () => {
  6 +
  7 + let $rootScope: ng.IRootScopeService;
  8 + let $q: ng.IQService;
  9 + let articleServiceMock: any;
  10 + let profileServiceMock: any;
  11 + let $state: any;
  12 + let $stateParams: any;
  13 + let $window: any;
  14 + let profile = { id: 1 };
  15 + let notification: any;
  16 +
  17 +
  18 + beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => {
  19 + $rootScope = _$rootScope_;
  20 + $q = _$q_;
  21 + }));
  22 +
  23 + beforeEach(() => {
  24 + $window = jasmine.createSpyObj("$window", ["back"]);
  25 + $state = jasmine.createSpyObj("$state", ["go"]);
  26 + notification = jasmine.createSpyObj("notification", ["success"]);
  27 + profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["setCurrentProfileByIdentifier"]);
  28 + articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInParent", "updateArticle", "get"]);
  29 +
  30 + $stateParams = { profile: "profile" };
  31 +
  32 + let setCurrentProfileByIdentifierResponse = $q.defer();
  33 + setCurrentProfileByIdentifierResponse.resolve(profile);
  34 +
  35 + let articleCreate = $q.defer();
  36 + articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } });
  37 +
  38 + let articleGet = $q.defer();
  39 + articleGet.resolve({ data: { path: "parent-path", profile: { identifier: "profile" } } });
  40 +
  41 + profileServiceMock.setCurrentProfileByIdentifier = jasmine.createSpy("setCurrentProfileByIdentifier").and.returnValue(setCurrentProfileByIdentifierResponse.promise);
  42 + articleServiceMock.createInParent = jasmine.createSpy("createInParent").and.returnValue(articleCreate.promise);
  43 + articleServiceMock.updateArticle = jasmine.createSpy("updateArticle").and.returnValue(articleCreate.promise);
  44 + articleServiceMock.get = jasmine.createSpy("get").and.returnValue(articleGet.promise);
  45 + });
  46 +
  47 + it("create an article in the current profile when save", done => {
  48 + $stateParams['parent_id'] = 1;
  49 + let component: CmsComponent = new CmsComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
  50 + component.save();
  51 + $rootScope.$apply();
  52 + expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled();
  53 + expect(articleServiceMock.createInParent).toHaveBeenCalledWith(1, component.article);
  54 + done();
  55 + });
  56 +
  57 + it("got to the new article page and display an alert when saving sucessfully", done => {
  58 + let component: CmsComponent = new CmsComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
  59 + component.save();
  60 + $rootScope.$apply();
  61 + expect($state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" });
  62 + expect(notification.success).toHaveBeenCalled();
  63 + done();
  64 + });
  65 +
  66 + it("go back when cancel article edition", done => {
  67 + let component: CmsComponent = new CmsComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
  68 + $window.history = { back: jasmine.createSpy('back') };
  69 + component.cancel();
  70 + expect($window.history.back).toHaveBeenCalled();
  71 + done();
  72 + });
  73 +
  74 + it("edit existing article when save", done => {
  75 + $stateParams['parent_id'] = null;
  76 + $stateParams['id'] = 2;
  77 + let component: CmsComponent = new CmsComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window);
  78 + component.save();
  79 + $rootScope.$apply();
  80 + expect(articleServiceMock.updateArticle).toHaveBeenCalledWith(component.article);
  81 + done();
  82 + });
  83 +
  84 +});
... ...
src/app/article/cms/cms.component.ts 0 → 100644
... ... @@ -0,0 +1,77 @@
  1 +import {StateConfig, Component, Inject, provide} from 'ng-forward';
  2 +import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service";
  3 +import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service";
  4 +import {NotificationService} from "../../shared/services/notification.service.ts";
  5 +import {BasicOptionsComponent} from './basic-options/basic-options.component';
  6 +import {BasicEditorComponent} from './basic-editor/basic-editor.component';
  7 +import {ArticleEditorComponent} from './article-editor/article-editor.component';
  8 +
  9 +@Component({
  10 + selector: 'article-cms',
  11 + templateUrl: "app/article/cms/cms.html",
  12 + providers: [
  13 + provide('articleService', { useClass: ArticleService }),
  14 + provide('profileService', { useClass: ProfileService }),
  15 + provide('notification', { useClass: NotificationService })
  16 + ],
  17 + directives: [ArticleEditorComponent, BasicOptionsComponent, BasicEditorComponent]
  18 +})
  19 +@Inject(ArticleService, ProfileService, "$state", NotificationService, "$stateParams", "$window")
  20 +export class CmsComponent {
  21 +
  22 + article: noosfero.Article;
  23 + parent: noosfero.Article = <noosfero.Article>{};
  24 +
  25 + id: number;
  26 + parentId: number;
  27 + profileIdentifier: string;
  28 +
  29 + constructor(private articleService: ArticleService,
  30 + private profileService: ProfileService,
  31 + private $state: ng.ui.IStateService,
  32 + private notification: NotificationService,
  33 + private $stateParams: ng.ui.IStateParamsService,
  34 + private $window: ng.IWindowService) {
  35 +
  36 + this.parentId = this.$stateParams['parent_id'];
  37 + this.profileIdentifier = this.$stateParams["profile"];
  38 + this.id = this.$stateParams['id'];
  39 +
  40 + if (this.parentId) {
  41 + this.articleService.get(this.parentId).then((result: noosfero.RestResult<noosfero.Article>) => {
  42 + this.parent = result.data;
  43 + });
  44 + }
  45 + if (this.id) {
  46 + this.articleService.get(this.id).then((result: noosfero.RestResult<noosfero.Article>) => {
  47 + this.article = result.data;
  48 + this.article.name = this.article.title; // FIXME
  49 + });
  50 + } else {
  51 + this.article = <noosfero.Article>{ type: this.$stateParams['type'] || "TextArticle", published: true };
  52 + }
  53 + }
  54 +
  55 + save() {
  56 + this.profileService.setCurrentProfileByIdentifier(this.profileIdentifier).then((profile: noosfero.Profile) => {
  57 + if (this.id) {
  58 + return this.articleService.updateArticle(this.article);
  59 + } else if (this.parentId) {
  60 + return this.articleService.createInParent(this.parentId, this.article);
  61 + } else {
  62 + return this.articleService.createInProfile(profile, this.article);
  63 + }
  64 + }).then((response: noosfero.RestResult<noosfero.Article>) => {
  65 + let article = (<noosfero.Article>response.data);
  66 + this.$state.go('main.profile.page', { page: article.path, profile: article.profile.identifier });
  67 + this.notification.success({ title: "article.basic_editor.success.title", message: "article.basic_editor.success.message" });
  68 + }).catch(() => {
  69 + this.notification.error({ message: "article.basic_editor.save.failed" });
  70 + });
  71 + }
  72 +
  73 + cancel() {
  74 + this.$window.history.back();
  75 + }
  76 +
  77 +}
... ...
src/app/article/cms/cms.html 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<div class="cms">
  2 + <div class="row">
  3 + <div class="col-md-1"></div>
  4 + <div class="col-md-8">
  5 + <article-editor ng-if="vm.article" [article]="vm.article"></article-editor>
  6 + </div>
  7 + <div class="col-md-3">
  8 + <article-basic-options ng-if="vm.article" [article]="vm.article"></article-basic-options>
  9 + </div>
  10 + </div>
  11 + <div class="row">
  12 + <div class="col-md-1"></div>
  13 + <div class="col-md-8">
  14 + <button type="submit" class="btn btn-default" ng-click="vm.save()">{{"article.basic_editor.save" | translate}}</button>
  15 + <button type="button" class="btn btn-danger" ng-click="vm.cancel()">{{"article.basic_editor.cancel" | translate}}</button>
  16 + </div>
  17 + </div>
  18 +</div>
... ...
src/app/article/cms/cms.scss 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +.cms {
  2 + @extend .container-fluid;
  3 + padding: 0 1%;
  4 +}
... ...
src/app/article/comment/comment-reply-tooltip.html 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<div class="reply-tooltip">
  2 + <noosfero-comment [comment]="ctrl.comment.reply_of" [article]="ctrl.article" [display-actions]="false" [display-replies]="false"></noosfero-comment>
  3 +</div>
... ...
src/app/article/comment/comment.component.ts
... ... @@ -9,6 +9,8 @@ export class CommentComponent {
9 9  
10 10 @Input() comment: noosfero.CommentViewModel;
11 11 @Input() article: noosfero.Article;
  12 + @Input() displayActions = true;
  13 + @Input() displayReplies = true;
12 14  
13 15 showReply() {
14 16 return this.comment && this.comment.__show_reply === true;
... ...
src/app/article/comment/comment.html
... ... @@ -9,13 +9,19 @@
9 9 <a class="pull-left" ui-sref="main.profile.home({profile: ctrl.comment.author.identifier})">
10 10 <h4 class="media-heading">{{ctrl.comment.author.name}}</h4>
11 11 </a>
  12 + <span class="reply-of" ng-if="ctrl.comment.reply_of" uib-tooltip-template="'app/article/comment/comment-reply-tooltip.html'">
  13 + <i class="fa fa-fw fa-mail-forward"></i>
  14 + <span class="author">{{ctrl.comment.reply_of.author.name}}</span>
  15 + </span>
12 16 <span class="date" am-time-ago="ctrl.comment.created_at | dateFormat"></span>
13 17 </div>
14 18 <div class="title">{{ctrl.comment.title}}</div>
15 19 <div class="body">{{ctrl.comment.body}}</div>
16   - <a href="#" (click)="ctrl.reply()" class="small text-muted">
17   - {{"comment.reply" | translate}}
18   - </a>
  20 + <div class="actions" ng-if="ctrl.displayActions">
  21 + <a href="#" (click)="ctrl.reply()" class="small text-muted" ng-if="ctrl.article.accept_comments">
  22 + {{"comment.reply" | translate}}
  23 + </a>
  24 + </div>
19 25 </div>
20   - <noosfero-comments [show-form]="ctrl.showReply()" [article]="ctrl.article" [parent]="ctrl.comment"></noosfero-comments>
  26 + <noosfero-comments [show-form]="ctrl.showReply()" [article]="ctrl.article" [parent]="ctrl.comment" ng-if="ctrl.displayReplies"></noosfero-comments>
21 27 </div>
... ...
src/app/article/comment/comment.scss
... ... @@ -5,6 +5,7 @@
5 5 @extend .text-muted;
6 6 @extend .small;
7 7 margin-left: 8px;
  8 + font-size: 12px;
8 9 }
9 10 .title {
10 11 font-weight: bold;
... ... @@ -13,7 +14,18 @@
13 14 min-width: 40px;
14 15 }
15 16 .media-body {
16   - padding: 0 10px 10px 10px;
  17 + padding: 0 10px 10px 0;
  18 + .reply-of {
  19 + font-size: 12px;
  20 + color: #B5B5B5;
  21 + margin-left: 5px;
  22 + i {
  23 + font-size: 10px;
  24 + }
  25 + }
  26 + h4 {
  27 + font-size: 16px;
  28 + }
17 29 }
18 30 noosfero-profile-image {
19 31 img {
... ... @@ -27,8 +39,24 @@
27 39 font-size: 1.7em;
28 40 }
29 41 }
  42 + // Limit identation of replies
30 43 .comments {
31 44 margin-left: 30px;
  45 + .comments .comments {
  46 + margin-left: 0px;
  47 + .comment {
  48 + margin-left: 0px;
  49 + }
  50 + }
  51 + }
  52 + .tooltip-inner {
  53 + max-width: 350px;
  54 + text-align: left;
  55 + .reply-tooltip {
  56 + .comment {
  57 + margin: 5px;
  58 + }
  59 + }
32 60 }
33 61 }
34 62 }
... ...
src/app/article/comment/comments.component.ts
1   -import { Inject, Input, Output, Component, provide, EventEmitter } from 'ng-forward';
2   -import {INgForwardJQuery} from "ng-forward/cjs/util/jqlite-extensions";
3   -
4   -
  1 +import { Inject, Input, Component, provide } from 'ng-forward';
5 2 import { PostCommentComponent } from "./post-comment/post-comment.component";
6 3 import { CommentService } from "../../../lib/ng-noosfero-api/http/comment.service";
7 4 import { CommentComponent } from "./comment.component";
... ... @@ -19,12 +16,13 @@ export class CommentsComponent {
19 16 @Input() showForm = true;
20 17 @Input() article: noosfero.Article;
21 18 @Input() parent: noosfero.CommentViewModel;
22   -
23 19 protected page = 1;
24 20 protected perPage = 5;
25 21 protected total = 0;
26 22  
27   - constructor(protected commentService: CommentService) { }
  23 + newComment = <noosfero.Comment>{};
  24 +
  25 + constructor(protected commentService: CommentService, private $scope: ng.IScope) { }
28 26  
29 27 ngOnInit() {
30 28 if (this.parent) {
... ... @@ -43,7 +41,7 @@ export class CommentsComponent {
43 41 this.comments.forEach((comment: noosfero.CommentViewModel) => {
44 42 comment.__show_reply = false;
45 43 });
46   - if (this.parent) {
  44 + if (this.parent) {
47 45 this.parent.__show_reply = false;
48 46 }
49 47 }
... ...
src/app/article/comment/comments.html
1 1 <div class="comments">
2   - <noosfero-post-comment (comment-saved)="ctrl.commentAdded($event.detail)" ng-if="ctrl.showForm" [article]="ctrl.article" [parent]="ctrl.parent"></noosfero-post-comment>
  2 + <noosfero-post-comment (comment-saved)="ctrl.commentAdded($event.detail)" ng-if="ctrl.showForm" [article]="ctrl.article" [parent]="ctrl.parent" [comment]="ctrl.newComment"></noosfero-post-comment>
3 3  
4 4 <div class="comments-list">
5 5 <noosfero-comment ng-repeat="comment in ctrl.comments | orderBy: 'created_at':true" [comment]="comment" [article]="ctrl.article"></noosfero-comment>
... ...
src/app/article/comment/comments.scss 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +.comments {
  2 + border-top: 2px solid #F3F3F3;
  3 + .comments {
  4 + border-top: 0;
  5 + }
  6 +}
... ...
src/app/article/comment/post-comment/post-comment.component.spec.ts
... ... @@ -7,6 +7,8 @@ const htmlTemplate: string = &#39;&lt;noosfero-post-comment [article]=&quot;ctrl.article&quot; [r
7 7 describe("Components", () => {
8 8 describe("Post Comment Component", () => {
9 9  
  10 + let properties = { article: { id: 1, accept_comments: true } };
  11 +
10 12 beforeEach(angular.mock.module("templates"));
11 13  
12 14 let commentService = jasmine.createSpyObj("commentService", ["createInArticle"]);
... ... @@ -19,7 +21,7 @@ describe(&quot;Components&quot;, () =&gt; {
19 21  
20 22 @Component({ selector: 'test-container-component', directives: [PostCommentComponent], template: htmlTemplate, providers: providers })
21 23 class ContainerComponent {
22   - article = { id: 1 };
  24 + article = properties['article'];
23 25 comment = { id: 2 };
24 26 }
25 27  
... ... @@ -30,6 +32,14 @@ describe(&quot;Components&quot;, () =&gt; {
30 32 });
31 33 });
32 34  
  35 + it("not render the post comment form when article doesn't accept comments", done => {
  36 + properties['article'].accept_comments = false;
  37 + helpers.createComponentFromClass(ContainerComponent).then(fixture => {
  38 + expect(fixture.debugElement.queryAll("form").length).toEqual(0);
  39 + done();
  40 + });
  41 + });
  42 +
33 43 it("emit an event when create comment", done => {
34 44 helpers.createComponentFromClass(ContainerComponent).then(fixture => {
35 45 let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
... ...
src/app/article/comment/post-comment/post-comment.component.ts
... ... @@ -2,20 +2,23 @@ import { Inject, Input, Output, EventEmitter, Component } from &#39;ng-forward&#39;;
2 2 import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service";
3 3 import { NotificationService } from "../../../shared/services/notification.service";
4 4 import { SessionService } from "../../../login";
  5 +import { CommentFormHotspotComponent } from "../../../hotspot/comment-form-hotspot.component";
5 6  
6 7 @Component({
7 8 selector: 'noosfero-post-comment',
8 9 templateUrl: 'app/article/comment/post-comment/post-comment.html',
9   - outputs: ['commentSaved']
  10 + outputs: ['commentSaved'],
  11 + directives: [CommentFormHotspotComponent]
10 12 })
11 13 @Inject(CommentService, NotificationService, SessionService)
12 14 export class PostCommentComponent {
13 15  
  16 + public static EVENT_COMMENT_RECEIVED = "comment.received";
  17 +
14 18 @Input() article: noosfero.Article;
15 19 @Input() parent: noosfero.Comment;
16 20 @Output() commentSaved: EventEmitter<Comment> = new EventEmitter<Comment>();
17   -
18   - comment = <noosfero.Comment>{};
  21 + @Input() comment = <noosfero.Comment>{};
19 22 private currentUser: noosfero.User;
20 23  
21 24 constructor(private commentService: CommentService,
... ...
src/app/article/comment/post-comment/post-comment.html
1   -<form class="clearfix post-comment">
  1 +<form class="clearfix post-comment" ng-if="ctrl.article.accept_comments">
2 2 <div class="form-group">
3 3 <div class="comment media">
4 4 <div class="media-left">
... ... @@ -8,6 +8,7 @@
8 8 </div>
9 9 <div class="media-body">
10 10 <textarea class="form-control custom-control" rows="1" ng-model="ctrl.comment.body" placeholder="{{'comment.post.placeholder' | translate}}"></textarea>
  11 + <noosfero-hotspot-comment-form [comment]="ctrl.comment" [parent]="ctrl.parent"></noosfero-hotspot-comment-form>
11 12 <button ng-show="ctrl.comment.body" type="submit" class="btn btn-default pull-right ng-hide" ng-click="ctrl.save()">{{"comment.post" | translate}}</button>
12 13 </div>
13 14 </div>
... ...
src/app/article/comment/post-comment/post-comment.scss
1 1 .comments {
2 2 .post-comment {
3 3 .media {
4   - border-top: 2px solid #F3F3F3;
5 4 padding-top: 10px;
6 5 .media-left {
7 6 padding: 10px 0;
... ...
src/app/article/content-viewer/navbar-actions.html
... ... @@ -4,4 +4,9 @@
4 4 <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}}
5 5 </a>
6 6 </li>
  7 + <li ng-show="vm.profile">
  8 + <a href="#" role="button" ui-sref="main.cms({profile: vm.profile.identifier, parent_id: vm.parentId, type: 'CommentParagraphPlugin::Discussion'})">
  9 + <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}}
  10 + </a>
  11 + </li>
7 12 </ul>
... ...
src/app/article/macro/macro.directive.spec.ts 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +import {Input, provide, Component} from 'ng-forward';
  2 +import {MacroDirective} from "./macro.directive";
  3 +
  4 +import * as helpers from "../../../spec/helpers";
  5 +
  6 +const htmlTemplate: string = '<div data-macro="macro_component" data-macro-custom="custom"></div>';
  7 +
  8 +describe("Directives", () => {
  9 +
  10 + describe("Macro directive", () => {
  11 + it("renders a macro component using the name passed in data-macro", (done: Function) => {
  12 + helpers.quickCreateComponent({ template: htmlTemplate, directives: [MacroDirective] }).then((fixture) => {
  13 + expect(fixture.debugElement.queryAll('macro-component').length).toEqual(1);
  14 + done();
  15 + });
  16 + });
  17 +
  18 + it("extract custom attributes from macro", (done: Function) => {
  19 + helpers.quickCreateComponent({ template: htmlTemplate, directives: [MacroDirective] }).then((fixture) => {
  20 + expect(fixture.debugElement.query('macro-component').attr("custom")).toEqual("custom");
  21 + done();
  22 + });
  23 + });
  24 + });
  25 +});
... ...
src/app/article/macro/macro.directive.ts 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +import {Directive, Inject} from "ng-forward";
  2 +
  3 +@Directive({
  4 + selector: '[macro]',
  5 + providers: []
  6 +})
  7 +@Inject('$element', '$scope', '$compile')
  8 +export class MacroDirective {
  9 +
  10 + private macroPrefix = "data-macro";
  11 +
  12 + constructor(private $element: any, private $scope: ng.IScope, private $compile: ng.ICompileService) {
  13 + let macro = $element[0].attributes[this.macroPrefix].value;
  14 + let componentName = this.normalizeName(macro);
  15 + let content = $element.html().replace(/"/g, '&quot;');
  16 + let customAttributes = this.extractCustomAttributes($element[0].attributes);
  17 + $element.replaceWith($compile(`<${componentName} [article]="ctrl.article" content="${content}" ${customAttributes}></${componentName}>`)($scope));
  18 + }
  19 +
  20 + extractCustomAttributes(attributes: any) {
  21 + let customAttributes = "";
  22 + for (let attr of attributes) {
  23 + if (attr.name.startsWith(this.macroPrefix + '-')) {
  24 + let name = this.normalizeName(attr.name.replace(this.macroPrefix + '-', ''));
  25 + customAttributes += ` ${name}='${attr.value}'`;
  26 + }
  27 + }
  28 + return customAttributes;
  29 + }
  30 +
  31 + normalizeName(name: string) {
  32 + return name.replace(/[_\/]/g, '-').toLowerCase();
  33 + }
  34 +}
... ...
src/app/hotspot/article-content-hotspot.component.ts 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +import {Component, Input, Inject} from "ng-forward";
  2 +import * as plugins from "../../plugins";
  3 +import {dasherize} from "ng-forward/cjs/util/helpers";
  4 +import {PluginHotspot} from "./plugin-hotspot";
  5 +
  6 +@Component({
  7 + selector: "noosfero-hotspot-article-content",
  8 + template: "<span></span>"
  9 +})
  10 +@Inject("$element", "$scope", "$compile")
  11 +export class ArticleContentHotspotComponent extends PluginHotspot {
  12 +
  13 + @Input() article: noosfero.Article;
  14 +
  15 + constructor(
  16 + private $element: any,
  17 + private $scope: ng.IScope,
  18 + private $compile: ng.ICompileService) {
  19 + super("article_extra_content");
  20 + }
  21 +
  22 + addHotspot(directiveName: string) {
  23 + this.$element.append(this.$compile('<' + directiveName + ' [article]="ctrl.article"></' + directiveName + '>')(this.$scope));
  24 + }
  25 +}
... ...
src/app/hotspot/article-toolbar-hotspot.component.ts 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +import {Component, Input, Inject} from "ng-forward";
  2 +import * as plugins from "../../plugins";
  3 +import {dasherize} from "ng-forward/cjs/util/helpers";
  4 +import {PluginHotspot} from "./plugin-hotspot";
  5 +
  6 +@Component({
  7 + selector: "noosfero-hotspot-article-toolbar",
  8 + template: "<span></span>"
  9 +})
  10 +@Inject("$element", "$scope", "$compile")
  11 +export class ArticleToolbarHotspotComponent extends PluginHotspot {
  12 +
  13 + @Input() article: noosfero.Article;
  14 +
  15 + constructor(
  16 + private $element: any,
  17 + private $scope: ng.IScope,
  18 + private $compile: ng.ICompileService) {
  19 + super("article_extra_toolbar_buttons");
  20 + }
  21 +
  22 + addHotspot(directiveName: string) {
  23 + this.$element.append(this.$compile('<' + directiveName + ' [article]="ctrl.article"></' + directiveName + '>')(this.$scope));
  24 + }
  25 +}
... ...
src/app/hotspot/comment-form-hotspot.component.ts 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +import {Component, Input, Inject} from "ng-forward";
  2 +import * as plugins from "../../plugins";
  3 +import {dasherize} from "ng-forward/cjs/util/helpers";
  4 +import {PluginHotspot} from "./plugin-hotspot";
  5 +
  6 +@Component({
  7 + selector: "noosfero-hotspot-comment-form",
  8 + template: "<span></span>"
  9 +})
  10 +@Inject("$element", "$scope", "$compile")
  11 +export class CommentFormHotspotComponent extends PluginHotspot {
  12 +
  13 + @Input() comment: noosfero.Comment;
  14 + @Input() parent: noosfero.Comment;
  15 +
  16 + constructor(
  17 + private $element: any,
  18 + private $scope: ng.IScope,
  19 + private $compile: ng.ICompileService) {
  20 + super("comment_form_extra_contents");
  21 + }
  22 +
  23 + addHotspot(directiveName: string) {
  24 + this.$element.append(this.$compile('<' + directiveName + ' [comment]="ctrl.comment" [parent]="ctrl.parent"></' + directiveName + '>')(this.$scope));
  25 + }
  26 +}
... ...
src/app/hotspot/hotspot.decorator.ts 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +export function Hotspot(hotspotName: string) {
  2 + return (target: any) => {
  3 + target['hotspot'] = hotspotName;
  4 + };
  5 +}
... ...
src/app/hotspot/plugin-hotspot.ts 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +import {Component, Input, Inject} from "ng-forward";
  2 +import * as plugins from "../../plugins";
  3 +import {dasherize} from "ng-forward/cjs/util/helpers";
  4 +
  5 +export abstract class PluginHotspot {
  6 +
  7 + constructor(protected hotspot: string) { }
  8 +
  9 + ngOnInit() {
  10 + for (let component of plugins.hotspots) {
  11 + if (component.hotspot === this.hotspot) {
  12 + let directiveName = dasherize(component.name.replace('Component', ''));
  13 + this.addHotspot(directiveName);
  14 + }
  15 + }
  16 + }
  17 +
  18 + abstract addHotspot(directiveName: string): any;
  19 +}
... ...
src/app/index.scss
... ... @@ -28,12 +28,16 @@ $primary-color-dark: #0288d1;
28 28 $default-bg-hover-color: #f8f8f8;
29 29 $red-color: #e84e40;
30 30 $red-color-dark: #dd191d;
  31 +$green-color: #8bc34a;
  32 +$green-color-dark: #689f38;
31 33  
32 34 //GRID - media queries breakpoints
33 35 $break-xxs-min: 420px;
34 36 $break-xs-min: 768px;
  37 +$break-sm-min: 992px;
35 38  
36 39 $break-xxs-max: ($break-xxs-min - 1);
  40 +$break-sm-max: ($break-sm-min - 1);
37 41 $break-xs-max: ($break-xs-min - 1);
38 42  
39 43  
... ... @@ -76,4 +80,5 @@ h1, h2, h3, h4, h5 {
76 80 @import "layout/scss/mixins";
77 81 @import "layout/scss/bootstrap-overrides";
78 82 @import "layout/scss/layout";
  83 +@import "layout/scss/sidebar";
79 84 @import "layout/scss/tables";
... ...
src/app/index.ts
... ... @@ -5,7 +5,7 @@ import {noosferoAngularRunBlock} from &quot;./index.run&quot;;
5 5 import {MainComponent} from "./main/main.component";
6 6 import {bootstrap, bundle} from "ng-forward";
7 7  
8   -import {AUTH_EVENTS} from "./login/auth-events";
  8 +import {AuthEvents} from "./login/auth-events";
9 9 import {AuthController} from "./login/auth.controller";
10 10  
11 11 import {Navbar} from "./layout/navbar/navbar";
... ... @@ -15,15 +15,16 @@ declare var moment: any;
15 15 let noosferoApp: any = bundle("noosferoApp", MainComponent, ["ngAnimate", "ngCookies", "ngStorage", "ngTouch",
16 16 "ngSanitize", "ngMessages", "ngAria", "restangular",
17 17 "ui.router", "ui.bootstrap", "toastr", "ngCkeditor",
18   - "angularMoment", "angular.filter", "akoenig.deckgrid",
  18 + "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid",
19 19 "angular-timeline", "duScroll", "oitozero.ngSweetAlert",
20   - "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad"]).publish();
  20 + "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad",
  21 + "angular-click-outside"]).publish();
21 22  
22 23 NoosferoApp.angularModule = noosferoApp;
23 24  
24 25  
25 26 NoosferoApp.addConstants("moment", moment);
26   -NoosferoApp.addConstants("AUTH_EVENTS", AUTH_EVENTS);
  27 +NoosferoApp.addConstants("AuthEvents", AuthEvents);
27 28  
28 29 NoosferoApp.addConfig(noosferoModuleConfig);
29 30 NoosferoApp.run(noosferoAngularRunBlock);
... ...
src/app/layout/blocks/block.component.ts
... ... @@ -11,7 +11,7 @@ export class BlockComponent {
11 11 @Input() owner: any;
12 12  
13 13 ngOnInit() {
14   - let blockName = (this.block && this.block.type) ? this.block.type.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block";
  14 + let blockName = (this.block && this.block.type) ? this.block.type.replace(/::/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block";
15 15 this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope));
16 16 }
17 17  
... ...
src/app/layout/blocks/communities-block/communities-block.component.spec.ts
... ... @@ -1,51 +0,0 @@
1   -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Provider, Input, provide, Component} from 'ng-forward';
3   -
4   -import {CommunitiesBlockComponent} from './communities-block.component';
5   -
6   -const htmlTemplate: string = '<noosfero-communities-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-communities-block>';
7   -
8   -const tcb = new TestComponentBuilder();
9   -
10   -describe("Components", () => {
11   - describe("Communities Block Component", () => {
12   -
13   - beforeEach(angular.mock.module("templates"));
14   -
15   - let state = jasmine.createSpyObj("state", ["go"]);
16   - let providers = [
17   - new Provider('truncateFilter', { useValue: () => { } }),
18   - new Provider('stripTagsFilter', { useValue: () => { } }),
19   - new Provider('$state', { useValue: state }),
20   - new Provider('CommunityService', {
21   - useValue: {
22   - getByOwner: (owner: any, params: any): any => {
23   - return Promise.resolve({ data: [{ identifier: "community1" }] });
24   - }
25   - }
26   - }),
27   - ];
28   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [CommunitiesBlockComponent], providers: providers })
29   - class BlockContainerComponent {
30   - block = { type: 'Block', settings: {} };
31   - owner = { name: 'profile-name' };
32   - }
33   -
34   - it("get communities", done => {
35   - tcb.createAsync(BlockContainerComponent).then(fixture => {
36   - let block: CommunitiesBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
37   - expect(block.profiles).toEqual([{ identifier: "community1" }]);
38   - done();
39   - });
40   - });
41   -
42   - it("render the profile image for each community", done => {
43   - tcb.createAsync(BlockContainerComponent).then(fixture => {
44   - fixture.debugElement.getLocal("$rootScope").$apply();
45   - expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1);
46   - done();
47   - });
48   - });
49   -
50   - });
51   -});
src/app/layout/blocks/communities-block/communities-block.component.ts
... ... @@ -1,24 +0,0 @@
1   -import {Input, Inject, Component} from "ng-forward";
2   -import {CommunityService} from "../../../../lib/ng-noosfero-api/http/community.service";
3   -
4   -@Component({
5   - selector: "noosfero-communities-block",
6   - templateUrl: 'app/layout/blocks/communities-block/communities-block.html',
7   -})
8   -@Inject(CommunityService)
9   -export class CommunitiesBlockComponent {
10   -
11   - @Input() block: noosfero.Block;
12   - @Input() owner: noosfero.Profile;
13   -
14   - profiles: any = [];
15   -
16   - constructor(private communityService: CommunityService) { }
17   -
18   - ngOnInit() {
19   - let limit: number = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5;
20   - this.communityService.getByOwner(this.owner, { limit: limit }).then((result: noosfero.RestResult<noosfero.Community[]>) => {
21   - this.profiles = result.data;
22   - });
23   - }
24   -}
src/app/layout/blocks/communities-block/communities-block.html
... ... @@ -1,5 +0,0 @@
1   -<div class="communities-block">
2   - <a ng-repeat="profile in ctrl.profiles" ui-sref="main.profile.home({profile: profile.identifier})" class="profile">
3   - <noosfero-profile-image [profile]="profile"></noosfero-profile-image>
4   - </a>
5   -</div>
src/app/layout/blocks/communities-block/communities-block.scss
... ... @@ -1,16 +0,0 @@
1   -.communities-block {
2   - .profile {
3   - margin: 10px;
4   - img, i.profile-image {
5   - width: 60px;
6   - }
7   - img {
8   - display: inline-block;
9   - vertical-align: top;
10   - }
11   - i.profile-image {
12   - text-align: center;
13   - font-size: 4.5em;
14   - }
15   - }
16   -}
src/app/layout/blocks/communities/communities-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Provider, Input, provide, Component} from 'ng-forward';
  3 +
  4 +import {CommunitiesBlockComponent} from './communities-block.component';
  5 +
  6 +const htmlTemplate: string = '<noosfero-communities-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-communities-block>';
  7 +
  8 +const tcb = new TestComponentBuilder();
  9 +
  10 +describe("Components", () => {
  11 + describe("Communities Block Component", () => {
  12 +
  13 + beforeEach(angular.mock.module("templates"));
  14 +
  15 + let state = jasmine.createSpyObj("state", ["go"]);
  16 + let providers = [
  17 + new Provider('truncateFilter', { useValue: () => { } }),
  18 + new Provider('stripTagsFilter', { useValue: () => { } }),
  19 + new Provider('$state', { useValue: state }),
  20 + new Provider('CommunityService', {
  21 + useValue: {
  22 + getByOwner: (owner: any, params: any): any => {
  23 + return Promise.resolve({ data: [{ identifier: "community1" }] });
  24 + }
  25 + }
  26 + }),
  27 + ];
  28 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [CommunitiesBlockComponent], providers: providers })
  29 + class BlockContainerComponent {
  30 + block = { type: 'Block', settings: {} };
  31 + owner = { name: 'profile-name' };
  32 + }
  33 +
  34 + it("get communities", done => {
  35 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  36 + let block: CommunitiesBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  37 + expect(block.profiles).toEqual([{ identifier: "community1" }]);
  38 + done();
  39 + });
  40 + });
  41 +
  42 + it("render the profile image for each community", done => {
  43 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  44 + fixture.debugElement.getLocal("$rootScope").$apply();
  45 + expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1);
  46 + done();
  47 + });
  48 + });
  49 +
  50 + });
  51 +});
... ...
src/app/layout/blocks/communities/communities-block.component.ts 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +import {Input, Inject, Component} from "ng-forward";
  2 +import {CommunityService} from "../../../../lib/ng-noosfero-api/http/community.service";
  3 +
  4 +@Component({
  5 + selector: "noosfero-communities-block",
  6 + templateUrl: 'app/layout/blocks/communities/communities-block.html',
  7 +})
  8 +@Inject(CommunityService)
  9 +export class CommunitiesBlockComponent {
  10 +
  11 + @Input() block: noosfero.Block;
  12 + @Input() owner: noosfero.Profile;
  13 +
  14 + profiles: any = [];
  15 +
  16 + constructor(private communityService: CommunityService) { }
  17 +
  18 + ngOnInit() {
  19 + let limit: number = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5;
  20 + this.communityService.getByOwner(this.owner, { limit: limit }).then((result: noosfero.RestResult<noosfero.Community[]>) => {
  21 + this.profiles = result.data;
  22 + });
  23 + }
  24 +}
... ...
src/app/layout/blocks/communities/communities-block.html 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +<div class="communities-block">
  2 + <a ng-repeat="profile in ctrl.profiles" ui-sref="main.profile.home({profile: profile.identifier})" class="profile">
  3 + <noosfero-profile-image [profile]="profile"></noosfero-profile-image>
  4 + </a>
  5 +</div>
... ...
src/app/layout/blocks/communities/communities-block.scss 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +.communities-block {
  2 + .profile {
  3 + margin: 10px;
  4 + img, i.profile-image {
  5 + width: 60px;
  6 + }
  7 + img {
  8 + display: inline-block;
  9 + vertical-align: top;
  10 + }
  11 + i.profile-image {
  12 + text-align: center;
  13 + font-size: 4.5em;
  14 + }
  15 + }
  16 +}
... ...
src/app/layout/blocks/communities/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./communities-block.component";
... ...
src/app/layout/blocks/link-list/index.ts
1 1 /* Module Index Entry - generated using the script npm run generate-index */
2   -export * from "./link-list.component";
  2 +export * from "./link-list-block.component";
... ...
src/app/layout/blocks/link-list/link-list-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,65 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Pipe, Input, provide, Component} from 'ng-forward';
  3 +import {provideFilters} from '../../../../spec/helpers';
  4 +
  5 +import {LinkListBlockComponent} from './link-list-block.component';
  6 +
  7 +const tcb = new TestComponentBuilder();
  8 +
  9 +const htmlTemplate: string = '<noosfero-link-list-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-link-list-block>';
  10 +
  11 +
  12 +describe("Components", () => {
  13 +
  14 + describe("Link List Block Component", () => {
  15 +
  16 + beforeEach(angular.mock.module("templates"));
  17 +
  18 + it("receives the block and the owner as inputs", done => {
  19 +
  20 + // Creating a container component (BlockContainerComponent) to include
  21 + // the component under test (Block)
  22 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [LinkListBlockComponent] })
  23 + class BlockContainerComponent {
  24 + block = { type: 'Block' };
  25 + owner = { name: 'profile-name' };
  26 + constructor() {
  27 + }
  28 + }
  29 +
  30 + // uses the TestComponentBuilder instance to initialize the component
  31 + // .overrideView(LinkListBlock, { template: 'asdasdasd', pipes: [NoosferoTemplate] })
  32 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  33 + // and here we can inspect and run the test assertions
  34 + let myComponent: LinkListBlockComponent = fixture.componentInstance;
  35 +
  36 + // assure the block object inside the Block matches
  37 + // the provided through the parent component
  38 + expect(myComponent.block.type).toEqual("Block");
  39 + expect(myComponent.owner.name).toEqual("profile-name");
  40 + done();
  41 + });
  42 + });
  43 +
  44 +
  45 + it("display links stored in block settings", done => {
  46 +
  47 + @Component({
  48 + selector: 'test-container-component',
  49 + template: htmlTemplate,
  50 + directives: [LinkListBlockComponent],
  51 + providers: provideFilters("noosferoTemplateFilter")
  52 + })
  53 + class CustomBlockType {
  54 + block: any = { settings: { links: [{ name: 'link1', address: 'address1' }, { name: 'link2', address: 'address2' }] } };
  55 + owner: any = { name: 'profile-name' };
  56 + }
  57 + tcb.createAsync(CustomBlockType).then(fixture => {
  58 + expect(fixture.debugElement.queryAll(".link-list-block a").length).toEqual(2);
  59 + done();
  60 + });
  61 + });
  62 +
  63 + });
  64 +
  65 +});
... ...
src/app/layout/blocks/link-list/link-list-block.component.ts 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +import {Component, Input} from "ng-forward";
  2 +
  3 +@Component({
  4 + selector: "noosfero-link-list-block",
  5 + templateUrl: "app/layout/blocks/link-list/link-list-block.html"
  6 +})
  7 +export class LinkListBlockComponent {
  8 +
  9 + @Input() block: any;
  10 + @Input() owner: any;
  11 +
  12 + links: any;
  13 +
  14 + ngOnInit() {
  15 + if (this.block && this.block.settings) {
  16 + this.links = this.block.settings.links;
  17 + }
  18 + }
  19 +
  20 +}
... ...
src/app/layout/blocks/link-list/link-list-block.html 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<div class="link-list-block">
  2 + <div ng-repeat="link in ctrl.links">
  3 + <a ng-href="{{link.address | noosferoTemplate:{profile: ctrl.owner.identifier} }}">
  4 + <i class="fa fa-fw icon-{{link.icon}}"></i> <span>{{link.name}}</span>
  5 + </a>
  6 + </div>
  7 +</div>
... ...
src/app/layout/blocks/link-list/link-list-block.scss 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +.icon-event {
  2 + @extend .fa-calendar;
  3 +}
  4 +.icon-photos {
  5 + @extend .fa-photo;
  6 +}
  7 +.icon-edit {
  8 + @extend .fa-edit;
  9 +}
  10 +.icon-ok {
  11 + @extend .fa-check;
  12 +}
  13 +.icon-send {
  14 + @extend .fa-send-o;
  15 +}
  16 +.icon-menu-people {
  17 + @extend .fa-user;
  18 +}
  19 +.icon-forum {
  20 + @extend .fa-users;
  21 +}
  22 +.icon-new {
  23 + @extend .fa-file-o;
  24 +}
  25 +.icon-save {
  26 + @extend .fa-save;
  27 +}
  28 +
  29 +.link-list-block {
  30 + a i {
  31 + line-height: 25px;
  32 + color: #949494;
  33 + }
  34 +}
... ...
src/app/layout/blocks/link-list/link-list.component.spec.ts
... ... @@ -1,65 +0,0 @@
1   -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Pipe, Input, provide, Component} from 'ng-forward';
3   -import {provideFilters} from '../../../../spec/helpers';
4   -
5   -import {LinkListBlockComponent} from './link-list.component';
6   -
7   -const tcb = new TestComponentBuilder();
8   -
9   -const htmlTemplate: string = '<noosfero-link-list-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-link-list-block>';
10   -
11   -
12   -describe("Components", () => {
13   -
14   - describe("Link List Block Component", () => {
15   -
16   - beforeEach(angular.mock.module("templates"));
17   -
18   - it("receives the block and the owner as inputs", done => {
19   -
20   - // Creating a container component (BlockContainerComponent) to include
21   - // the component under test (Block)
22   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [LinkListBlockComponent] })
23   - class BlockContainerComponent {
24   - block = { type: 'Block' };
25   - owner = { name: 'profile-name' };
26   - constructor() {
27   - }
28   - }
29   -
30   - // uses the TestComponentBuilder instance to initialize the component
31   - // .overrideView(LinkListBlock, { template: 'asdasdasd', pipes: [NoosferoTemplate] })
32   - tcb.createAsync(BlockContainerComponent).then(fixture => {
33   - // and here we can inspect and run the test assertions
34   - let myComponent: LinkListBlockComponent = fixture.componentInstance;
35   -
36   - // assure the block object inside the Block matches
37   - // the provided through the parent component
38   - expect(myComponent.block.type).toEqual("Block");
39   - expect(myComponent.owner.name).toEqual("profile-name");
40   - done();
41   - });
42   - });
43   -
44   -
45   - it("display links stored in block settings", done => {
46   -
47   - @Component({
48   - selector: 'test-container-component',
49   - template: htmlTemplate,
50   - directives: [LinkListBlockComponent],
51   - providers: provideFilters("noosferoTemplateFilter")
52   - })
53   - class CustomBlockType {
54   - block: any = { settings: { links: [{ name: 'link1', address: 'address1' }, { name: 'link2', address: 'address2' }] } };
55   - owner: any = { name: 'profile-name' };
56   - }
57   - tcb.createAsync(CustomBlockType).then(fixture => {
58   - expect(fixture.debugElement.queryAll(".link-list-block a").length).toEqual(2);
59   - done();
60   - });
61   - });
62   -
63   - });
64   -
65   -});
66 0 \ No newline at end of file
src/app/layout/blocks/link-list/link-list.component.ts
... ... @@ -1,20 +0,0 @@
1   -import {Component, Input} from "ng-forward";
2   -
3   -@Component({
4   - selector: "noosfero-link-list-block",
5   - templateUrl: "app/layout/blocks/link-list/link-list.html"
6   -})
7   -export class LinkListBlockComponent {
8   -
9   - @Input() block: any;
10   - @Input() owner: any;
11   -
12   - links: any;
13   -
14   - ngOnInit() {
15   - if (this.block && this.block.settings) {
16   - this.links = this.block.settings.links;
17   - }
18   - }
19   -
20   -}
src/app/layout/blocks/link-list/link-list.html
... ... @@ -1,7 +0,0 @@
1   -<div class="link-list-block">
2   - <div ng-repeat="link in ctrl.links">
3   - <a ng-href="{{link.address | noosferoTemplate:{profile: ctrl.owner.identifier} }}">
4   - <i class="fa fa-fw icon-{{link.icon}}"></i> <span>{{link.name}}</span>
5   - </a>
6   - </div>
7   -</div>
src/app/layout/blocks/link-list/link-list.scss
... ... @@ -1,34 +0,0 @@
1   -.icon-event {
2   - @extend .fa-calendar;
3   -}
4   -.icon-photos {
5   - @extend .fa-photo;
6   -}
7   -.icon-edit {
8   - @extend .fa-edit;
9   -}
10   -.icon-ok {
11   - @extend .fa-check;
12   -}
13   -.icon-send {
14   - @extend .fa-send-o;
15   -}
16   -.icon-menu-people {
17   - @extend .fa-user;
18   -}
19   -.icon-forum {
20   - @extend .fa-users;
21   -}
22   -.icon-new {
23   - @extend .fa-file-o;
24   -}
25   -.icon-save {
26   - @extend .fa-save;
27   -}
28   -
29   -.link-list-block {
30   - a i {
31   - line-height: 25px;
32   - color: #949494;
33   - }
34   -}
src/app/layout/blocks/main-block/index.ts
... ... @@ -1,2 +0,0 @@
1   -/* Module Index Entry - generated using the script npm run generate-index */
2   -export * from "./main-block.component";
src/app/layout/blocks/main-block/main-block.component.spec.ts
... ... @@ -1,40 +0,0 @@
1   -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Input, provide, Component, StateConfig} from 'ng-forward';
3   -
4   -import {MainBlockComponent} from './main-block.component';
5   -import {NoosferoApp} from '../../../index.module';
6   -
7   -const tcb = new TestComponentBuilder();
8   -
9   -const htmlTemplate: string = '<noosfero-main-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-main-block>';
10   -
11   -describe("Components", () => {
12   - describe("Main Block Component", () => {
13   -
14   - // the karma preprocessor html2js transform the templates html into js files which put
15   - // the templates to the templateCache into the module templates
16   - // we need to load the module templates here as the template for the
17   - // component Block will be load on our tests
18   - beforeEach(angular.mock.module("templates"));
19   -
20   - it("check if the main block has a tag with ui-view attribute", done => {
21   -
22   - // Creating a container component (BlockContainerComponent) to include
23   - // the component under test (Block)
24   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MainBlockComponent] })
25   - class BlockContainerComponent {
26   - }
27   -
28   - // uses the TestComponentBuilder instance to initialize the component
29   - tcb.createAsync(BlockContainerComponent).then(fixture => {
30   - // and here we can inspect and run the test assertions
31   - // let myComponent: MainBlockComponent = fixture.componentInstance;
32   -
33   - // assure the block object inside the Block matches
34   - // the provided through the parent component
35   - expect(fixture.debugElement.queryAll('[ui-view="mainBlockContent"]').length).toEqual(1);
36   - done();
37   - });
38   - });
39   - });
40   -});
41 0 \ No newline at end of file
src/app/layout/blocks/main-block/main-block.component.ts
... ... @@ -1,10 +0,0 @@
1   -import {Component, Input} from 'ng-forward';
2   -import {BlockComponent} from '../block.component';
3   -
4   -@Component({
5   - selector: 'noosfero-main-block',
6   - templateUrl: 'app/layout/blocks/main-block/main-block.html'
7   -})
8   -export class MainBlockComponent {
9   -
10   -}
src/app/layout/blocks/main-block/main-block.html
... ... @@ -1 +0,0 @@
1   -<div ui-view="mainBlockContent" autoscroll></div>
src/app/layout/blocks/main/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./main-block.component";
... ...
src/app/layout/blocks/main/main-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Input, provide, Component, StateConfig} from 'ng-forward';
  3 +
  4 +import {MainBlockComponent} from './main-block.component';
  5 +import {NoosferoApp} from '../../../index.module';
  6 +
  7 +const tcb = new TestComponentBuilder();
  8 +
  9 +const htmlTemplate: string = '<noosfero-main-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-main-block>';
  10 +
  11 +describe("Components", () => {
  12 + describe("Main Block Component", () => {
  13 +
  14 + // the karma preprocessor html2js transform the templates html into js files which put
  15 + // the templates to the templateCache into the module templates
  16 + // we need to load the module templates here as the template for the
  17 + // component Block will be load on our tests
  18 + beforeEach(angular.mock.module("templates"));
  19 +
  20 + it("check if the main block has a tag with ui-view attribute", done => {
  21 +
  22 + // Creating a container component (BlockContainerComponent) to include
  23 + // the component under test (Block)
  24 + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MainBlockComponent] })
  25 + class BlockContainerComponent {
  26 + }
  27 +
  28 + // uses the TestComponentBuilder instance to initialize the component
  29 + tcb.createAsync(BlockContainerComponent).then(fixture => {
  30 + // and here we can inspect and run the test assertions
  31 + // let myComponent: MainBlockComponent = fixture.componentInstance;
  32 +
  33 + // assure the block object inside the Block matches
  34 + // the provided through the parent component
  35 + expect(fixture.debugElement.queryAll('[ui-view="mainBlockContent"]').length).toEqual(1);
  36 + done();
  37 + });
  38 + });
  39 + });
  40 +});
0 41 \ No newline at end of file
... ...
src/app/layout/blocks/main/main-block.component.ts 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +import {Component, Input} from 'ng-forward';
  2 +import {BlockComponent} from '../block.component';
  3 +
  4 +@Component({
  5 + selector: 'noosfero-main-block',
  6 + templateUrl: 'app/layout/blocks/main/main-block.html'
  7 +})
  8 +export class MainBlockComponent {
  9 +
  10 +}
... ...
src/app/layout/blocks/main/main-block.html 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<div ui-view="mainBlockContent" autoscroll></div>
... ...
src/app/layout/blocks/members-block/index.ts
... ... @@ -1,2 +0,0 @@
1   -/* Module Index Entry - generated using the script npm run generate-index */
2   -export * from "./members-block.component";
src/app/layout/blocks/members-block/members-block.component.spec.ts
... ... @@ -1,49 +0,0 @@
1   -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Provider, Input, provide, Component} from 'ng-forward';
3   -import {MembersBlockComponent} from './members-block.component';
4   -import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
5   -
6   -const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>';
7   -
8   -const tcb = new TestComponentBuilder();
9   -
10   -describe("Components", () => {
11   - describe("Members Block Component", () => {
12   -
13   - let helper: ComponentTestHelper<MembersBlockComponent>;
14   -
15   - let providers = [
16   - new Provider('ProfileService', {
17   - useValue: {
18   - getProfileMembers: (profileId: number, filters: any): any => {
19   - return Promise.resolve({ data: { people: [{ identifier: "person1" }] } });
20   - }
21   - }
22   - }),
23   - ];
24   -
25   - beforeEach(angular.mock.module("templates"));
26   -
27   - beforeEach((done) => {
28   - // Custom properties for the component
29   - let properties = { owner: { id: 1 } };
30   - // Create the component bed for the test.
31   - let cls = createClass({
32   - template: htmlTemplate,
33   - directives: [MembersBlockComponent],
34   - providers: providers,
35   - properties: properties
36   - });
37   - helper = new ComponentTestHelper<MembersBlockComponent>(cls, done);
38   - });
39   -
40   - it("get members of the block owner", () => {
41   - expect(helper.component.members[0].identifier).toEqual("person1");
42   - });
43   -
44   - it("render the profile image for each member", () => {
45   - expect(helper.all("noosfero-profile-image").length).toEqual(1);
46   - });
47   -
48   - });
49   -});
src/app/layout/blocks/members-block/members-block.component.ts
... ... @@ -1,25 +0,0 @@
1   -import {Input, Inject, Component} from "ng-forward";
2   -import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service";
3   -
4   -@Component({
5   - selector: "noosfero-members-block",
6   - templateUrl: 'app/layout/blocks/members-block/members-block.html',
7   -})
8   -@Inject(ProfileService)
9   -export class MembersBlockComponent {
10   -
11   - @Input() block: noosfero.Block;
12   - @Input() owner: noosfero.Profile;
13   -
14   - members: any = [];
15   -
16   - constructor(private profileService: ProfileService) {
17   -
18   - }
19   -
20   - ngOnInit() {
21   - this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => {
22   - this.members = response.data.people;
23   - });
24   - }
25   -}
src/app/layout/blocks/members-block/members-block.html
... ... @@ -1,5 +0,0 @@
1   -<div class="members-block">
2   - <a ng-repeat="member in ctrl.members" ui-sref="main.profile.home({profile: member.identifier})" class="member">
3   - <noosfero-profile-image [profile]="member"></noosfero-profile-image>
4   - </a>
5   -</div>
src/app/layout/blocks/members-block/members-block.scss
... ... @@ -1,17 +0,0 @@
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/members/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./members-block.component";
... ...
src/app/layout/blocks/members/members-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,49 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Provider, Input, provide, Component} from 'ng-forward';
  3 +import {MembersBlockComponent} from './members-block.component';
  4 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
  5 +
  6 +const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>';
  7 +
  8 +const tcb = new TestComponentBuilder();
  9 +
  10 +describe("Components", () => {
  11 + describe("Members Block Component", () => {
  12 +
  13 + let helper: ComponentTestHelper<MembersBlockComponent>;
  14 +
  15 + let providers = [
  16 + new Provider('ProfileService', {
  17 + useValue: {
  18 + getProfileMembers: (profileId: number, filters: any): any => {
  19 + return Promise.resolve({ data: { people: [{ identifier: "person1" }] } });
  20 + }
  21 + }
  22 + }),
  23 + ];
  24 +
  25 + beforeEach(angular.mock.module("templates"));
  26 +
  27 + beforeEach((done) => {
  28 + // Custom properties for the component
  29 + let properties = { owner: { id: 1 } };
  30 + // Create the component bed for the test.
  31 + let cls = createClass({
  32 + template: htmlTemplate,
  33 + directives: [MembersBlockComponent],
  34 + providers: providers,
  35 + properties: properties
  36 + });
  37 + helper = new ComponentTestHelper<MembersBlockComponent>(cls, done);
  38 + });
  39 +
  40 + it("get members of the block owner", () => {
  41 + expect(helper.component.members[0].identifier).toEqual("person1");
  42 + });
  43 +
  44 + it("render the profile image for each member", () => {
  45 + expect(helper.all("noosfero-profile-image").length).toEqual(1);
  46 + });
  47 +
  48 + });
  49 +});
... ...
src/app/layout/blocks/members/members-block.component.ts 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +import {Input, Inject, Component} from "ng-forward";
  2 +import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service";
  3 +
  4 +@Component({
  5 + selector: "noosfero-members-block",
  6 + templateUrl: 'app/layout/blocks/members/members-block.html',
  7 +})
  8 +@Inject(ProfileService)
  9 +export class MembersBlockComponent {
  10 +
  11 + @Input() block: noosfero.Block;
  12 + @Input() owner: noosfero.Profile;
  13 +
  14 + members: any = [];
  15 +
  16 + constructor(private profileService: ProfileService) {
  17 +
  18 + }
  19 +
  20 + ngOnInit() {
  21 + this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => {
  22 + this.members = response.data.people;
  23 + });
  24 + }
  25 +}
... ...
src/app/layout/blocks/members/members-block.html 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +<div class="members-block">
  2 + <a ng-repeat="member in ctrl.members" ui-sref="main.profile.home({profile: member.identifier})" class="member">
  3 + <noosfero-profile-image [profile]="member"></noosfero-profile-image>
  4 + </a>
  5 +</div>
... ...
src/app/layout/blocks/members/members-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/people-block/index.ts
... ... @@ -1,2 +0,0 @@
1   -/* Module Index Entry - generated using the script npm run generate-index */
2   -export * from "./people-block.component";
src/app/layout/blocks/people-block/people-block.component.spec.ts
... ... @@ -1,72 +0,0 @@
1   -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Provider} from 'ng-forward';
3   -import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
4   -import {providers} from 'ng-forward/cjs/testing/providers';
5   -import {PeopleBlockComponent} from './people-block.component';
6   -
7   -const htmlTemplate: string = '<noosfero-people-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-people-block>';
8   -
9   -describe("Components", () => {
10   -
11   - describe("People Block Component", () => {
12   - let serviceMock = {
13   - getEnvironmentPeople: (filters: any): any => {
14   - return Promise.resolve([{ identifier: "person1" }]);
15   - }
16   - };
17   - let providers = [new Provider('EnvironmentService', { useValue: serviceMock })];
18   -
19   - let helper: ComponentTestHelper<PeopleBlockComponent>;
20   -
21   - beforeEach(angular.mock.module("templates"));
22   -
23   - /**
24   - * The beforeEach procedure will initialize the helper and parse
25   - * the component according to the given providers. Unfortunetly, in
26   - * this mode, the providers and properties given to the construtor
27   - * can't be overriden.
28   - */
29   - beforeEach((done) => {
30   - // Create the component bed for the test. Optionally, this could be done
31   - // in each test if one needs customization of these parameters per test
32   - let cls = createClass({
33   - template: htmlTemplate,
34   - directives: [PeopleBlockComponent],
35   - providers: providers,
36   - properties: {}
37   - });
38   - helper = new ComponentTestHelper<PeopleBlockComponent>(cls, done);
39   - });
40   -
41   - /**
42   - * By default the helper will have the component, with all properties
43   - * ready to be used. Here the mock provider 'EnvironmentService' will
44   - * return the given array with one person.
45   - */
46   - it("get block with one people", () => {
47   - expect(helper.component.people[0].identifier).toEqual("person1");
48   - });
49   -
50   - /**
51   - * There are helper functions to access the JQuery DOM like this.
52   - */
53   - it("render the profile image for each person", () => {
54   - expect(helper.all("noosfero-profile-image").length).toEqual(1);
55   - });
56   -
57   - /**
58   - * The main debugElement element is also available
59   - */
60   - it("render the main noosfero people block", () => {
61   - expect(helper.debugElement.children().length).toEqual(1, "The people-block should have a div children");
62   - });
63   -
64   - /**
65   - * Just another example of a JQuery DOM helper function
66   - */
67   - it("render the noosfero people block div", () => {
68   - let div = helper.findChildren("noosfero-people-block", "div");
69   - expect(div.className).toBe('people-block', "The class should be people-block");
70   - });
71   - });
72   -});
src/app/layout/blocks/people-block/people-block.component.ts
... ... @@ -1,26 +0,0 @@
1   -import {Input, Inject, Component} from "ng-forward";
2   -import {EnvironmentService} from "../../../../lib/ng-noosfero-api/http/environment.service";
3   -
4   -@Component({
5   - selector: "noosfero-people-block",
6   - templateUrl: 'app/layout/blocks/people-block/people-block.html',
7   -})
8   -@Inject(EnvironmentService)
9   -export class PeopleBlockComponent {
10   -
11   - @Input() block: noosfero.Block;
12   - @Input() owner: noosfero.Environment;
13   - private type: string = "people";
14   -
15   - people: noosfero.Person[] = [];
16   -
17   - constructor(private environmentService: EnvironmentService) {
18   - }
19   -
20   - ngOnInit() {
21   - this.environmentService.getEnvironmentPeople({ limit: '6' }).then((people: noosfero.Person[]) => {
22   - this.people = people;
23   - });
24   - }
25   -
26   -}
src/app/layout/blocks/people-block/people-block.html
... ... @@ -1,5 +0,0 @@
1   -<div class="{{ctrl.type}}-block">
2   - <a ng-repeat="person in ctrl.people" ui-sref="main.profile.home({profile: person.identifier})" class="person">
3   - <noosfero-profile-image [profile]="person"></noosfero-profile-image>
4   - </a>
5   -</div>
src/app/layout/blocks/people-block/people-block.scss
... ... @@ -1,17 +0,0 @@
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/people/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./people-block.component";
... ...
src/app/layout/blocks/people/people-block.component.spec.ts 0 → 100644
... ... @@ -0,0 +1,72 @@
  1 +import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
  2 +import {Provider} from 'ng-forward';
  3 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
  4 +import {providers} from 'ng-forward/cjs/testing/providers';
  5 +import {PeopleBlockComponent} from './people-block.component';
  6 +
  7 +const htmlTemplate: string = '<noosfero-people-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-people-block>';
  8 +
  9 +describe("Components", () => {
  10 +
  11 + describe("People Block Component", () => {
  12 + let serviceMock = {
  13 + getEnvironmentPeople: (filters: any): any => {
  14 + return Promise.resolve([{ identifier: "person1" }]);
  15 + }
  16 + };
  17 + let providers = [new Provider('EnvironmentService', { useValue: serviceMock })];
  18 +
  19 + let helper: ComponentTestHelper<PeopleBlockComponent>;
  20 +
  21 + beforeEach(angular.mock.module("templates"));
  22 +
  23 + /**
  24 + * The beforeEach procedure will initialize the helper and parse
  25 + * the component according to the given providers. Unfortunetly, in
  26 + * this mode, the providers and properties given to the construtor
  27 + * can't be overriden.
  28 + */
  29 + beforeEach((done) => {
  30 + // Create the component bed for the test. Optionally, this could be done
  31 + // in each test if one needs customization of these parameters per test
  32 + let cls = createClass({
  33 + template: htmlTemplate,
  34 + directives: [PeopleBlockComponent],
  35 + providers: providers,
  36 + properties: {}
  37 + });
  38 + helper = new ComponentTestHelper<PeopleBlockComponent>(cls, done);
  39 + });
  40 +
  41 + /**
  42 + * By default the helper will have the component, with all properties
  43 + * ready to be used. Here the mock provider 'EnvironmentService' will
  44 + * return the given array with one person.
  45 + */
  46 + it("get block with one people", () => {
  47 + expect(helper.component.people[0].identifier).toEqual("person1");
  48 + });
  49 +
  50 + /**
  51 + * There are helper functions to access the JQuery DOM like this.
  52 + */
  53 + it("render the profile image for each person", () => {
  54 + expect(helper.all("noosfero-profile-image").length).toEqual(1);
  55 + });
  56 +
  57 + /**
  58 + * The main debugElement element is also available
  59 + */
  60 + it("render the main noosfero people block", () => {
  61 + expect(helper.debugElement.children().length).toEqual(1, "The people-block should have a div children");
  62 + });
  63 +
  64 + /**
  65 + * Just another example of a JQuery DOM helper function
  66 + */
  67 + it("render the noosfero people block div", () => {
  68 + let div = helper.findChildren("noosfero-people-block", "div");
  69 + expect(div.className).toBe('people-block', "The class should be people-block");
  70 + });
  71 + });
  72 +});
... ...
src/app/layout/blocks/people/people-block.component.ts 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +import {Input, Inject, Component} from "ng-forward";
  2 +import {EnvironmentService} from "../../../../lib/ng-noosfero-api/http/environment.service";
  3 +
  4 +@Component({
  5 + selector: "noosfero-people-block",
  6 + templateUrl: 'app/layout/blocks/people/people-block.html',
  7 +})
  8 +@Inject(EnvironmentService)
  9 +export class PeopleBlockComponent {
  10 +
  11 + @Input() block: noosfero.Block;
  12 + @Input() owner: noosfero.Environment;
  13 + private type: string = "people";
  14 +
  15 + people: noosfero.Person[] = [];
  16 +
  17 + constructor(private environmentService: EnvironmentService) {
  18 + }
  19 +
  20 + ngOnInit() {
  21 + this.environmentService.getEnvironmentPeople({ limit: '6' }).then((people: noosfero.Person[]) => {
  22 + this.people = people;
  23 + });
  24 + }
  25 +
  26 +}
... ...
src/app/layout/blocks/people/people-block.html 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +<div class="{{ctrl.type}}-block">
  2 + <a ng-repeat="person in ctrl.people" ui-sref="main.profile.home({profile: person.identifier})" class="person">
  3 + <noosfero-profile-image [profile]="person"></noosfero-profile-image>
  4 + </a>
  5 +</div>
... ...
src/app/layout/blocks/people/people-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/profile-image-block/index.ts
... ... @@ -1,2 +0,0 @@
1   -/* Module Index Entry - generated using the script npm run generate-index */
2   -export * from "./profile-image-block.component";
src/app/layout/blocks/profile-image-block/profile-image-block.component.spec.ts
... ... @@ -1,46 +0,0 @@
1   -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Pipe, Input, provide, Component} from 'ng-forward';
3   -
4   -import {ProfileImageBlockComponent} from './profile-image-block.component';
5   -
6   -import * as helpers from "./../../../../spec/helpers";
7   -
8   -const tcb = new TestComponentBuilder();
9   -
10   -const htmlTemplate: string = '<noosfero-profile-image-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-profile-image-block>';
11   -
12   -describe("Components", () => {
13   -
14   - describe("Profile Image Block Component", () => {
15   -
16   - beforeEach(angular.mock.module("templates"));
17   -
18   - @Component({
19   - selector: 'test-container-component',
20   - template: htmlTemplate,
21   - directives: [ProfileImageBlockComponent],
22   - providers: helpers.provideFilters("translateFilter")
23   - })
24   - class BlockContainerComponent {
25   - block = { type: 'Block' };
26   - owner = { name: 'profile-name' };
27   - constructor() {
28   - }
29   - }
30   -
31   - it("show image if present", () => {
32   - helpers.tcb.createAsync(BlockContainerComponent).then(fixture => {
33   - let elProfile = fixture.debugElement.componentViewChildren[0];
34   - expect(elProfile.query('div.profile-image-block').length).toEqual(1);
35   - });
36   - });
37   -
38   - it("has link to the profile", () => {
39   - helpers.tcb.createAsync(BlockContainerComponent).then(fixture => {
40   - let elProfile = fixture.debugElement.componentViewChildren[0];
41   - expect(elProfile.query('a.settings-link').length).toEqual(1);
42   - });
43   - });
44   -
45   - });
46   -});
src/app/layout/blocks/profile-image-block/profile-image-block.component.ts
... ... @@ -1,14 +0,0 @@
1   -import {Inject, Input, Component} from "ng-forward";
2   -import {ProfileImageComponent} from "./../../../profile/image/image.component";
3   -
4   -@Component({
5   - selector: "noosfero-profile-image-block",
6   - templateUrl: 'app/layout/blocks/profile-image-block/profile-image-block.html',
7   - directives: [ProfileImageComponent]
8   -})
9   -export class ProfileImageBlockComponent {
10   -
11   - @Input() block: noosfero.Block;
12   - @Input() owner: noosfero.Profile;
13   -
14   -}
src/app/layout/blocks/profile-image-block/profile-image-block.html
... ... @@ -1,6 +0,0 @@
1   -<div class="center-block text-center profile-image-block">
2   - <a ui-sref="main.profile.info({profile: ctrl.owner.identifier})">
3   - <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image>
4   - </a>
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>
src/app/layout/blocks/profile-image-block/profile-image-block.scss
... ... @@ -1,5 +0,0 @@
1   -.profile-image-block {
2   - .settings-link {
3   - display: block;
4   - }
5   -}
src/app/layout/blocks/profile-image/index.ts 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/* Module Index Entry - generated using the script npm run generate-index */
  2 +export * from "./profile-image-block.component";
... ...