Commit b9abd2b869a1c1b804344ed3d057ba7682ad51e4
Exists in
updated_signup_page
and in
2 other branches
Merge branch 'master' into staging
Showing
193 changed files
with
4023 additions
and
1233 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 193 files displayed.
bower.json
... | ... | @@ -35,6 +35,8 @@ |
35 | 35 | "angular-i18n": "^1.5.0", |
36 | 36 | "angular-load": "^0.4.1", |
37 | 37 | "angular-translate-interpolation-messageformat": "^2.10.0", |
38 | + "angular-bind-html-compile": "^1.2.1", | |
39 | + "angular-click-outside": "^2.7.1", | |
38 | 40 | "ng-ckeditor": "^0.2.1", |
39 | 41 | "ckeditor": "^4.5.8" |
40 | 42 | }, | ... | ... |
gulp/build.js
... | ... | @@ -6,6 +6,7 @@ var rename = require('gulp-rename'); |
6 | 6 | var insert = require('gulp-insert'); |
7 | 7 | var merge = require('merge-stream'); |
8 | 8 | var conf = require('./conf'); |
9 | +var languages = require('./languages'); | |
9 | 10 | |
10 | 11 | var themeName = conf.paths.theme.replace('-', ' '); |
11 | 12 | themeName = themeName.charAt(0).toUpperCase() + themeName.slice(1); |
... | ... | @@ -16,25 +17,31 @@ var $ = require('gulp-load-plugins')({ |
16 | 17 | }); |
17 | 18 | |
18 | 19 | gulp.task('partials', function () { |
19 | - var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')]; | |
20 | - conf.paths.allSources.forEach(function(src) { | |
21 | - srcPaths.push(path.join(src, '/app/**/*.html')); | |
20 | + var merged = merge(); | |
21 | + ['app', conf.paths.plugins].forEach(function(partialPath) { | |
22 | + var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')]; | |
23 | + conf.paths.allSources.forEach(function(src) { | |
24 | + srcPaths.push(path.join(src, partialPath, '/**/*.html')); | |
25 | + }); | |
26 | + merged.add(gulp.src(srcPaths) | |
27 | + .pipe($.minifyHtml({ | |
28 | + empty: true, | |
29 | + spare: true, | |
30 | + quotes: true | |
31 | + })) | |
32 | + .pipe($.angularTemplatecache('templateCacheHtml-'+partialPath+'.js', { | |
33 | + module: 'noosferoApp', | |
34 | + root: partialPath | |
35 | + })) | |
36 | + .pipe(gulp.dest(conf.paths.tmp + '/partials/'))); | |
22 | 37 | }); |
23 | - return gulp.src(srcPaths) | |
24 | - .pipe($.minifyHtml({ | |
25 | - empty: true, | |
26 | - spare: true, | |
27 | - quotes: true | |
28 | - })) | |
29 | - .pipe($.angularTemplatecache('templateCacheHtml.js', { | |
30 | - module: 'noosferoApp', | |
31 | - root: 'app' | |
32 | - })) | |
33 | - .pipe(gulp.dest(conf.paths.tmp + '/partials/')); | |
38 | + return merged; | |
34 | 39 | }); |
35 | 40 | |
36 | 41 | gulp.task('html', ['inject', 'partials'], function () { |
37 | - var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false }); | |
42 | + var partialsInjectFile = gulp.src([ | |
43 | + path.join(conf.paths.tmp, '/partials/templateCacheHtml-app.js'), | |
44 | + path.join(conf.paths.tmp, '/partials/templateCacheHtml-plugins.js')], { read: false }); | |
38 | 45 | var partialsInjectOptions = { |
39 | 46 | starttag: '<!-- inject:partials -->', |
40 | 47 | ignorePath: path.join(conf.paths.tmp, '/partials'), |
... | ... | @@ -124,6 +131,10 @@ gulp.task('clean-docs', [], function() { |
124 | 131 | return $.del([path.join(conf.paths.docs, '/')]); |
125 | 132 | }); |
126 | 133 | |
134 | +gulp.task('plugin-languages', ['locale'], function() { | |
135 | + return languages.pluginLanguages(conf.paths.dist); | |
136 | +}); | |
137 | + | |
127 | 138 | gulp.task('noosfero', ['html'], function () { |
128 | 139 | var layouts = gulp.src('layouts/**/*') |
129 | 140 | .pipe(gulp.dest(path.join(conf.paths.dist, "layouts"))); |
... | ... | @@ -136,4 +147,4 @@ gulp.task('noosfero', ['html'], function () { |
136 | 147 | return merge(layouts, theme, index); |
137 | 148 | }); |
138 | 149 | |
139 | -gulp.task('build', ['html', 'fonts', 'other', 'locale', 'noosfero']); | |
150 | +gulp.task('build', ['html', 'fonts', 'other', 'locale', 'plugin-languages', 'noosfero']); | ... | ... |
gulp/conf.js
... | ... | @@ -15,11 +15,13 @@ var path = require('path'); |
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"; | ... | ... |
... | ... | @@ -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('watch', ['inject'], 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('watch', ['inject'], 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
package.json
src/app/article/article-default-view.component.ts
1 | 1 | import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; |
2 | 2 | import {ArticleBlogComponent} from "./types/blog/blog.component"; |
3 | 3 | import {CommentsComponent} from "./comment/comments.component"; |
4 | +import {MacroDirective} from "./macro/macro.directive"; | |
5 | +import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot.component"; | |
6 | +import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot.component"; | |
4 | 7 | |
5 | 8 | /** |
6 | 9 | * @ngdoc controller |
... | ... | @@ -30,7 +33,9 @@ export class ArticleDefaultViewComponent { |
30 | 33 | @Component({ |
31 | 34 | selector: 'noosfero-article', |
32 | 35 | template: 'not-used', |
33 | - directives: [ArticleDefaultViewComponent, ArticleBlogComponent, CommentsComponent] | |
36 | + directives: [ArticleDefaultViewComponent, ArticleBlogComponent, | |
37 | + CommentsComponent, MacroDirective, ArticleToolbarHotspotComponent, | |
38 | + ArticleContentHotspotComponent] | |
34 | 39 | }) |
35 | 40 | @Inject("$element", "$scope", "$injector", "$compile") |
36 | 41 | export class ArticleViewComponent { |
... | ... | @@ -40,7 +45,8 @@ export class ArticleViewComponent { |
40 | 45 | directiveName: string; |
41 | 46 | |
42 | 47 | ngOnInit() { |
43 | - let specificDirective = 'noosfero' + this.article.type; | |
48 | + let articleType = this.article.type.replace(/::/, ''); | |
49 | + let specificDirective = 'noosfero' + articleType; | |
44 | 50 | this.directiveName = "noosfero-default-article"; |
45 | 51 | if (this.$injector.has(specificDirective + 'Directive')) { |
46 | 52 | this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); |
... | ... | @@ -53,6 +59,5 @@ export class ArticleViewComponent { |
53 | 59 | private $scope: ng.IScope, |
54 | 60 | private $injector: ng.auto.IInjectorService, |
55 | 61 | private $compile: ng.ICompileService) { |
56 | - | |
57 | 62 | } |
58 | 63 | } | ... | ... |
src/app/article/article.html
... | ... | @@ -7,6 +7,7 @@ |
7 | 7 | <a href="#" class="btn btn-default btn-xs" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})"> |
8 | 8 | <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}} |
9 | 9 | </a> |
10 | + <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar> | |
10 | 11 | <div class="page-info pull-right small text-muted"> |
11 | 12 | <span class="time"> |
12 | 13 | <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> |
... | ... | @@ -19,9 +20,9 @@ |
19 | 20 | </span> |
20 | 21 | </div> |
21 | 22 | </div> |
22 | - | |
23 | + <noosfero-hotspot-article-content [article]="ctrl.article"></noosfero-hotspot-article-content> | |
23 | 24 | <div class="page-body"> |
24 | - <div ng-bind-html="ctrl.article.body"></div> | |
25 | + <div bind-html-compile="ctrl.article.body"></div> | |
25 | 26 | </div> |
26 | 27 | |
27 | 28 | <noosfero-comments [article]="ctrl.article"></noosfero-comments> | ... | ... |
src/app/article/basic-editor/basic-editor.component.spec.ts
... | ... | @@ -1,84 +0,0 @@ |
1 | -import {quickCreateComponent} from "../../../spec/helpers"; | |
2 | -import {BasicEditorComponent} from "./basic-editor.component"; | |
3 | - | |
4 | - | |
5 | -describe("Article BasicEditor", () => { | |
6 | - | |
7 | - let $rootScope: ng.IRootScopeService; | |
8 | - let $q: ng.IQService; | |
9 | - let articleServiceMock: any; | |
10 | - let profileServiceMock: any; | |
11 | - let $state: any; | |
12 | - let $stateParams: any; | |
13 | - let $window: any; | |
14 | - let profile = { id: 1 }; | |
15 | - let notification: any; | |
16 | - | |
17 | - | |
18 | - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
19 | - $rootScope = _$rootScope_; | |
20 | - $q = _$q_; | |
21 | - })); | |
22 | - | |
23 | - beforeEach(() => { | |
24 | - $window = jasmine.createSpyObj("$window", ["back"]); | |
25 | - $state = jasmine.createSpyObj("$state", ["go"]); | |
26 | - notification = jasmine.createSpyObj("notification", ["success"]); | |
27 | - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["setCurrentProfileByIdentifier"]); | |
28 | - articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInParent", "updateArticle", "get"]); | |
29 | - | |
30 | - $stateParams = { profile: "profile" }; | |
31 | - | |
32 | - let setCurrentProfileByIdentifierResponse = $q.defer(); | |
33 | - setCurrentProfileByIdentifierResponse.resolve(profile); | |
34 | - | |
35 | - let articleCreate = $q.defer(); | |
36 | - articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } }); | |
37 | - | |
38 | - let articleGet = $q.defer(); | |
39 | - articleGet.resolve({ data: { path: "parent-path", profile: { identifier: "profile" } } }); | |
40 | - | |
41 | - profileServiceMock.setCurrentProfileByIdentifier = jasmine.createSpy("setCurrentProfileByIdentifier").and.returnValue(setCurrentProfileByIdentifierResponse.promise); | |
42 | - articleServiceMock.createInParent = jasmine.createSpy("createInParent").and.returnValue(articleCreate.promise); | |
43 | - articleServiceMock.updateArticle = jasmine.createSpy("updateArticle").and.returnValue(articleCreate.promise); | |
44 | - articleServiceMock.get = jasmine.createSpy("get").and.returnValue(articleGet.promise); | |
45 | - }); | |
46 | - | |
47 | - it("create an article in the current profile when save", done => { | |
48 | - $stateParams['parent_id'] = 1; | |
49 | - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window); | |
50 | - component.save(); | |
51 | - $rootScope.$apply(); | |
52 | - expect(profileServiceMock.setCurrentProfileByIdentifier).toHaveBeenCalled(); | |
53 | - expect(articleServiceMock.createInParent).toHaveBeenCalledWith(1, component.article); | |
54 | - done(); | |
55 | - }); | |
56 | - | |
57 | - it("got to the new article page and display an alert when saving sucessfully", done => { | |
58 | - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window); | |
59 | - component.save(); | |
60 | - $rootScope.$apply(); | |
61 | - expect($state.go).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); | |
62 | - expect(notification.success).toHaveBeenCalled(); | |
63 | - done(); | |
64 | - }); | |
65 | - | |
66 | - it("go back when cancel article edition", done => { | |
67 | - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window); | |
68 | - $window.history = { back: jasmine.createSpy('back') }; | |
69 | - component.cancel(); | |
70 | - expect($window.history.back).toHaveBeenCalled(); | |
71 | - done(); | |
72 | - }); | |
73 | - | |
74 | - it("edit existing article when save", done => { | |
75 | - $stateParams['parent_id'] = null; | |
76 | - $stateParams['id'] = 2; | |
77 | - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams, $window); | |
78 | - component.save(); | |
79 | - $rootScope.$apply(); | |
80 | - expect(articleServiceMock.updateArticle).toHaveBeenCalledWith(component.article); | |
81 | - done(); | |
82 | - }); | |
83 | - | |
84 | -}); |
src/app/article/basic-editor/basic-editor.component.ts
... | ... | @@ -1,69 +0,0 @@ |
1 | -import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
2 | -import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service"; | |
3 | -import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | |
4 | -import {NotificationService} from "../../shared/services/notification.service.ts"; | |
5 | - | |
6 | -@Component({ | |
7 | - selector: 'article-basic-editor', | |
8 | - templateUrl: "app/article/basic-editor/basic-editor.html", | |
9 | - providers: [ | |
10 | - provide('articleService', { useClass: ArticleService }), | |
11 | - provide('profileService', { useClass: ProfileService }), | |
12 | - provide('notification', { useClass: NotificationService }) | |
13 | - ] | |
14 | -}) | |
15 | -@Inject(ArticleService, ProfileService, "$state", NotificationService, "$stateParams", "$window") | |
16 | -export class BasicEditorComponent { | |
17 | - | |
18 | - article: noosfero.Article = <noosfero.Article>{ type: "TextArticle" }; | |
19 | - parent: noosfero.Article = <noosfero.Article>{}; | |
20 | - | |
21 | - id: number; | |
22 | - parentId: number; | |
23 | - profileIdentifier: string; | |
24 | - | |
25 | - constructor(private articleService: ArticleService, | |
26 | - private profileService: ProfileService, | |
27 | - private $state: ng.ui.IStateService, | |
28 | - private notification: NotificationService, | |
29 | - private $stateParams: ng.ui.IStateParamsService, | |
30 | - private $window: ng.IWindowService) { | |
31 | - | |
32 | - this.parentId = this.$stateParams['parent_id']; | |
33 | - this.profileIdentifier = this.$stateParams["profile"]; | |
34 | - this.id = this.$stateParams['id']; | |
35 | - | |
36 | - if (this.parentId) { | |
37 | - this.articleService.get(this.parentId).then((result: noosfero.RestResult<noosfero.Article>) => { | |
38 | - this.parent = result.data; | |
39 | - }); | |
40 | - } | |
41 | - if (this.id) { | |
42 | - this.articleService.get(this.id).then((result: noosfero.RestResult<noosfero.Article>) => { | |
43 | - this.article = result.data; | |
44 | - this.article.name = this.article.title; // FIXME | |
45 | - }); | |
46 | - } | |
47 | - } | |
48 | - | |
49 | - save() { | |
50 | - this.profileService.setCurrentProfileByIdentifier(this.profileIdentifier).then((profile: noosfero.Profile) => { | |
51 | - if (this.id) { | |
52 | - return this.articleService.updateArticle(this.article); | |
53 | - } else { | |
54 | - return this.articleService.createInParent(this.parentId, this.article); | |
55 | - } | |
56 | - }).then((response: noosfero.RestResult<noosfero.Article>) => { | |
57 | - let article = (<noosfero.Article>response.data); | |
58 | - this.$state.go('main.profile.page', { page: article.path, profile: article.profile.identifier }); | |
59 | - this.notification.success({ title: "article.basic_editor.success.title", message: "article.basic_editor.success.message" }); | |
60 | - }).catch(() => { | |
61 | - this.notification.error({ message: "article.basic_editor.save.failed" }); | |
62 | - }); | |
63 | - } | |
64 | - | |
65 | - cancel() { | |
66 | - this.$window.history.back(); | |
67 | - } | |
68 | - | |
69 | -} |
src/app/article/basic-editor/basic-editor.html
... | ... | @@ -1,34 +0,0 @@ |
1 | -<div class="basic-editor"> | |
2 | - <div class="row"> | |
3 | - <div class="col-md-1"></div> | |
4 | - <div class="col-md-8"> | |
5 | - <form> | |
6 | - <div class="form-group"> | |
7 | - <label for="titleInput">{{"article.basic_editor.title" | translate}}</label> | |
8 | - <input type="text" class="form-control" id="titleInput" placeholder="{{'article.basic_editor.title' | translate}}" ng-model="vm.article.name"> | |
9 | - </div> | |
10 | - <div class="form-group"> | |
11 | - <label for="bodyInput">{{"article.basic_editor.body" | translate}}</label> | |
12 | - <html-editor [(value)]="vm.article.body"></html-editor> | |
13 | - </div> | |
14 | - <button type="submit" class="btn btn-default" ng-click="vm.save()">{{"article.basic_editor.save" | translate}}</button> | |
15 | - <button type="button" class="btn btn-danger" ng-click="vm.cancel()">{{"article.basic_editor.cancel" | translate}}</button> | |
16 | - </form> | |
17 | - </div> | |
18 | - <div class="side-options"> | |
19 | - <div class="visibility panel panel-default"> | |
20 | - <div class="panel-heading">{{"article.basic_editor.visibility" | translate}}</div> | |
21 | - <div class="panel-body"> | |
22 | - <div> | |
23 | - <input type="radio" ng-model="vm.article.published" ng-value="true"> | |
24 | - <i class="fa fa-unlock fa-fw"></i> {{"article.basic_editor.visibility.public" | translate}} | |
25 | - </div> | |
26 | - <div> | |
27 | - <input type="radio" ng-model="vm.article.published" ng-value="false"> | |
28 | - <i class="fa fa-lock fa-fw"></i> {{"article.basic_editor.visibility.private" | translate}} | |
29 | - </div> | |
30 | - </div> | |
31 | - </div> | |
32 | - </div> | |
33 | - </div> | |
34 | -</div> |
src/app/article/basic-editor/basic-editor.scss
... | ... | @@ -1,21 +0,0 @@ |
1 | -.basic-editor { | |
2 | - @extend .container-fluid; | |
3 | - padding: 0 1%; | |
4 | - | |
5 | - .side-options { | |
6 | - @extend .col-md-3; | |
7 | - margin-top: 25px; | |
8 | - | |
9 | - .visibility { | |
10 | - .panel-heading { | |
11 | - background-color: transparent; | |
12 | - font-weight: bold; | |
13 | - } | |
14 | - .panel-body { | |
15 | - i { | |
16 | - color: #A5A5A5; | |
17 | - } | |
18 | - } | |
19 | - } | |
20 | - } | |
21 | -} |
src/app/article/cms/article-editor/article-editor.component.ts
0 → 100644
... | ... | @@ -0,0 +1,27 @@ |
1 | +import {Component, Input, Inject} from 'ng-forward'; | |
2 | + | |
3 | +@Component({ | |
4 | + selector: 'article-editor', | |
5 | + template: "not-used" | |
6 | +}) | |
7 | +@Inject("$element", "$scope", "$injector", "$compile") | |
8 | +export class ArticleEditorComponent { | |
9 | + | |
10 | + @Input() article: noosfero.Article; | |
11 | + | |
12 | + constructor( | |
13 | + private $element: any, | |
14 | + private $scope: ng.IScope, | |
15 | + private $injector: ng.auto.IInjectorService, | |
16 | + private $compile: ng.ICompileService) { } | |
17 | + | |
18 | + ngOnInit() { | |
19 | + let articleType = this.article.type.replace(/::/, ''); | |
20 | + let specificDirective = `${articleType.charAt(0).toLowerCase()}${articleType.substring(1)}Editor`; | |
21 | + let directiveName = "article-basic-editor"; | |
22 | + if (this.$injector.has(specificDirective + 'Directive')) { | |
23 | + directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | |
24 | + } | |
25 | + this.$element.replaceWith(this.$compile('<' + directiveName + ' [article]="ctrl.article"></' + directiveName + '>')(this.$scope)); | |
26 | + } | |
27 | +} | ... | ... |
src/app/article/cms/basic-editor/basic-editor.component.ts
0 → 100644
... | ... | @@ -0,0 +1,10 @@ |
1 | +<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-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 | +} | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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 | +}); | ... | ... |
... | ... | @@ -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 | +} | ... | ... |
... | ... | @@ -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/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/post-comment/post-comment.component.spec.ts
... | ... | @@ -7,6 +7,8 @@ const htmlTemplate: string = '<noosfero-post-comment [article]="ctrl.article" [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("Components", () => { |
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("Components", () => { |
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 'ng-forward'; |
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
src/app/article/content-viewer/navbar-actions.html
... | ... | @@ -4,4 +4,9 @@ |
4 | 4 | <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} |
5 | 5 | </a> |
6 | 6 | </li> |
7 | + <li ng-show="vm.profile"> | |
8 | + <a href="#" role="button" ui-sref="main.cms({profile: vm.profile.identifier, parent_id: vm.parentId, type: 'CommentParagraphPlugin::Discussion'})"> | |
9 | + <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} | |
10 | + </a> | |
11 | + </li> | |
7 | 12 | </ul> | ... | ... |
... | ... | @@ -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 | +}); | ... | ... |
... | ... | @@ -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, '"'); | |
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 | +} | ... | ... |
... | ... | @@ -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 | +} | ... | ... |
... | ... | @@ -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 | +} | ... | ... |
... | ... | @@ -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 | +} | ... | ... |
... | ... | @@ -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 "./index.run"; |
5 | 5 | import {MainComponent} from "./main/main.component"; |
6 | 6 | import {bootstrap, bundle} from "ng-forward"; |
7 | 7 | |
8 | -import {AUTH_EVENTS} from "./login/auth-events"; | |
8 | +import {AuthEvents} from "./login/auth-events"; | |
9 | 9 | import {AuthController} from "./login/auth.controller"; |
10 | 10 | |
11 | 11 | import {Navbar} from "./layout/navbar/navbar"; |
... | ... | @@ -15,15 +15,16 @@ declare var moment: any; |
15 | 15 | let noosferoApp: any = bundle("noosferoApp", MainComponent, ["ngAnimate", "ngCookies", "ngStorage", "ngTouch", |
16 | 16 | "ngSanitize", "ngMessages", "ngAria", "restangular", |
17 | 17 | "ui.router", "ui.bootstrap", "toastr", "ngCkeditor", |
18 | - "angularMoment", "angular.filter", "akoenig.deckgrid", | |
18 | + "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid", | |
19 | 19 | "angular-timeline", "duScroll", "oitozero.ngSweetAlert", |
20 | - "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad"]).publish(); | |
20 | + "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad", | |
21 | + "angular-click-outside"]).publish(); | |
21 | 22 | |
22 | 23 | NoosferoApp.angularModule = noosferoApp; |
23 | 24 | |
24 | 25 | |
25 | 26 | NoosferoApp.addConstants("moment", moment); |
26 | -NoosferoApp.addConstants("AUTH_EVENTS", AUTH_EVENTS); | |
27 | +NoosferoApp.addConstants("AuthEvents", AuthEvents); | |
27 | 28 | |
28 | 29 | NoosferoApp.addConfig(noosferoModuleConfig); |
29 | 30 | NoosferoApp.run(noosferoAngularRunBlock); | ... | ... |
src/app/layout/blocks/block.component.ts
... | ... | @@ -11,7 +11,7 @@ export class BlockComponent { |
11 | 11 | @Input() owner: any; |
12 | 12 | |
13 | 13 | ngOnInit() { |
14 | - let blockName = (this.block && this.block.type) ? this.block.type.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
14 | + let blockName = (this.block && this.block.type) ? this.block.type.replace(/::/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
15 | 15 | this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope)); |
16 | 16 | } |
17 | 17 | ... | ... |
src/app/layout/blocks/communities-block/communities-block.component.spec.ts
... | ... | @@ -1,51 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Provider, Input, provide, Component} from 'ng-forward'; | |
3 | - | |
4 | -import {CommunitiesBlockComponent} from './communities-block.component'; | |
5 | - | |
6 | -const htmlTemplate: string = '<noosfero-communities-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-communities-block>'; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - describe("Communities Block Component", () => { | |
12 | - | |
13 | - beforeEach(angular.mock.module("templates")); | |
14 | - | |
15 | - let state = jasmine.createSpyObj("state", ["go"]); | |
16 | - let providers = [ | |
17 | - new Provider('truncateFilter', { useValue: () => { } }), | |
18 | - new Provider('stripTagsFilter', { useValue: () => { } }), | |
19 | - new Provider('$state', { useValue: state }), | |
20 | - new Provider('CommunityService', { | |
21 | - useValue: { | |
22 | - getByOwner: (owner: any, params: any): any => { | |
23 | - return Promise.resolve({ data: [{ identifier: "community1" }] }); | |
24 | - } | |
25 | - } | |
26 | - }), | |
27 | - ]; | |
28 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [CommunitiesBlockComponent], providers: providers }) | |
29 | - class BlockContainerComponent { | |
30 | - block = { type: 'Block', settings: {} }; | |
31 | - owner = { name: 'profile-name' }; | |
32 | - } | |
33 | - | |
34 | - it("get communities", done => { | |
35 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
36 | - let block: CommunitiesBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
37 | - expect(block.profiles).toEqual([{ identifier: "community1" }]); | |
38 | - done(); | |
39 | - }); | |
40 | - }); | |
41 | - | |
42 | - it("render the profile image for each community", done => { | |
43 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
44 | - fixture.debugElement.getLocal("$rootScope").$apply(); | |
45 | - expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1); | |
46 | - done(); | |
47 | - }); | |
48 | - }); | |
49 | - | |
50 | - }); | |
51 | -}); |
src/app/layout/blocks/communities-block/communities-block.component.ts
... | ... | @@ -1,24 +0,0 @@ |
1 | -import {Input, Inject, Component} from "ng-forward"; | |
2 | -import {CommunityService} from "../../../../lib/ng-noosfero-api/http/community.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-communities-block", | |
6 | - templateUrl: 'app/layout/blocks/communities-block/communities-block.html', | |
7 | -}) | |
8 | -@Inject(CommunityService) | |
9 | -export class CommunitiesBlockComponent { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Profile; | |
13 | - | |
14 | - profiles: any = []; | |
15 | - | |
16 | - constructor(private communityService: CommunityService) { } | |
17 | - | |
18 | - ngOnInit() { | |
19 | - let limit: number = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5; | |
20 | - this.communityService.getByOwner(this.owner, { limit: limit }).then((result: noosfero.RestResult<noosfero.Community[]>) => { | |
21 | - this.profiles = result.data; | |
22 | - }); | |
23 | - } | |
24 | -} |
src/app/layout/blocks/communities-block/communities-block.html
src/app/layout/blocks/communities-block/communities-block.scss
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
src/app/layout/blocks/communities/communities-block.scss
0 → 100644
src/app/layout/blocks/link-list/index.ts
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 | +} | ... | ... |
... | ... | @@ -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
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
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> |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | +} | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<div ui-view="mainBlockContent" autoscroll></div> | ... | ... |
src/app/layout/blocks/members-block/index.ts
src/app/layout/blocks/members-block/members-block.component.spec.ts
... | ... | @@ -1,49 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Provider, Input, provide, Component} from 'ng-forward'; | |
3 | -import {MembersBlockComponent} from './members-block.component'; | |
4 | -import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper'; | |
5 | - | |
6 | -const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>'; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - describe("Members Block Component", () => { | |
12 | - | |
13 | - let helper: ComponentTestHelper<MembersBlockComponent>; | |
14 | - | |
15 | - let providers = [ | |
16 | - new Provider('ProfileService', { | |
17 | - useValue: { | |
18 | - getProfileMembers: (profileId: number, filters: any): any => { | |
19 | - return Promise.resolve({ data: { people: [{ identifier: "person1" }] } }); | |
20 | - } | |
21 | - } | |
22 | - }), | |
23 | - ]; | |
24 | - | |
25 | - beforeEach(angular.mock.module("templates")); | |
26 | - | |
27 | - beforeEach((done) => { | |
28 | - // Custom properties for the component | |
29 | - let properties = { owner: { id: 1 } }; | |
30 | - // Create the component bed for the test. | |
31 | - let cls = createClass({ | |
32 | - template: htmlTemplate, | |
33 | - directives: [MembersBlockComponent], | |
34 | - providers: providers, | |
35 | - properties: properties | |
36 | - }); | |
37 | - helper = new ComponentTestHelper<MembersBlockComponent>(cls, done); | |
38 | - }); | |
39 | - | |
40 | - it("get members of the block owner", () => { | |
41 | - expect(helper.component.members[0].identifier).toEqual("person1"); | |
42 | - }); | |
43 | - | |
44 | - it("render the profile image for each member", () => { | |
45 | - expect(helper.all("noosfero-profile-image").length).toEqual(1); | |
46 | - }); | |
47 | - | |
48 | - }); | |
49 | -}); |
src/app/layout/blocks/members-block/members-block.component.ts
... | ... | @@ -1,25 +0,0 @@ |
1 | -import {Input, Inject, Component} from "ng-forward"; | |
2 | -import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-members-block", | |
6 | - templateUrl: 'app/layout/blocks/members-block/members-block.html', | |
7 | -}) | |
8 | -@Inject(ProfileService) | |
9 | -export class MembersBlockComponent { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Profile; | |
13 | - | |
14 | - members: any = []; | |
15 | - | |
16 | - constructor(private profileService: ProfileService) { | |
17 | - | |
18 | - } | |
19 | - | |
20 | - ngOnInit() { | |
21 | - this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => { | |
22 | - this.members = response.data.people; | |
23 | - }); | |
24 | - } | |
25 | -} |
src/app/layout/blocks/members-block/members-block.html
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/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 | +} | ... | ... |
... | ... | @@ -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
src/app/layout/blocks/people-block/people-block.component.spec.ts
... | ... | @@ -1,72 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Provider} from 'ng-forward'; | |
3 | -import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper'; | |
4 | -import {providers} from 'ng-forward/cjs/testing/providers'; | |
5 | -import {PeopleBlockComponent} from './people-block.component'; | |
6 | - | |
7 | -const htmlTemplate: string = '<noosfero-people-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-people-block>'; | |
8 | - | |
9 | -describe("Components", () => { | |
10 | - | |
11 | - describe("People Block Component", () => { | |
12 | - let serviceMock = { | |
13 | - getEnvironmentPeople: (filters: any): any => { | |
14 | - return Promise.resolve([{ identifier: "person1" }]); | |
15 | - } | |
16 | - }; | |
17 | - let providers = [new Provider('EnvironmentService', { useValue: serviceMock })]; | |
18 | - | |
19 | - let helper: ComponentTestHelper<PeopleBlockComponent>; | |
20 | - | |
21 | - beforeEach(angular.mock.module("templates")); | |
22 | - | |
23 | - /** | |
24 | - * The beforeEach procedure will initialize the helper and parse | |
25 | - * the component according to the given providers. Unfortunetly, in | |
26 | - * this mode, the providers and properties given to the construtor | |
27 | - * can't be overriden. | |
28 | - */ | |
29 | - beforeEach((done) => { | |
30 | - // Create the component bed for the test. Optionally, this could be done | |
31 | - // in each test if one needs customization of these parameters per test | |
32 | - let cls = createClass({ | |
33 | - template: htmlTemplate, | |
34 | - directives: [PeopleBlockComponent], | |
35 | - providers: providers, | |
36 | - properties: {} | |
37 | - }); | |
38 | - helper = new ComponentTestHelper<PeopleBlockComponent>(cls, done); | |
39 | - }); | |
40 | - | |
41 | - /** | |
42 | - * By default the helper will have the component, with all properties | |
43 | - * ready to be used. Here the mock provider 'EnvironmentService' will | |
44 | - * return the given array with one person. | |
45 | - */ | |
46 | - it("get block with one people", () => { | |
47 | - expect(helper.component.people[0].identifier).toEqual("person1"); | |
48 | - }); | |
49 | - | |
50 | - /** | |
51 | - * There are helper functions to access the JQuery DOM like this. | |
52 | - */ | |
53 | - it("render the profile image for each person", () => { | |
54 | - expect(helper.all("noosfero-profile-image").length).toEqual(1); | |
55 | - }); | |
56 | - | |
57 | - /** | |
58 | - * The main debugElement element is also available | |
59 | - */ | |
60 | - it("render the main noosfero people block", () => { | |
61 | - expect(helper.debugElement.children().length).toEqual(1, "The people-block should have a div children"); | |
62 | - }); | |
63 | - | |
64 | - /** | |
65 | - * Just another example of a JQuery DOM helper function | |
66 | - */ | |
67 | - it("render the noosfero people block div", () => { | |
68 | - let div = helper.findChildren("noosfero-people-block", "div"); | |
69 | - expect(div.className).toBe('people-block', "The class should be people-block"); | |
70 | - }); | |
71 | - }); | |
72 | -}); |
src/app/layout/blocks/people-block/people-block.component.ts
... | ... | @@ -1,26 +0,0 @@ |
1 | -import {Input, Inject, Component} from "ng-forward"; | |
2 | -import {EnvironmentService} from "../../../../lib/ng-noosfero-api/http/environment.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-people-block", | |
6 | - templateUrl: 'app/layout/blocks/people-block/people-block.html', | |
7 | -}) | |
8 | -@Inject(EnvironmentService) | |
9 | -export class PeopleBlockComponent { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Environment; | |
13 | - private type: string = "people"; | |
14 | - | |
15 | - people: noosfero.Person[] = []; | |
16 | - | |
17 | - constructor(private environmentService: EnvironmentService) { | |
18 | - } | |
19 | - | |
20 | - ngOnInit() { | |
21 | - this.environmentService.getEnvironmentPeople({ limit: '6' }).then((people: noosfero.Person[]) => { | |
22 | - this.people = people; | |
23 | - }); | |
24 | - } | |
25 | - | |
26 | -} |
src/app/layout/blocks/people-block/people-block.html
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/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 | +}); | ... | ... |
... | ... | @@ -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 | +} | ... | ... |
... | ... | @@ -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
src/app/layout/blocks/profile-image-block/profile-image-block.component.spec.ts
... | ... | @@ -1,46 +0,0 @@ |
1 | -import {TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Pipe, Input, provide, Component} from 'ng-forward'; | |
3 | - | |
4 | -import {ProfileImageBlockComponent} from './profile-image-block.component'; | |
5 | - | |
6 | -import * as helpers from "./../../../../spec/helpers"; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -const htmlTemplate: string = '<noosfero-profile-image-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-profile-image-block>'; | |
11 | - | |
12 | -describe("Components", () => { | |
13 | - | |
14 | - describe("Profile Image Block Component", () => { | |
15 | - | |
16 | - beforeEach(angular.mock.module("templates")); | |
17 | - | |
18 | - @Component({ | |
19 | - selector: 'test-container-component', | |
20 | - template: htmlTemplate, | |
21 | - directives: [ProfileImageBlockComponent], | |
22 | - providers: helpers.provideFilters("translateFilter") | |
23 | - }) | |
24 | - class BlockContainerComponent { | |
25 | - block = { type: 'Block' }; | |
26 | - owner = { name: 'profile-name' }; | |
27 | - constructor() { | |
28 | - } | |
29 | - } | |
30 | - | |
31 | - it("show image if present", () => { | |
32 | - helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
33 | - let elProfile = fixture.debugElement.componentViewChildren[0]; | |
34 | - expect(elProfile.query('div.profile-image-block').length).toEqual(1); | |
35 | - }); | |
36 | - }); | |
37 | - | |
38 | - it("has link to the profile", () => { | |
39 | - helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | |
40 | - let elProfile = fixture.debugElement.componentViewChildren[0]; | |
41 | - expect(elProfile.query('a.settings-link').length).toEqual(1); | |
42 | - }); | |
43 | - }); | |
44 | - | |
45 | - }); | |
46 | -}); |
src/app/layout/blocks/profile-image-block/profile-image-block.component.ts
... | ... | @@ -1,14 +0,0 @@ |
1 | -import {Inject, Input, Component} from "ng-forward"; | |
2 | -import {ProfileImageComponent} from "./../../../profile/image/image.component"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-profile-image-block", | |
6 | - templateUrl: 'app/layout/blocks/profile-image-block/profile-image-block.html', | |
7 | - directives: [ProfileImageComponent] | |
8 | -}) | |
9 | -export class ProfileImageBlockComponent { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Profile; | |
13 | - | |
14 | -} |
src/app/layout/blocks/profile-image-block/profile-image-block.html
... | ... | @@ -1,6 +0,0 @@ |
1 | -<div class="center-block text-center profile-image-block"> | |
2 | - <a ui-sref="main.profile.info({profile: ctrl.owner.identifier})"> | |
3 | - <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image> | |
4 | - </a> | |
5 | - <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a> | |
6 | -</div> |
src/app/layout/blocks/profile-image-block/profile-image-block.scss