Commit 49c1ee2611f73c70c3f6873b49c2fa9d369d8af2

Authored by Carlos Purificação
2 parents b420f699 ab10b960

Merging with master

Merge remote-tracking branch 'origin/master' into login-block
Showing 204 changed files with 4245 additions and 1182 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 204 files displayed.

bower.json
... ... @@ -34,10 +34,14 @@
34 34 "angular-dynamic-locale": "^0.1.30",
35 35 "angular-i18n": "^1.5.0",
36 36 "angular-load": "^0.4.1",
37   - "angular-translate-interpolation-messageformat": "^2.10.0"
  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 },
39 41 "devDependencies": {
40   - "angular-mocks": "~1.5.0"
  42 + "angular-mocks": "~1.5.0",
  43 + "ng-ckeditor": "^0.2.1",
  44 + "ckeditor": "^4.5.8"
41 45 },
42 46 "overrides": {
43 47 "bootstrap-sass": {
... ...
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'),
... ... @@ -73,6 +80,8 @@ gulp.task(&#39;html&#39;, [&#39;inject&#39;, &#39;partials&#39;], function () {
73 80 .pipe($.useref())
74 81 .pipe($.revReplace({prefix: noosferoThemePrefix}))
75 82 .pipe(htmlFilter)
  83 + .pipe($.replace('/bower_components/ng-ckeditor/libs/ckeditor/', noosferoThemePrefix + 'ng-ckeditor/libs/ckeditor/'))
  84 + .pipe($.replace('/bower_components/ng-ckeditor/ng-ckeditor.min.js', noosferoThemePrefix + 'ng-ckeditor/ng-ckeditor.min.js'))
76 85 .pipe($.minifyHtml({
77 86 empty: true,
78 87 spare: true,
... ... @@ -93,6 +102,10 @@ gulp.task(&#39;fonts&#39;, function () {
93 102 .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/')));
94 103 });
95 104  
  105 +gulp.task('ckeditor', function () {
  106 + return gulp.src(['bower_components/ng-ckeditor/**/*']).pipe(gulp.dest(path.join(conf.paths.dist, '/ng-ckeditor')));
  107 +});
  108 +
96 109 gulp.task('locale', function () {
97 110 return gulp.src([
98 111 path.join("bower_components/angular-i18n", '*.js'),
... ... @@ -124,6 +137,10 @@ gulp.task(&#39;clean-docs&#39;, [], function() {
124 137 return $.del([path.join(conf.paths.docs, '/')]);
125 138 });
126 139  
  140 +gulp.task('plugin-languages', ['locale'], function() {
  141 + return languages.pluginLanguages(conf.paths.dist);
  142 +});
  143 +
127 144 gulp.task('noosfero', ['html'], function () {
128 145 var layouts = gulp.src('layouts/**/*')
129 146 .pipe(gulp.dest(path.join(conf.paths.dist, "layouts")));
... ... @@ -136,4 +153,4 @@ gulp.task(&#39;noosfero&#39;, [&#39;html&#39;], function () {
136 153 return merge(layouts, theme, index);
137 154 });
138 155  
139   -gulp.task('build', ['html', 'fonts', 'other', 'locale', 'noosfero']);
  156 +gulp.task('build', ['html', 'fonts', 'other', 'locale', 'ckeditor', '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";
... ... @@ -34,7 +36,7 @@ exports.configTheme(argv.theme);
34 36 * to inject css preprocessor deps and js files in karma
35 37 */
36 38 exports.wiredep = {
37   - exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/],
  39 + exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/, /ckeditor/],
38 40 directory: 'bower_components'
39 41 };
40 42  
... ...
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");
... ...
layouts/angular-layout.html.erb
1   -<% if params[:angular_theme_old] || request.fullpath.start_with?('/myprofile') %>
  1 +<% if params[:angular_theme_old] %>
2 2 <%= render file: Rails.root.join("app/views/layouts/application-ng.html.erb"), use_full_path: false %>
3 3 <% else %>
4 4 <%= from_theme_include(current_theme, "index") %>
... ...
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
... ... @@ -4,6 +4,10 @@
4 4 </div>
5 5  
6 6 <div class="sub-header clearfix">
  7 + <a href="#" class="btn btn-default btn-xs" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})">
  8 + <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}}
  9 + </a>
  10 + <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar>
7 11 <div class="page-info pull-right small text-muted">
8 12 <span class="time">
9 13 <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span>
... ... @@ -16,9 +20,9 @@
16 20 </span>
17 21 </div>
18 22 </div>
19   -
  23 + <noosfero-hotspot-article-content [article]="ctrl.article"></noosfero-hotspot-article-content>
20 24 <div class="page-body">
21   - <div ng-bind-html="ctrl.article.body"></div>
  25 + <div bind-html-compile="ctrl.article.body"></div>
22 26 </div>
23 27  
24 28 <noosfero-comments [article]="ctrl.article"></noosfero-comments>
... ...
src/app/article/basic-editor.component.spec.ts
... ... @@ -1,55 +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 profile = { id: 1 };
13   - let notification: any;
14   -
15   -
16   - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => {
17   - $rootScope = _$rootScope_;
18   - $q = _$q_;
19   - }));
20   -
21   - beforeEach(() => {
22   - $state = jasmine.createSpyObj("$state", ["transitionTo"]);
23   - notification = jasmine.createSpyObj("notification", ["success"]);
24   - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]);
25   - articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInProfile"]);
26   -
27   - let getCurrentProfileResponse = $q.defer();
28   - getCurrentProfileResponse.resolve(profile);
29   -
30   - let articleCreate = $q.defer();
31   - articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } });
32   -
33   - profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise);
34   - articleServiceMock.createInProfile = jasmine.createSpy("createInProfile").and.returnValue(articleCreate.promise);
35   - });
36   -
37   - it("create an article in the current profile when save", done => {
38   - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification);
39   - component.save();
40   - $rootScope.$apply();
41   - expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled();
42   - expect(articleServiceMock.createInProfile).toHaveBeenCalledWith(profile, component.article);
43   - done();
44   - });
45   -
46   - it("got to the new article page and display an alert when saving sucessfully", done => {
47   - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification);
48   - component.save();
49   - $rootScope.$apply();
50   - expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" });
51   - expect(notification.success).toHaveBeenCalled();
52   - done();
53   - });
54   -
55   -});
src/app/article/basic-editor.component.ts
... ... @@ -1,35 +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.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)
16   -export class BasicEditorComponent {
17   -
18   - article: noosfero.Article = <noosfero.Article>{};
19   -
20   - constructor(private articleService: ArticleService,
21   - private profileService: ProfileService,
22   - private $state: ng.ui.IStateService,
23   - private notification: NotificationService) { }
24   -
25   - save() {
26   - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
27   - return this.articleService.createInProfile(profile, this.article);
28   - }).then((response: noosfero.RestResult<noosfero.Article>) => {
29   - let article = (<noosfero.Article>response.data);
30   - this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier });
31   - this.notification.success({ title: "Good job!", message: "Article saved!" });
32   - });
33   - }
34   -
35   -}
src/app/article/basic-editor.html
... ... @@ -1,11 +0,0 @@
1   -<form>
2   - <div class="form-group">
3   - <label for="titleInput">Title</label>
4   - <input type="text" class="form-control" id="titleInput" placeholder="title" ng-model="vm.article.name">
5   - </div>
6   - <div class="form-group">
7   - <label for="bodyInput">Text</label>
8   - <textarea class="form-control" id="bodyInput" rows="10" ng-model="vm.article.body"></textarea>
9   - </div>
10   - <button type="submit" class="btn btn-default" ng-click="vm.save()">Save</button>
11   -</form>
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/content-viewer-actions.component.spec.ts
1   -import {providers} from 'ng-forward/cjs/testing/providers';
2   -
3 1 import {Input, Component, provide} from 'ng-forward';
4 2  
5 3 import * as helpers from "../../../spec/helpers";
6   -
7   -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
  4 +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper';
