Commit 49c1ee2611f73c70c3f6873b49c2fa9d369d8af2
Exists in
master
and in
26 other branches
Merging with master
Merge remote-tracking branch 'origin/master' into login-block
Showing
204 changed files
with
4245 additions
and
1182 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 204 files displayed.
bower.json
... | ... | @@ -34,10 +34,14 @@ |
34 | 34 | "angular-dynamic-locale": "^0.1.30", |
35 | 35 | "angular-i18n": "^1.5.0", |
36 | 36 | "angular-load": "^0.4.1", |
37 | - "angular-translate-interpolation-messageformat": "^2.10.0" | |
37 | + "angular-translate-interpolation-messageformat": "^2.10.0", | |
38 | + "angular-bind-html-compile": "^1.2.1", | |
39 | + "angular-click-outside": "^2.7.1" | |
38 | 40 | }, |
39 | 41 | "devDependencies": { |
40 | - "angular-mocks": "~1.5.0" | |
42 | + "angular-mocks": "~1.5.0", | |
43 | + "ng-ckeditor": "^0.2.1", | |
44 | + "ckeditor": "^4.5.8" | |
41 | 45 | }, |
42 | 46 | "overrides": { |
43 | 47 | "bootstrap-sass": { | ... | ... |
gulp/build.js
... | ... | @@ -6,6 +6,7 @@ var rename = require('gulp-rename'); |
6 | 6 | var insert = require('gulp-insert'); |
7 | 7 | var merge = require('merge-stream'); |
8 | 8 | var conf = require('./conf'); |
9 | +var languages = require('./languages'); | |
9 | 10 | |
10 | 11 | var themeName = conf.paths.theme.replace('-', ' '); |
11 | 12 | themeName = themeName.charAt(0).toUpperCase() + themeName.slice(1); |
... | ... | @@ -16,25 +17,31 @@ var $ = require('gulp-load-plugins')({ |
16 | 17 | }); |
17 | 18 | |
18 | 19 | gulp.task('partials', function () { |
19 | - var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')]; | |
20 | - conf.paths.allSources.forEach(function(src) { | |
21 | - srcPaths.push(path.join(src, '/app/**/*.html')); | |
20 | + var merged = merge(); | |
21 | + ['app', conf.paths.plugins].forEach(function(partialPath) { | |
22 | + var srcPaths = [path.join(conf.paths.tmp, '/serve/app/**/*.html')]; | |
23 | + conf.paths.allSources.forEach(function(src) { | |
24 | + srcPaths.push(path.join(src, partialPath, '/**/*.html')); | |
25 | + }); | |
26 | + merged.add(gulp.src(srcPaths) | |
27 | + .pipe($.minifyHtml({ | |
28 | + empty: true, | |
29 | + spare: true, | |
30 | + quotes: true | |
31 | + })) | |
32 | + .pipe($.angularTemplatecache('templateCacheHtml-'+partialPath+'.js', { | |
33 | + module: 'noosferoApp', | |
34 | + root: partialPath | |
35 | + })) | |
36 | + .pipe(gulp.dest(conf.paths.tmp + '/partials/'))); | |
22 | 37 | }); |
23 | - return gulp.src(srcPaths) | |
24 | - .pipe($.minifyHtml({ | |
25 | - empty: true, | |
26 | - spare: true, | |
27 | - quotes: true | |
28 | - })) | |
29 | - .pipe($.angularTemplatecache('templateCacheHtml.js', { | |
30 | - module: 'noosferoApp', | |
31 | - root: 'app' | |
32 | - })) | |
33 | - .pipe(gulp.dest(conf.paths.tmp + '/partials/')); | |
38 | + return merged; | |
34 | 39 | }); |
35 | 40 | |
36 | 41 | gulp.task('html', ['inject', 'partials'], function () { |
37 | - var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false }); | |
42 | + var partialsInjectFile = gulp.src([ | |
43 | + path.join(conf.paths.tmp, '/partials/templateCacheHtml-app.js'), | |
44 | + path.join(conf.paths.tmp, '/partials/templateCacheHtml-plugins.js')], { read: false }); | |
38 | 45 | var partialsInjectOptions = { |
39 | 46 | starttag: '<!-- inject:partials -->', |
40 | 47 | ignorePath: path.join(conf.paths.tmp, '/partials'), |
... | ... | @@ -73,6 +80,8 @@ gulp.task('html', ['inject', 'partials'], function () { |
73 | 80 | .pipe($.useref()) |
74 | 81 | .pipe($.revReplace({prefix: noosferoThemePrefix})) |
75 | 82 | .pipe(htmlFilter) |
83 | + .pipe($.replace('/bower_components/ng-ckeditor/libs/ckeditor/', noosferoThemePrefix + 'ng-ckeditor/libs/ckeditor/')) | |
84 | + .pipe($.replace('/bower_components/ng-ckeditor/ng-ckeditor.min.js', noosferoThemePrefix + 'ng-ckeditor/ng-ckeditor.min.js')) | |
76 | 85 | .pipe($.minifyHtml({ |
77 | 86 | empty: true, |
78 | 87 | spare: true, |
... | ... | @@ -93,6 +102,10 @@ gulp.task('fonts', function () { |
93 | 102 | .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/'))); |
94 | 103 | }); |
95 | 104 | |
105 | +gulp.task('ckeditor', function () { | |
106 | + return gulp.src(['bower_components/ng-ckeditor/**/*']).pipe(gulp.dest(path.join(conf.paths.dist, '/ng-ckeditor'))); | |
107 | +}); | |
108 | + | |
96 | 109 | gulp.task('locale', function () { |
97 | 110 | return gulp.src([ |
98 | 111 | path.join("bower_components/angular-i18n", '*.js'), |
... | ... | @@ -124,6 +137,10 @@ gulp.task('clean-docs', [], function() { |
124 | 137 | return $.del([path.join(conf.paths.docs, '/')]); |
125 | 138 | }); |
126 | 139 | |
140 | +gulp.task('plugin-languages', ['locale'], function() { | |
141 | + return languages.pluginLanguages(conf.paths.dist); | |
142 | +}); | |
143 | + | |
127 | 144 | gulp.task('noosfero', ['html'], function () { |
128 | 145 | var layouts = gulp.src('layouts/**/*') |
129 | 146 | .pipe(gulp.dest(path.join(conf.paths.dist, "layouts"))); |
... | ... | @@ -136,4 +153,4 @@ gulp.task('noosfero', ['html'], function () { |
136 | 153 | return merge(layouts, theme, index); |
137 | 154 | }); |
138 | 155 | |
139 | -gulp.task('build', ['html', 'fonts', 'other', 'locale', 'noosfero']); | |
156 | +gulp.task('build', ['html', 'fonts', 'other', 'locale', 'ckeditor', 'plugin-languages', 'noosfero']); | ... | ... |
gulp/conf.js
... | ... | @@ -15,11 +15,13 @@ var path = require('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"; |
... | ... | @@ -34,7 +36,7 @@ exports.configTheme(argv.theme); |
34 | 36 | * to inject css preprocessor deps and js files in karma |
35 | 37 | */ |
36 | 38 | exports.wiredep = { |
37 | - exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/], | |
39 | + exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/, /ckeditor/], | |
38 | 40 | directory: 'bower_components' |
39 | 41 | }; |
40 | 42 | ... | ... |
... | ... | @@ -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
layouts/angular-layout.html.erb
1 | -<% if params[:angular_theme_old] || request.fullpath.start_with?('/myprofile') %> | |
1 | +<% if params[:angular_theme_old] %> | |
2 | 2 | <%= render file: Rails.root.join("app/views/layouts/application-ng.html.erb"), use_full_path: false %> |
3 | 3 | <% else %> |
4 | 4 | <%= from_theme_include(current_theme, "index") %> | ... | ... |
package.json
src/app/article/article-default-view.component.ts
1 | 1 | import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; |
2 | 2 | import {ArticleBlogComponent} from "./types/blog/blog.component"; |
3 | 3 | import {CommentsComponent} from "./comment/comments.component"; |
4 | +import {MacroDirective} from "./macro/macro.directive"; | |
5 | +import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot.component"; | |
6 | +import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot.component"; | |
4 | 7 | |
5 | 8 | /** |
6 | 9 | * @ngdoc controller |
... | ... | @@ -30,7 +33,9 @@ export class ArticleDefaultViewComponent { |
30 | 33 | @Component({ |
31 | 34 | selector: 'noosfero-article', |
32 | 35 | template: 'not-used', |
33 | - directives: [ArticleDefaultViewComponent, ArticleBlogComponent, CommentsComponent] | |
36 | + directives: [ArticleDefaultViewComponent, ArticleBlogComponent, | |
37 | + CommentsComponent, MacroDirective, ArticleToolbarHotspotComponent, | |
38 | + ArticleContentHotspotComponent] | |
34 | 39 | }) |
35 | 40 | @Inject("$element", "$scope", "$injector", "$compile") |
36 | 41 | export class ArticleViewComponent { |
... | ... | @@ -40,7 +45,8 @@ export class ArticleViewComponent { |
40 | 45 | directiveName: string; |
41 | 46 | |
42 | 47 | ngOnInit() { |
43 | - let specificDirective = 'noosfero' + this.article.type; | |
48 | + let articleType = this.article.type.replace(/::/, ''); | |
49 | + let specificDirective = 'noosfero' + articleType; | |
44 | 50 | this.directiveName = "noosfero-default-article"; |
45 | 51 | if (this.$injector.has(specificDirective + 'Directive')) { |
46 | 52 | this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); |
... | ... | @@ -53,6 +59,5 @@ export class ArticleViewComponent { |
53 | 59 | private $scope: ng.IScope, |
54 | 60 | private $injector: ng.auto.IInjectorService, |
55 | 61 | private $compile: ng.ICompileService) { |
56 | - | |
57 | 62 | } |
58 | 63 | } | ... | ... |
src/app/article/article.html
... | ... | @@ -4,6 +4,10 @@ |
4 | 4 | </div> |
5 | 5 | |
6 | 6 | <div class="sub-header clearfix"> |
7 | + <a href="#" class="btn btn-default btn-xs" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})"> | |
8 | + <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}} | |
9 | + </a> | |
10 | + <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar> | |
7 | 11 | <div class="page-info pull-right small text-muted"> |
8 | 12 | <span class="time"> |
9 | 13 | <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> |
... | ... | @@ -16,9 +20,9 @@ |
16 | 20 | </span> |
17 | 21 | </div> |
18 | 22 | </div> |
19 | - | |
23 | + <noosfero-hotspot-article-content [article]="ctrl.article"></noosfero-hotspot-article-content> | |
20 | 24 | <div class="page-body"> |
21 | - <div ng-bind-html="ctrl.article.body"></div> | |
25 | + <div bind-html-compile="ctrl.article.body"></div> | |
22 | 26 | </div> |
23 | 27 | |
24 | 28 | <noosfero-comments [article]="ctrl.article"></noosfero-comments> | ... | ... |
src/app/article/basic-editor.component.spec.ts
... | ... | @@ -1,55 +0,0 @@ |
1 | -import {quickCreateComponent} from "../../spec/helpers"; | |
2 | -import {BasicEditorComponent} from "./basic-editor.component"; | |
3 | - | |
4 | - | |
5 | -describe("Article BasicEditor", () => { | |
6 | - | |
7 | - let $rootScope: ng.IRootScopeService; | |
8 | - let $q: ng.IQService; | |
9 | - let articleServiceMock: any; | |
10 | - let profileServiceMock: any; | |
11 | - let $state: any; | |
12 | - let profile = { id: 1 }; | |
13 | - let notification: any; | |
14 | - | |
15 | - | |
16 | - beforeEach(inject((_$rootScope_: ng.IRootScopeService, _$q_: ng.IQService) => { | |
17 | - $rootScope = _$rootScope_; | |
18 | - $q = _$q_; | |
19 | - })); | |
20 | - | |
21 | - beforeEach(() => { | |
22 | - $state = jasmine.createSpyObj("$state", ["transitionTo"]); | |
23 | - notification = jasmine.createSpyObj("notification", ["success"]); | |
24 | - profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]); | |
25 | - articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInProfile"]); | |
26 | - | |
27 | - let getCurrentProfileResponse = $q.defer(); | |
28 | - getCurrentProfileResponse.resolve(profile); | |
29 | - | |
30 | - let articleCreate = $q.defer(); | |
31 | - articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } }); | |
32 | - | |
33 | - profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | |
34 | - articleServiceMock.createInProfile = jasmine.createSpy("createInProfile").and.returnValue(articleCreate.promise); | |
35 | - }); | |
36 | - | |
37 | - it("create an article in the current profile when save", done => { | |
38 | - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification); | |
39 | - component.save(); | |
40 | - $rootScope.$apply(); | |
41 | - expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | |
42 | - expect(articleServiceMock.createInProfile).toHaveBeenCalledWith(profile, component.article); | |
43 | - done(); | |
44 | - }); | |
45 | - | |
46 | - it("got to the new article page and display an alert when saving sucessfully", done => { | |
47 | - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification); | |
48 | - component.save(); | |
49 | - $rootScope.$apply(); | |
50 | - expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); | |
51 | - expect(notification.success).toHaveBeenCalled(); | |
52 | - done(); | |
53 | - }); | |
54 | - | |
55 | -}); |
src/app/article/basic-editor.component.ts
... | ... | @@ -1,35 +0,0 @@ |
1 | -import {StateConfig, Component, Inject, provide} from 'ng-forward'; | |
2 | -import {ArticleService} from "../../lib/ng-noosfero-api/http/article.service"; | |
3 | -import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service"; | |
4 | -import {NotificationService} from "../shared/services/notification.service.ts"; | |
5 | - | |
6 | -@Component({ | |
7 | - selector: 'article-basic-editor', | |
8 | - templateUrl: "app/article/basic-editor.html", | |
9 | - providers: [ | |
10 | - provide('articleService', { useClass: ArticleService }), | |
11 | - provide('profileService', { useClass: ProfileService }), | |
12 | - provide('notification', { useClass: NotificationService }) | |
13 | - ] | |
14 | -}) | |
15 | -@Inject(ArticleService, ProfileService, "$state", NotificationService) | |
16 | -export class BasicEditorComponent { | |
17 | - | |
18 | - article: noosfero.Article = <noosfero.Article>{}; | |
19 | - | |
20 | - constructor(private articleService: ArticleService, | |
21 | - private profileService: ProfileService, | |
22 | - private $state: ng.ui.IStateService, | |
23 | - private notification: NotificationService) { } | |
24 | - | |
25 | - save() { | |
26 | - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
27 | - return this.articleService.createInProfile(profile, this.article); | |
28 | - }).then((response: noosfero.RestResult<noosfero.Article>) => { | |
29 | - let article = (<noosfero.Article>response.data); | |
30 | - this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier }); | |
31 | - this.notification.success({ title: "Good job!", message: "Article saved!" }); | |
32 | - }); | |
33 | - } | |
34 | - | |
35 | -} |
src/app/article/basic-editor.html
... | ... | @@ -1,11 +0,0 @@ |
1 | -<form> | |
2 | - <div class="form-group"> | |
3 | - <label for="titleInput">Title</label> | |
4 | - <input type="text" class="form-control" id="titleInput" placeholder="title" ng-model="vm.article.name"> | |
5 | - </div> | |
6 | - <div class="form-group"> | |
7 | - <label for="bodyInput">Text</label> | |
8 | - <textarea class="form-control" id="bodyInput" rows="10" ng-model="vm.article.body"></textarea> | |
9 | - </div> | |
10 | - <button type="submit" class="btn btn-default" ng-click="vm.save()">Save</button> | |
11 | -</form> |
src/app/article/cms/article-editor/article-editor.component.ts
0 → 100644
... | ... | @@ -0,0 +1,27 @@ |
1 | +import {Component, Input, Inject} from 'ng-forward'; | |
2 | + | |
3 | +@Component({ | |
4 | + selector: 'article-editor', | |
5 | + template: "not-used" | |
6 | +}) | |
7 | +@Inject("$element", "$scope", "$injector", "$compile") | |
8 | +export class ArticleEditorComponent { | |
9 | + | |
10 | + @Input() article: noosfero.Article; | |
11 | + | |
12 | + constructor( | |
13 | + private $element: any, | |
14 | + private $scope: ng.IScope, | |
15 | + private $injector: ng.auto.IInjectorService, | |
16 | + private $compile: ng.ICompileService) { } | |
17 | + | |
18 | + ngOnInit() { | |
19 | + let articleType = this.article.type.replace(/::/, ''); | |
20 | + let specificDirective = `${articleType.charAt(0).toLowerCase()}${articleType.substring(1)}Editor`; | |
21 | + let directiveName = "article-basic-editor"; | |
22 | + if (this.$injector.has(specificDirective + 'Directive')) { | |
23 | + directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | |
24 | + } | |
25 | + this.$element.replaceWith(this.$compile('<' + directiveName + ' [article]="ctrl.article"></' + directiveName + '>')(this.$scope)); | |
26 | + } | |
27 | +} | ... | ... |
src/app/article/cms/basic-editor/basic-editor.component.ts
0 → 100644
... | ... | @@ -0,0 +1,10 @@ |
1 | +<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/content-viewer-actions.component.spec.ts
1 | -import {providers} from 'ng-forward/cjs/testing/providers'; | |
2 | - | |
3 | 1 | import {Input, Component, provide} from 'ng-forward'; |
4 | 2 | |
5 | 3 | import * as helpers from "../../../spec/helpers"; |
6 | - | |
7 | -import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; | |
4 | +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; | |
8 | 5 | import {ContentViewerActionsComponent} from './content-viewer-actions.component'; |
9 | 6 | |
10 | 7 | // this htmlTemplate will be re-used between the container components in this spec file |
... | ... | @@ -12,56 +9,57 @@ const htmlTemplate: string = '<content-viewer-actions [article]="ctrl.article" [ |
12 | 9 | |
13 | 10 | describe('Content Viewer Actions Component', () => { |
14 | 11 | |
15 | - beforeEach(() => { | |
12 | + let helper: ComponentTestHelper<ContentViewerActionsComponent>; | |
16 | 13 | |
17 | - angular.mock.module("templates"); | |
14 | + beforeEach(angular.mock.module("templates")); | |
18 | 15 | |
19 | - providers((provide: any) => { | |
20 | - return <any>[ | |
21 | - provide('ProfileService', { | |
22 | - useValue: helpers.mocks.profileService | |
23 | - }) | |
24 | - ]; | |
25 | - }); | |
26 | - }); | |
16 | + let providers = [ | |
17 | + provide('ProfileService', { | |
18 | + useValue: helpers.mocks.profileService | |
19 | + }), | |
20 | + provide('ArticleService', { | |
21 | + useValue: helpers.mocks.articleService | |
22 | + }) | |
23 | + ].concat(helpers.provideFilters("translateFilter")); | |
27 | 24 | |
28 | - let buildComponent = (): Promise<ComponentFixture> => { | |
29 | - return helpers.quickCreateComponent({ | |
30 | - providers: [ | |
31 | - helpers.provideEmptyObjects('Restangular'), | |
32 | - helpers.provideFilters('translateFilter') | |
33 | - ], | |
25 | + beforeEach((done) => { | |
26 | + let cls = createClass({ | |
27 | + template: htmlTemplate, | |
34 | 28 | directives: [ContentViewerActionsComponent], |
35 | - template: htmlTemplate | |
29 | + providers: providers | |
36 | 30 | }); |
37 | - }; | |
31 | + helper = new ComponentTestHelper<ContentViewerActionsComponent>(cls, done); | |
32 | + }); | |
38 | 33 | |
39 | - it('renders content viewer actions directive', (done: Function) => { | |
40 | - buildComponent().then((fixture: ComponentFixture) => { | |
41 | - expect(fixture.debugElement.query('content-viewer-actions').length).toEqual(1); | |
34 | + it('renders content viewer actions directive', () => { | |
35 | + expect(helper.all("content-viewer-actions").length).toEqual(1); | |
36 | + }); | |
42 | 37 | |
43 | - done(); | |
44 | - }); | |
38 | + it('return article parent as container when it is not a folder', () => { | |
39 | + let article = <noosfero.Article>({ id: 1, type: 'TextArticle', parent: { id: 2 } }); | |
40 | + expect(helper.component.getArticleContainer(article)).toEqual(2); | |
41 | + }); | |
42 | + | |
43 | + it('return article as container when it is a folder', () => { | |
44 | + let article = <noosfero.Article>({ id: 1, type: 'Folder' }); | |
45 | + expect(helper.component.getArticleContainer(article)).toEqual(1); | |
45 | 46 | }); |
46 | 47 | |
47 | - it('check if profile was loaded', (done: Function) => { | |
48 | + it('return article as container when it is a blog', () => { | |
49 | + let article = <noosfero.Article>({ id: 1, type: 'Blog' }); | |
50 | + expect(helper.component.getArticleContainer(article)).toEqual(1); | |
51 | + }); | |
52 | + | |
53 | + it('check if profile was loaded', () => { | |
48 | 54 | let profile: any = { |
49 | 55 | id: 1, |
50 | 56 | identifier: 'the-profile-test', |
51 | 57 | type: 'Person' |
52 | 58 | }; |
53 | - | |
54 | 59 | helpers.mocks.profileService.getCurrentProfile = () => { |
55 | 60 | return helpers.mocks.promiseResultTemplate(profile); |
56 | 61 | }; |
57 | - | |
58 | - buildComponent().then((fixture: ComponentFixture) => { | |
59 | - let contentViewerComp: ContentViewerActionsComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
60 | - | |
61 | - expect(contentViewerComp.profile).toEqual(jasmine.objectContaining(profile)); | |
62 | - | |
63 | - done(); | |
64 | - }); | |
62 | + let component = new ContentViewerActionsComponent(<any>helpers.mocks.profileService, <any>helpers.mocks.articleService); | |
63 | + expect(component.profile).toEqual(jasmine.objectContaining(profile)); | |
65 | 64 | }); |
66 | - | |
67 | 65 | }); | ... | ... |
src/app/article/content-viewer/content-viewer-actions.component.ts
1 | 1 | import {Component, Inject, provide} from "ng-forward"; |
2 | 2 | import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; |
3 | +import {ArticleService} from "../../../lib/ng-noosfero-api/http/article.service"; | |
3 | 4 | |
4 | 5 | @Component({ |
5 | 6 | selector: "content-viewer-actions", |
6 | 7 | templateUrl: "app/article/content-viewer/navbar-actions.html", |
7 | - providers: [provide('profileService', { useClass: ProfileService })] | |
8 | + providers: [ | |
9 | + provide('profileService', { useClass: ProfileService }), | |
10 | + provide('articleService', { useClass: ArticleService }) | |
11 | + ] | |
8 | 12 | }) |
9 | -@Inject(ProfileService) | |
13 | +@Inject(ProfileService, ArticleService) | |
10 | 14 | export class ContentViewerActionsComponent { |
11 | 15 | |
12 | 16 | article: noosfero.Article; |
13 | 17 | profile: noosfero.Profile; |
18 | + parentId: number; | |
14 | 19 | |
15 | - constructor(profileService: ProfileService) { | |
20 | + constructor(profileService: ProfileService, articleService: ArticleService) { | |
16 | 21 | profileService.getCurrentProfile().then((profile: noosfero.Profile) => { |
17 | 22 | this.profile = profile; |
23 | + return articleService.getCurrent(); | |
24 | + }).then((article: noosfero.Article) => { | |
25 | + this.article = article; | |
26 | + this.parentId = this.getArticleContainer(article); | |
18 | 27 | }); |
19 | 28 | } |
29 | + | |
30 | + getArticleContainer(article: noosfero.Article) { | |
31 | + // FIXME get folder types from api | |
32 | + if (article.type === "Blog" || article.type === "Folder") { | |
33 | + return article.id; | |
34 | + } else if (article.parent) { | |
35 | + return article.parent.id; | |
36 | + } | |
37 | + } | |
20 | 38 | } | ... | ... |
src/app/article/content-viewer/content-viewer.component.ts
... | ... | @@ -28,11 +28,12 @@ export class ContentViewerComponent { |
28 | 28 | } |
29 | 29 | |
30 | 30 | activate() { |
31 | - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
31 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | |
32 | 32 | this.profile = profile; |
33 | 33 | return this.articleService.getArticleByProfileAndPath(this.profile, this.$stateParams["page"]); |
34 | 34 | }).then((result: noosfero.RestResult<any>) => { |
35 | 35 | this.article = <noosfero.Article>result.data; |
36 | + this.articleService.setCurrent(this.article); | |
36 | 37 | }); |
37 | 38 | } |
38 | 39 | } | ... | ... |
src/app/article/content-viewer/navbar-actions.html
1 | 1 | <ul class="nav navbar-nav"> |
2 | 2 | <li ng-show="vm.profile"> |
3 | - <a href="#" role="button" ui-sref="main.profile.cms({profile: vm.profile.identifier})"> | |
3 | + <a ng-show="vm.parentId" href="#" role="button" ui-sref="main.cms({profile: vm.profile.identifier, parent_id: vm.parentId})"> | |
4 | + <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} | |
5 | + </a> | |
6 | + </li> | |
7 | + <li ng-show="vm.profile"> | |
8 | + <a href="#" role="button" ui-sref="main.cms({profile: vm.profile.identifier, parent_id: vm.parentId, type: 'CommentParagraphPlugin::Discussion'})"> | |
4 | 9 | <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} |
5 | 10 | </a> |
6 | 11 | </li> | ... | ... |
src/app/article/index.ts
... | ... | @@ -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"; |
... | ... | @@ -14,16 +14,17 @@ declare var moment: any; |
14 | 14 | |
15 | 15 | let noosferoApp: any = bundle("noosferoApp", MainComponent, ["ngAnimate", "ngCookies", "ngStorage", "ngTouch", |
16 | 16 | "ngSanitize", "ngMessages", "ngAria", "restangular", |
17 | - "ui.router", "ui.bootstrap", "toastr", | |
18 | - "angularMoment", "angular.filter", "akoenig.deckgrid", | |
17 | + "ui.router", "ui.bootstrap", "toastr", "ngCkeditor", | |
18 | + "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid", | |
19 | 19 | "angular-timeline", "duScroll", "oitozero.ngSweetAlert", |
20 | - "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad"]).publish(); | |
20 | + "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad", | |
21 | + "angular-click-outside"]).publish(); | |
21 | 22 | |
22 | 23 | NoosferoApp.angularModule = noosferoApp; |
23 | 24 | |
24 | 25 | |
25 | 26 | NoosferoApp.addConstants("moment", moment); |
26 | -NoosferoApp.addConstants("AUTH_EVENTS", AUTH_EVENTS); | |
27 | +NoosferoApp.addConstants("AuthEvents", AuthEvents); | |
27 | 28 | |
28 | 29 | NoosferoApp.addConfig(noosferoModuleConfig); |
29 | 30 | NoosferoApp.run(noosferoAngularRunBlock); | ... | ... |
src/app/layout/blocks/block.component.ts
... | ... | @@ -11,7 +11,7 @@ export class BlockComponent { |
11 | 11 | @Input() owner: any; |
12 | 12 | |
13 | 13 | ngOnInit() { |
14 | - let blockName = (this.block && this.block.type) ? this.block.type.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
14 | + let blockName = (this.block && this.block.type) ? this.block.type.replace(/::/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : "default-block"; | |
15 | 15 | this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope)); |
16 | 16 | } |
17 | 17 | ... | ... |
src/app/layout/blocks/communities-block/communities-block.component.spec.ts
... | ... | @@ -1,51 +0,0 @@ |
1 | -import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder'; | |
2 | -import {Provider, Input, provide, Component} from 'ng-forward'; | |
3 | - | |
4 | -import {CommunitiesBlockComponent} from './communities-block.component'; | |
5 | - | |
6 | -const htmlTemplate: string = '<noosfero-communities-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-communities-block>'; | |
7 | - | |
8 | -const tcb = new TestComponentBuilder(); | |
9 | - | |
10 | -describe("Components", () => { | |
11 | - describe("Communities Block Component", () => { | |
12 | - | |
13 | - beforeEach(angular.mock.module("templates")); | |
14 | - | |
15 | - let state = jasmine.createSpyObj("state", ["go"]); | |
16 | - let providers = [ | |
17 | - new Provider('truncateFilter', { useValue: () => { } }), | |
18 | - new Provider('stripTagsFilter', { useValue: () => { } }), | |
19 | - new Provider('$state', { useValue: state }), | |
20 | - new Provider('CommunityService', { | |
21 | - useValue: { | |
22 | - getByOwner: (owner: any, params: any): any => { | |
23 | - return Promise.resolve({ data: [{ identifier: "community1" }] }); | |
24 | - } | |
25 | - } | |
26 | - }), | |
27 | - ]; | |
28 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [CommunitiesBlockComponent], providers: providers }) | |
29 | - class BlockContainerComponent { | |
30 | - block = { type: 'Block', settings: {} }; | |
31 | - owner = { name: 'profile-name' }; | |
32 | - } | |
33 | - | |
34 | - it("get communities", done => { | |
35 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
36 | - let block: CommunitiesBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | |
37 | - expect(block.profiles).toEqual([{ identifier: "community1" }]); | |
38 | - done(); | |
39 | - }); | |
40 | - }); | |
41 | - | |
42 | - it("render the profile image for each community", done => { | |
43 | - tcb.createAsync(BlockContainerComponent).then(fixture => { | |
44 | - fixture.debugElement.getLocal("$rootScope").$apply(); | |
45 | - expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1); | |
46 | - done(); | |
47 | - }); | |
48 | - }); | |
49 | - | |
50 | - }); | |
51 | -}); |
src/app/layout/blocks/communities-block/communities-block.component.ts
... | ... | @@ -1,24 +0,0 @@ |
1 | -import {Input, Inject, Component} from "ng-forward"; | |
2 | -import {CommunityService} from "../../../../lib/ng-noosfero-api/http/community.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-communities-block", | |
6 | - templateUrl: 'app/layout/blocks/communities-block/communities-block.html', | |
7 | -}) | |
8 | -@Inject(CommunityService) | |
9 | -export class CommunitiesBlockComponent { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Profile; | |
13 | - | |
14 | - profiles: any = []; | |
15 | - | |
16 | - constructor(private communityService: CommunityService) { } | |
17 | - | |
18 | - ngOnInit() { | |
19 | - let limit: number = ((this.block && this.block.settings) ? this.block.settings.limit : null) || 5; | |
20 | - this.communityService.getByOwner(this.owner, { limit: limit }).then((result: noosfero.RestResult<noosfero.Community[]>) => { | |
21 | - this.profiles = result.data; | |
22 | - }); | |
23 | - } | |
24 | -} |
src/app/layout/blocks/communities-block/communities-block.html
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; | |
14 | - | |
15 | - let providers = [ | |
16 | - new Provider('ProfileService', { | |
17 | - useValue: { | |
18 | - getProfileMembers: (profileId: number, filters: any): any => { | |
19 | - return Promise.resolve({ data: { people: [{ identifier: "person1" }] } }); | |
20 | - } | |
21 | - } | |
22 | - }), | |
23 | - ]; | |
24 | - | |
25 | - beforeEach(angular.mock.module("templates")); | |
26 | - | |
27 | - beforeEach( (done) => { | |
28 | - // Custom properties for the component | |
29 | - let properties = { owner: { id: 1 } }; | |
30 | - // Create the component bed for the test. | |
31 | - let cls = createClass({ | |
32 | - template: htmlTemplate, | |
33 | - directives: [MembersBlockComponent], | |
34 | - providers: providers, | |
35 | - properties: properties | |
36 | - }); | |
37 | - helper = new ComponentTestHelper(cls, done); | |
38 | - }); | |
39 | - | |
40 | - it("get members of the block owner", () => { | |
41 | - expect(helper.component.members[0].identifier).toEqual("person1"); | |
42 | - }); | |
43 | - | |
44 | - it("render the profile image for each member", () => { | |
45 | - expect(helper.all("noosfero-profile-image").length).toEqual(1); | |
46 | - }); | |
47 | - | |
48 | - }); | |
49 | -}); | |
50 | 0 | \ No newline at end of file |
src/app/layout/blocks/members-block/members-block.component.ts
... | ... | @@ -1,25 +0,0 @@ |
1 | -import {Input, Inject, Component} from "ng-forward"; | |
2 | -import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-members-block", | |
6 | - templateUrl: 'app/layout/blocks/members-block/members-block.html', | |
7 | -}) | |
8 | -@Inject(ProfileService) | |
9 | -export class MembersBlockComponent { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Profile; | |
13 | - | |
14 | - members: any = []; | |
15 | - | |
16 | - constructor(private profileService: ProfileService) { | |
17 | - | |
18 | - } | |
19 | - | |
20 | - ngOnInit() { | |
21 | - this.profileService.getProfileMembers(this.owner.id, { per_page: 6 }).then((response: any) => { | |
22 | - this.members = response.data.people; | |
23 | - }); | |
24 | - } | |
25 | -} |
src/app/layout/blocks/members-block/members-block.html
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; | |
20 | - | |
21 | - beforeEach( angular.mock.module("templates") ); | |
22 | - | |
23 | - /** | |
24 | - * The beforeEach procedure will initialize the helper and parse | |
25 | - * the component according to the given providers. Unfortunetly, in | |
26 | - * this mode, the providers and properties given to the construtor | |
27 | - * can't be overriden. | |
28 | - */ | |
29 | - beforeEach( (done) => { | |
30 | - // Create the component bed for the test. Optionally, this could be done | |
31 | - // in each test if one needs customization of these parameters per test | |
32 | - let cls = createClass({ | |
33 | - template: htmlTemplate, | |
34 | - directives: [PeopleBlockComponent], | |
35 | - providers: providers, | |
36 | - properties: {} | |
37 | - }); | |
38 | - helper = new ComponentTestHelper(cls, done); | |
39 | - }); | |
40 | - | |
41 | - /** | |
42 | - * By default the helper will have the component, with all properties | |
43 | - * ready to be used. Here the mock provider 'EnvironmentService' will | |
44 | - * return the given array with one person. | |
45 | - */ | |
46 | - it("get block with one people", () => { | |
47 | - expect(helper.component.people[0].identifier).toEqual("person1"); | |
48 | - }); | |
49 | - | |
50 | - /** | |
51 | - * There are helper functions to access the JQuery DOM like this. | |
52 | - */ | |
53 | - it("render the profile image for each person", () => { | |
54 | - expect(helper.all("noosfero-profile-image").length).toEqual(1); | |
55 | - }); | |
56 | - | |
57 | - /** | |
58 | - * The main debugElement element is also available | |
59 | - */ | |
60 | - it("render the main noosfero people block", () => { | |
61 | - expect(helper.debugElement.children().length).toEqual(1, "The people-block should have a div children"); | |
62 | - }); | |
63 | - | |
64 | - /** | |
65 | - * Just another example of a JQuery DOM helper function | |
66 | - */ | |
67 | - it("render the noosfero people block div", () => { | |
68 | - let div = helper.findChildren("noosfero-people-block", "div"); | |
69 | - expect(div.className).toBe('people-block', "The class should be people-block"); | |
70 | - }); | |
71 | - }); | |
72 | -}); | |
73 | 0 | \ No newline at end of file |
src/app/layout/blocks/people-block/people-block.component.ts
... | ... | @@ -1,26 +0,0 @@ |
1 | -import {Input, Inject, Component} from "ng-forward"; | |
2 | -import {EnvironmentService} from "../../../../lib/ng-noosfero-api/http/environment.service"; | |
3 | - | |
4 | -@Component({ | |
5 | - selector: "noosfero-people-block", | |
6 | - templateUrl: 'app/layout/blocks/people-block/people-block.html', | |
7 | -}) | |
8 | -@Inject(EnvironmentService) | |
9 | -export class PeopleBlockComponent { | |
10 | - | |
11 | - @Input() block: noosfero.Block; | |
12 | - @Input() owner: noosfero.Environment; | |
13 | - private type: string = "people"; | |
14 | - | |
15 | - people: noosfero.Person[] = []; | |
16 | - | |
17 | - constructor(private environmentService: EnvironmentService) { | |
18 | - } | |
19 | - | |
20 | - ngOnInit() { | |
21 | - this.environmentService.getEnvironmentPeople({ limit: '6' }).then((people: noosfero.Person[]) => { | |
22 | - this.people = people; | |
23 | - }); | |
24 | - } | |
25 | - | |
26 | -} |
src/app/layout/blocks/people-block/people-block.html
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 | -}); |