8 5 import {ContentViewerActionsComponent} from './content-viewer-actions.component';
9 6  
10 7 // this htmlTemplate will be re-used between the container components in this spec file
... ... @@ -12,56 +9,57 @@ const htmlTemplate: string = &#39;&lt;content-viewer-actions [article]=&quot;ctrl.article&quot; [
12 9  
13 10 describe('Content Viewer Actions Component', () => {
14 11  
15   - beforeEach(() => {
  12 + let helper: ComponentTestHelper<ContentViewerActionsComponent>;
16 13  
17   - angular.mock.module("templates");
  14 + beforeEach(angular.mock.module("templates"));
18 15  
19   - providers((provide: any) => {
20   - return <any>[
21   - provide('ProfileService', {
22   - useValue: helpers.mocks.profileService
23   - })
24   - ];
25   - });
26   - });
  16 + let providers = [
  17 + provide('ProfileService', {
  18 + useValue: helpers.mocks.profileService
  19 + }),
  20 + provide('ArticleService', {
  21 + useValue: helpers.mocks.articleService
  22 + })
  23 + ].concat(helpers.provideFilters("translateFilter"));
27 24  
28   - let buildComponent = (): Promise<ComponentFixture> => {
29   - return helpers.quickCreateComponent({
30   - providers: [
31   - helpers.provideEmptyObjects('Restangular'),
32   - helpers.provideFilters('translateFilter')
33   - ],
  25 + beforeEach((done) => {
  26 + let cls = createClass({
  27 + template: htmlTemplate,
34 28 directives: [ContentViewerActionsComponent],
35   - template: htmlTemplate
  29 + providers: providers
36 30 });
37   - };
  31 + helper = new ComponentTestHelper<ContentViewerActionsComponent>(cls, done);
  32 + });
38 33  
39   - it('renders content viewer actions directive', (done: Function) => {
40   - buildComponent().then((fixture: ComponentFixture) => {
41   - expect(fixture.debugElement.query('content-viewer-actions').length).toEqual(1);
  34 + it('renders content viewer actions directive', () => {
  35 + expect(helper.all("content-viewer-actions").length).toEqual(1);
  36 + });
42 37  
43   - done();
44   - });
  38 + it('return article parent as container when it is not a folder', () => {
  39 + let article = <noosfero.Article>({ id: 1, type: 'TextArticle', parent: { id: 2 } });
  40 + expect(helper.component.getArticleContainer(article)).toEqual(2);
  41 + });
  42 +
  43 + it('return article as container when it is a folder', () => {
  44 + let article = <noosfero.Article>({ id: 1, type: 'Folder' });
  45 + expect(helper.component.getArticleContainer(article)).toEqual(1);
45 46 });
46 47  
47   - it('check if profile was loaded', (done: Function) => {
  48 + it('return article as container when it is a blog', () => {
  49 + let article = <noosfero.Article>({ id: 1, type: 'Blog' });
  50 + expect(helper.component.getArticleContainer(article)).toEqual(1);
  51 + });
  52 +
  53 + it('check if profile was loaded', () => {
48 54 let profile: any = {
49 55 id: 1,
50 56 identifier: 'the-profile-test',
51 57 type: 'Person'
52 58 };
53   -
54 59 helpers.mocks.profileService.getCurrentProfile = () => {
55 60 return helpers.mocks.promiseResultTemplate(profile);
56 61 };
57   -
58   - buildComponent().then((fixture: ComponentFixture) => {
59   - let contentViewerComp: ContentViewerActionsComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
60   -
61   - expect(contentViewerComp.profile).toEqual(jasmine.objectContaining(profile));
62   -
63   - done();
64   - });
  62 + let component = new ContentViewerActionsComponent(<any>helpers.mocks.profileService, <any>helpers.mocks.articleService);
  63 + expect(component.profile).toEqual(jasmine.objectContaining(profile));
65 64 });
66   -
67 65 });
... ...
src/app/article/content-viewer/content-viewer-actions.component.ts
1 1 import {Component, Inject, provide} from "ng-forward";
2 2 import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service";
  3 +import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service";
3 4  
4 5 @Component({
5 6 selector: "content-viewer-actions",
6 7 templateUrl: "app/article/content-viewer/navbar-actions.html",
7   - providers: [provide('profileService', { useClass: ProfileService })]
  8 + providers: [
  9 + provide('profileService', { useClass: ProfileService }),
  10 + provide('articleService', { useClass: ArticleService })
  11 + ]
8 12 })
9   -@Inject(ProfileService)
  13 +@Inject(ProfileService, ArticleService)
10 14 export class ContentViewerActionsComponent {
11 15  
12 16 article: noosfero.Article;
13 17 profile: noosfero.Profile;
  18 + parentId: number;
14 19  
15   - constructor(profileService: ProfileService) {
  20 + constructor(profileService: ProfileService, articleService: ArticleService) {
16 21 profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
17 22 this.profile = profile;
  23 + return articleService.getCurrent();
  24 + }).then((article: noosfero.Article) => {
  25 + this.article = article;
  26 + this.parentId = this.getArticleContainer(article);
18 27 });
19 28 }
  29 +
  30 + getArticleContainer(article: noosfero.Article) {
  31 + // FIXME get folder types from api
  32 + if (article.type === "Blog" || article.type === "Folder") {
  33 + return article.id;
  34 + } else if (article.parent) {
  35 + return article.parent.id;
  36 + }
  37 + }
20 38 }
... ...
src/app/article/content-viewer/content-viewer.component.ts
... ... @@ -28,11 +28,12 @@ export class ContentViewerComponent {
28 28 }
29 29  
30 30 activate() {
31   - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
  31 + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
32 32 this.profile = profile;
33 33 return this.articleService.getArticleByProfileAndPath(this.profile, this.$stateParams["page"]);
34 34 }).then((result: noosfero.RestResult<any>) => {
35 35 this.article = <noosfero.Article>result.data;
  36 + this.articleService.setCurrent(this.article);
36 37 });
37 38 }
38 39 }
... ...
src/app/article/content-viewer/navbar-actions.html
1 1 <ul class="nav navbar-nav">
2 2 <li ng-show="vm.profile">
3   - <a href="#" role="button" ui-sref="main.profile.cms({profile: vm.profile.identifier})">
  3 + <a ng-show="vm.parentId" href="#" role="button" ui-sref="main.cms({profile: vm.profile.identifier, parent_id: vm.parentId})">
  4 + <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}}
  5 + </a>
  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'})">
4 9 <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}}
5 10 </a>
6 11 </li>
... ...
src/app/article/index.ts
1 1 /* Module Index Entry - generated using the script npm run generate-index */
2 2 export * from "./article-default-view.component";
3   -export * from "./basic-editor.component";
... ...
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";
... ... @@ -14,16 +14,17 @@ declare var moment: any;
14 14  
15 15 let noosferoApp: any = bundle("noosferoApp", MainComponent, ["ngAnimate", "ngCookies", "ngStorage", "ngTouch",
16 16 "ngSanitize", "ngMessages", "ngAria", "restangular",
17   - "ui.router", "ui.bootstrap", "toastr",
18   - "angularMoment", "angular.filter", "akoenig.deckgrid",
  17 + "ui.router", "ui.bootstrap", "toastr", "ngCkeditor",
  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;
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(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   -});
50 0 \ No newline at end of file
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;
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(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   -});
73 0 \ No newline at end of file
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   -});