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,10 +34,14 @@ | ||
34 | "angular-dynamic-locale": "^0.1.30", | 34 | "angular-dynamic-locale": "^0.1.30", |
35 | "angular-i18n": "^1.5.0", | 35 | "angular-i18n": "^1.5.0", |
36 | "angular-load": "^0.4.1", | 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 | "devDependencies": { | 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 | "overrides": { | 46 | "overrides": { |
43 | "bootstrap-sass": { | 47 | "bootstrap-sass": { |
gulp/build.js
@@ -6,6 +6,7 @@ var rename = require('gulp-rename'); | @@ -6,6 +6,7 @@ var rename = require('gulp-rename'); | ||
6 | var insert = require('gulp-insert'); | 6 | var insert = require('gulp-insert'); |
7 | var merge = require('merge-stream'); | 7 | var merge = require('merge-stream'); |
8 | var conf = require('./conf'); | 8 | var conf = require('./conf'); |
9 | +var languages = require('./languages'); | ||
9 | 10 | ||
10 | var themeName = conf.paths.theme.replace('-', ' '); | 11 | var themeName = conf.paths.theme.replace('-', ' '); |
11 | themeName = themeName.charAt(0).toUpperCase() + themeName.slice(1); | 12 | themeName = themeName.charAt(0).toUpperCase() + themeName.slice(1); |
@@ -16,25 +17,31 @@ var $ = require('gulp-load-plugins')({ | @@ -16,25 +17,31 @@ var $ = require('gulp-load-plugins')({ | ||
16 | }); | 17 | }); |
17 | 18 | ||
18 | gulp.task('partials', function () { | 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 | gulp.task('html', ['inject', 'partials'], function () { | 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 | var partialsInjectOptions = { | 45 | var partialsInjectOptions = { |
39 | starttag: '<!-- inject:partials -->', | 46 | starttag: '<!-- inject:partials -->', |
40 | ignorePath: path.join(conf.paths.tmp, '/partials'), | 47 | ignorePath: path.join(conf.paths.tmp, '/partials'), |
@@ -73,6 +80,8 @@ gulp.task('html', ['inject', 'partials'], function () { | @@ -73,6 +80,8 @@ gulp.task('html', ['inject', 'partials'], function () { | ||
73 | .pipe($.useref()) | 80 | .pipe($.useref()) |
74 | .pipe($.revReplace({prefix: noosferoThemePrefix})) | 81 | .pipe($.revReplace({prefix: noosferoThemePrefix})) |
75 | .pipe(htmlFilter) | 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 | .pipe($.minifyHtml({ | 85 | .pipe($.minifyHtml({ |
77 | empty: true, | 86 | empty: true, |
78 | spare: true, | 87 | spare: true, |
@@ -93,6 +102,10 @@ gulp.task('fonts', function () { | @@ -93,6 +102,10 @@ gulp.task('fonts', function () { | ||
93 | .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/'))); | 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 | gulp.task('locale', function () { | 109 | gulp.task('locale', function () { |
97 | return gulp.src([ | 110 | return gulp.src([ |
98 | path.join("bower_components/angular-i18n", '*.js'), | 111 | path.join("bower_components/angular-i18n", '*.js'), |
@@ -124,6 +137,10 @@ gulp.task('clean-docs', [], function() { | @@ -124,6 +137,10 @@ gulp.task('clean-docs', [], function() { | ||
124 | return $.del([path.join(conf.paths.docs, '/')]); | 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 | gulp.task('noosfero', ['html'], function () { | 144 | gulp.task('noosfero', ['html'], function () { |
128 | var layouts = gulp.src('layouts/**/*') | 145 | var layouts = gulp.src('layouts/**/*') |
129 | .pipe(gulp.dest(path.join(conf.paths.dist, "layouts"))); | 146 | .pipe(gulp.dest(path.join(conf.paths.dist, "layouts"))); |
@@ -136,4 +153,4 @@ gulp.task('noosfero', ['html'], function () { | @@ -136,4 +153,4 @@ gulp.task('noosfero', ['html'], function () { | ||
136 | return merge(layouts, theme, index); | 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,11 +15,13 @@ var path = require('path'); | ||
15 | */ | 15 | */ |
16 | exports.paths = { | 16 | exports.paths = { |
17 | src: 'src', | 17 | src: 'src', |
18 | + plugins: 'plugins', | ||
18 | dist: 'dist', | 19 | dist: 'dist', |
19 | tmp: '.tmp', | 20 | tmp: '.tmp', |
20 | e2e: 'e2e', | 21 | e2e: 'e2e', |
21 | docs: 'docs', | 22 | docs: 'docs', |
22 | - themes: 'themes' | 23 | + themes: 'themes', |
24 | + languages: 'languages' | ||
23 | }; | 25 | }; |
24 | exports.configTheme = function(theme) { | 26 | exports.configTheme = function(theme) { |
25 | exports.paths.theme = theme || "angular-default"; | 27 | exports.paths.theme = theme || "angular-default"; |
@@ -34,7 +36,7 @@ exports.configTheme(argv.theme); | @@ -34,7 +36,7 @@ exports.configTheme(argv.theme); | ||
34 | * to inject css preprocessor deps and js files in karma | 36 | * to inject css preprocessor deps and js files in karma |
35 | */ | 37 | */ |
36 | exports.wiredep = { | 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 | directory: 'bower_components' | 40 | directory: 'bower_components' |
39 | }; | 41 | }; |
40 | 42 |
@@ -0,0 +1,29 @@ | @@ -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,7 +45,7 @@ browserSync.use(browserSyncSpa({ | ||
45 | selector: '[ng-app]'// Only needed for angular apps | 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 | var srcPaths = [path.join(conf.paths.tmp, '/serve')]; | 49 | var srcPaths = [path.join(conf.paths.tmp, '/serve')]; |
50 | conf.paths.allSources.reverse().forEach(function(src) { | 50 | conf.paths.allSources.reverse().forEach(function(src) { |
51 | srcPaths.push(src); | 51 | srcPaths.push(src); |
gulp/styles.js
@@ -31,6 +31,7 @@ var buildStyles = function() { | @@ -31,6 +31,7 @@ var buildStyles = function() { | ||
31 | ]; | 31 | ]; |
32 | conf.paths.allSources.forEach(function(src) { | 32 | conf.paths.allSources.forEach(function(src) { |
33 | srcPaths.push(path.join(src, '/app/**/*.scss')); | 33 | srcPaths.push(path.join(src, '/app/**/*.scss')); |
34 | + srcPaths.push(path.join(src, conf.paths.plugins, '/**/*.scss')); | ||
34 | }); | 35 | }); |
35 | var injectFiles = gulp.src(srcPaths, { read: false }); | 36 | var injectFiles = gulp.src(srcPaths, { read: false }); |
36 | 37 |
gulp/watch.js
@@ -3,6 +3,7 @@ | @@ -3,6 +3,7 @@ | ||
3 | var path = require('path'); | 3 | var path = require('path'); |
4 | var gulp = require('gulp'); | 4 | var gulp = require('gulp'); |
5 | var conf = require('./conf'); | 5 | var conf = require('./conf'); |
6 | +var languages = require('./languages'); | ||
6 | 7 | ||
7 | var browserSync = require('browser-sync'); | 8 | var browserSync = require('browser-sync'); |
8 | 9 | ||
@@ -16,7 +17,8 @@ gulp.task('watch', ['inject'], function () { | @@ -16,7 +17,8 @@ gulp.task('watch', ['inject'], function () { | ||
16 | 17 | ||
17 | gulp.watch([ | 18 | gulp.watch([ |
18 | path.join(conf.paths.src, '/app/**/*.css'), | 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 | ], function(event) { | 22 | ], function(event) { |
21 | if(isOnlyChange(event)) { | 23 | if(isOnlyChange(event)) { |
22 | gulp.start('styles-reload'); | 24 | gulp.start('styles-reload'); |
@@ -33,9 +35,14 @@ gulp.task('watch', ['inject'], function () { | @@ -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 | var watchPaths = []; | 42 | var watchPaths = []; |
37 | conf.paths.allSources.forEach(function(src) { | 43 | conf.paths.allSources.forEach(function(src) { |
38 | watchPaths.push(path.join(src, '/app/**/*.html')); | 44 | watchPaths.push(path.join(src, '/app/**/*.html')); |
45 | + watchPaths.push(path.join(src, conf.paths.plugins, '/**/*.html')); | ||
39 | }); | 46 | }); |
40 | gulp.watch(watchPaths, function(event) { | 47 | gulp.watch(watchPaths, function(event) { |
41 | browserSync.reload(event.path); | 48 | browserSync.reload(event.path); |
karma.conf.js
@@ -49,7 +49,7 @@ var _ = require('lodash'); | @@ -49,7 +49,7 @@ var _ = require('lodash'); | ||
49 | var wiredep = require('wiredep'); | 49 | var wiredep = require('wiredep'); |
50 | 50 | ||
51 | var pathSrcHtml = [ | 51 | var pathSrcHtml = [ |
52 | - path.join('./src/app/**/*.html') | 52 | + path.join('./src/**/*.html') |
53 | ]; | 53 | ]; |
54 | 54 | ||
55 | var glob = require("glob"); | 55 | var glob = require("glob"); |
layouts/angular-layout.html.erb
1 | -<% if params[:angular_theme_old] || request.fullpath.start_with?('/myprofile') %> | 1 | +<% if params[:angular_theme_old] %> |
2 | <%= render file: Rails.root.join("app/views/layouts/application-ng.html.erb"), use_full_path: false %> | 2 | <%= render file: Rails.root.join("app/views/layouts/application-ng.html.erb"), use_full_path: false %> |
3 | <% else %> | 3 | <% else %> |
4 | <%= from_theme_include(current_theme, "index") %> | 4 | <%= from_theme_include(current_theme, "index") %> |
package.json
@@ -49,6 +49,7 @@ | @@ -49,6 +49,7 @@ | ||
49 | "gulp-insert": "^0.5.0", | 49 | "gulp-insert": "^0.5.0", |
50 | "gulp-inject": "~3.0.0", | 50 | "gulp-inject": "~3.0.0", |
51 | "gulp-load-plugins": "~0.10.0", | 51 | "gulp-load-plugins": "~0.10.0", |
52 | + "gulp-merge-json": "^0.4.0", | ||
52 | "gulp-minify-css": "~1.2.1", | 53 | "gulp-minify-css": "~1.2.1", |
53 | "gulp-minify-html": "~1.0.4", | 54 | "gulp-minify-html": "~1.0.4", |
54 | "gulp-ng-annotate": "~1.1.0", | 55 | "gulp-ng-annotate": "~1.1.0", |
src/app/article/article-default-view.component.ts
1 | import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; | 1 | import { bundle, Input, Inject, Component, Directive } from 'ng-forward'; |
2 | import {ArticleBlogComponent} from "./types/blog/blog.component"; | 2 | import {ArticleBlogComponent} from "./types/blog/blog.component"; |
3 | import {CommentsComponent} from "./comment/comments.component"; | 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 | * @ngdoc controller | 9 | * @ngdoc controller |
@@ -30,7 +33,9 @@ export class ArticleDefaultViewComponent { | @@ -30,7 +33,9 @@ export class ArticleDefaultViewComponent { | ||
30 | @Component({ | 33 | @Component({ |
31 | selector: 'noosfero-article', | 34 | selector: 'noosfero-article', |
32 | template: 'not-used', | 35 | template: 'not-used', |
33 | - directives: [ArticleDefaultViewComponent, ArticleBlogComponent, CommentsComponent] | 36 | + directives: [ArticleDefaultViewComponent, ArticleBlogComponent, |
37 | + CommentsComponent, MacroDirective, ArticleToolbarHotspotComponent, | ||
38 | + ArticleContentHotspotComponent] | ||
34 | }) | 39 | }) |
35 | @Inject("$element", "$scope", "$injector", "$compile") | 40 | @Inject("$element", "$scope", "$injector", "$compile") |
36 | export class ArticleViewComponent { | 41 | export class ArticleViewComponent { |
@@ -40,7 +45,8 @@ export class ArticleViewComponent { | @@ -40,7 +45,8 @@ export class ArticleViewComponent { | ||
40 | directiveName: string; | 45 | directiveName: string; |
41 | 46 | ||
42 | ngOnInit() { | 47 | ngOnInit() { |
43 | - let specificDirective = 'noosfero' + this.article.type; | 48 | + let articleType = this.article.type.replace(/::/, ''); |
49 | + let specificDirective = 'noosfero' + articleType; | ||
44 | this.directiveName = "noosfero-default-article"; | 50 | this.directiveName = "noosfero-default-article"; |
45 | if (this.$injector.has(specificDirective + 'Directive')) { | 51 | if (this.$injector.has(specificDirective + 'Directive')) { |
46 | this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | 52 | this.directiveName = specificDirective.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); |
@@ -53,6 +59,5 @@ export class ArticleViewComponent { | @@ -53,6 +59,5 @@ export class ArticleViewComponent { | ||
53 | private $scope: ng.IScope, | 59 | private $scope: ng.IScope, |
54 | private $injector: ng.auto.IInjectorService, | 60 | private $injector: ng.auto.IInjectorService, |
55 | private $compile: ng.ICompileService) { | 61 | private $compile: ng.ICompileService) { |
56 | - | ||
57 | } | 62 | } |
58 | } | 63 | } |
src/app/article/article.html
@@ -4,6 +4,10 @@ | @@ -4,6 +4,10 @@ | ||
4 | </div> | 4 | </div> |
5 | 5 | ||
6 | <div class="sub-header clearfix"> | 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 | <div class="page-info pull-right small text-muted"> | 11 | <div class="page-info pull-right small text-muted"> |
8 | <span class="time"> | 12 | <span class="time"> |
9 | <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> | 13 | <i class="fa fa-clock-o"></i> <span am-time-ago="ctrl.article.created_at | dateFormat"></span> |
@@ -16,9 +20,9 @@ | @@ -16,9 +20,9 @@ | ||
16 | </span> | 20 | </span> |
17 | </div> | 21 | </div> |
18 | </div> | 22 | </div> |
19 | - | 23 | + <noosfero-hotspot-article-content [article]="ctrl.article"></noosfero-hotspot-article-content> |
20 | <div class="page-body"> | 24 | <div class="page-body"> |
21 | - <div ng-bind-html="ctrl.article.body"></div> | 25 | + <div bind-html-compile="ctrl.article.body"></div> |
22 | </div> | 26 | </div> |
23 | 27 | ||
24 | <noosfero-comments [article]="ctrl.article"></noosfero-comments> | 28 | <noosfero-comments [article]="ctrl.article"></noosfero-comments> |
src/app/article/basic-editor.component.spec.ts
@@ -1,55 +0,0 @@ | @@ -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,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,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 @@ | @@ -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 @@ | @@ -0,0 +1,10 @@ | ||
1 | +import {Component, Input} from 'ng-forward'; | ||
2 | + | ||
3 | +@Component({ | ||
4 | + selector: 'article-basic-editor', | ||
5 | + templateUrl: "app/article/cms/basic-editor/basic-editor.html" | ||
6 | +}) | ||
7 | +export class BasicEditorComponent { | ||
8 | + | ||
9 | + @Input() article: noosfero.Article; | ||
10 | +} |
@@ -0,0 +1,10 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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,6 +9,8 @@ export class CommentComponent { | ||
9 | 9 | ||
10 | @Input() comment: noosfero.CommentViewModel; | 10 | @Input() comment: noosfero.CommentViewModel; |
11 | @Input() article: noosfero.Article; | 11 | @Input() article: noosfero.Article; |
12 | + @Input() displayActions = true; | ||
13 | + @Input() displayReplies = true; | ||
12 | 14 | ||
13 | showReply() { | 15 | showReply() { |
14 | return this.comment && this.comment.__show_reply === true; | 16 | return this.comment && this.comment.__show_reply === true; |
src/app/article/comment/comment.html
@@ -9,13 +9,19 @@ | @@ -9,13 +9,19 @@ | ||
9 | <a class="pull-left" ui-sref="main.profile.home({profile: ctrl.comment.author.identifier})"> | 9 | <a class="pull-left" ui-sref="main.profile.home({profile: ctrl.comment.author.identifier})"> |
10 | <h4 class="media-heading">{{ctrl.comment.author.name}}</h4> | 10 | <h4 class="media-heading">{{ctrl.comment.author.name}}</h4> |
11 | </a> | 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 | <span class="date" am-time-ago="ctrl.comment.created_at | dateFormat"></span> | 16 | <span class="date" am-time-ago="ctrl.comment.created_at | dateFormat"></span> |
13 | </div> | 17 | </div> |
14 | <div class="title">{{ctrl.comment.title}}</div> | 18 | <div class="title">{{ctrl.comment.title}}</div> |
15 | <div class="body">{{ctrl.comment.body}}</div> | 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 | </div> | 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 | </div> | 27 | </div> |
src/app/article/comment/comment.scss
@@ -5,6 +5,7 @@ | @@ -5,6 +5,7 @@ | ||
5 | @extend .text-muted; | 5 | @extend .text-muted; |
6 | @extend .small; | 6 | @extend .small; |
7 | margin-left: 8px; | 7 | margin-left: 8px; |
8 | + font-size: 12px; | ||
8 | } | 9 | } |
9 | .title { | 10 | .title { |
10 | font-weight: bold; | 11 | font-weight: bold; |
@@ -13,7 +14,18 @@ | @@ -13,7 +14,18 @@ | ||
13 | min-width: 40px; | 14 | min-width: 40px; |
14 | } | 15 | } |
15 | .media-body { | 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 | noosfero-profile-image { | 30 | noosfero-profile-image { |
19 | img { | 31 | img { |
@@ -27,8 +39,24 @@ | @@ -27,8 +39,24 @@ | ||
27 | font-size: 1.7em; | 39 | font-size: 1.7em; |
28 | } | 40 | } |
29 | } | 41 | } |
42 | + // Limit identation of replies | ||
30 | .comments { | 43 | .comments { |
31 | margin-left: 30px; | 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 | import { PostCommentComponent } from "./post-comment/post-comment.component"; | 2 | import { PostCommentComponent } from "./post-comment/post-comment.component"; |
6 | import { CommentService } from "../../../lib/ng-noosfero-api/http/comment.service"; | 3 | import { CommentService } from "../../../lib/ng-noosfero-api/http/comment.service"; |
7 | import { CommentComponent } from "./comment.component"; | 4 | import { CommentComponent } from "./comment.component"; |
@@ -19,12 +16,13 @@ export class CommentsComponent { | @@ -19,12 +16,13 @@ export class CommentsComponent { | ||
19 | @Input() showForm = true; | 16 | @Input() showForm = true; |
20 | @Input() article: noosfero.Article; | 17 | @Input() article: noosfero.Article; |
21 | @Input() parent: noosfero.CommentViewModel; | 18 | @Input() parent: noosfero.CommentViewModel; |
22 | - | ||
23 | protected page = 1; | 19 | protected page = 1; |
24 | protected perPage = 5; | 20 | protected perPage = 5; |
25 | protected total = 0; | 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 | ngOnInit() { | 27 | ngOnInit() { |
30 | if (this.parent) { | 28 | if (this.parent) { |
@@ -43,7 +41,7 @@ export class CommentsComponent { | @@ -43,7 +41,7 @@ export class CommentsComponent { | ||
43 | this.comments.forEach((comment: noosfero.CommentViewModel) => { | 41 | this.comments.forEach((comment: noosfero.CommentViewModel) => { |
44 | comment.__show_reply = false; | 42 | comment.__show_reply = false; |
45 | }); | 43 | }); |
46 | - if (this.parent) { | 44 | + if (this.parent) { |
47 | this.parent.__show_reply = false; | 45 | this.parent.__show_reply = false; |
48 | } | 46 | } |
49 | } | 47 | } |
src/app/article/comment/comments.html
1 | <div class="comments"> | 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 | <div class="comments-list"> | 4 | <div class="comments-list"> |
5 | <noosfero-comment ng-repeat="comment in ctrl.comments | orderBy: 'created_at':true" [comment]="comment" [article]="ctrl.article"></noosfero-comment> | 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,6 +7,8 @@ const htmlTemplate: string = '<noosfero-post-comment [article]="ctrl.article" [r | ||
7 | describe("Components", () => { | 7 | describe("Components", () => { |
8 | describe("Post Comment Component", () => { | 8 | describe("Post Comment Component", () => { |
9 | 9 | ||
10 | + let properties = { article: { id: 1, accept_comments: true } }; | ||
11 | + | ||
10 | beforeEach(angular.mock.module("templates")); | 12 | beforeEach(angular.mock.module("templates")); |
11 | 13 | ||
12 | let commentService = jasmine.createSpyObj("commentService", ["createInArticle"]); | 14 | let commentService = jasmine.createSpyObj("commentService", ["createInArticle"]); |
@@ -19,7 +21,7 @@ describe("Components", () => { | @@ -19,7 +21,7 @@ describe("Components", () => { | ||
19 | 21 | ||
20 | @Component({ selector: 'test-container-component', directives: [PostCommentComponent], template: htmlTemplate, providers: providers }) | 22 | @Component({ selector: 'test-container-component', directives: [PostCommentComponent], template: htmlTemplate, providers: providers }) |
21 | class ContainerComponent { | 23 | class ContainerComponent { |
22 | - article = { id: 1 }; | 24 | + article = properties['article']; |
23 | comment = { id: 2 }; | 25 | comment = { id: 2 }; |
24 | } | 26 | } |
25 | 27 | ||
@@ -30,6 +32,14 @@ describe("Components", () => { | @@ -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 | it("emit an event when create comment", done => { | 43 | it("emit an event when create comment", done => { |
34 | helpers.createComponentFromClass(ContainerComponent).then(fixture => { | 44 | helpers.createComponentFromClass(ContainerComponent).then(fixture => { |
35 | let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | 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,20 +2,23 @@ import { Inject, Input, Output, EventEmitter, Component } from 'ng-forward'; | ||
2 | import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service"; | 2 | import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service"; |
3 | import { NotificationService } from "../../../shared/services/notification.service"; | 3 | import { NotificationService } from "../../../shared/services/notification.service"; |
4 | import { SessionService } from "../../../login"; | 4 | import { SessionService } from "../../../login"; |
5 | +import { CommentFormHotspotComponent } from "../../../hotspot/comment-form-hotspot.component"; | ||
5 | 6 | ||
6 | @Component({ | 7 | @Component({ |
7 | selector: 'noosfero-post-comment', | 8 | selector: 'noosfero-post-comment', |
8 | templateUrl: 'app/article/comment/post-comment/post-comment.html', | 9 | templateUrl: 'app/article/comment/post-comment/post-comment.html', |
9 | - outputs: ['commentSaved'] | 10 | + outputs: ['commentSaved'], |
11 | + directives: [CommentFormHotspotComponent] | ||
10 | }) | 12 | }) |
11 | @Inject(CommentService, NotificationService, SessionService) | 13 | @Inject(CommentService, NotificationService, SessionService) |
12 | export class PostCommentComponent { | 14 | export class PostCommentComponent { |
13 | 15 | ||
16 | + public static EVENT_COMMENT_RECEIVED = "comment.received"; | ||
17 | + | ||
14 | @Input() article: noosfero.Article; | 18 | @Input() article: noosfero.Article; |
15 | @Input() parent: noosfero.Comment; | 19 | @Input() parent: noosfero.Comment; |
16 | @Output() commentSaved: EventEmitter<Comment> = new EventEmitter<Comment>(); | 20 | @Output() commentSaved: EventEmitter<Comment> = new EventEmitter<Comment>(); |
17 | - | ||
18 | - comment = <noosfero.Comment>{}; | 21 | + @Input() comment = <noosfero.Comment>{}; |
19 | private currentUser: noosfero.User; | 22 | private currentUser: noosfero.User; |
20 | 23 | ||
21 | constructor(private commentService: CommentService, | 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 | <div class="form-group"> | 2 | <div class="form-group"> |
3 | <div class="comment media"> | 3 | <div class="comment media"> |
4 | <div class="media-left"> | 4 | <div class="media-left"> |
@@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
8 | </div> | 8 | </div> |
9 | <div class="media-body"> | 9 | <div class="media-body"> |
10 | <textarea class="form-control custom-control" rows="1" ng-model="ctrl.comment.body" placeholder="{{'comment.post.placeholder' | translate}}"></textarea> | 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 | <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 | <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 | </div> | 13 | </div> |
13 | </div> | 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 | import {Input, Component, provide} from 'ng-forward'; | 1 | import {Input, Component, provide} from 'ng-forward'; |
4 | 2 | ||
5 | import * as helpers from "../../../spec/helpers"; | 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 | import {ContentViewerActionsComponent} from './content-viewer-actions.component'; | 5 | import {ContentViewerActionsComponent} from './content-viewer-actions.component'; |
9 | 6 | ||
10 | // this htmlTemplate will be re-used between the container components in this spec file | 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,56 +9,57 @@ const htmlTemplate: string = '<content-viewer-actions [article]="ctrl.article" [ | ||
12 | 9 | ||
13 | describe('Content Viewer Actions Component', () => { | 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 | directives: [ContentViewerActionsComponent], | 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 | let profile: any = { | 54 | let profile: any = { |
49 | id: 1, | 55 | id: 1, |
50 | identifier: 'the-profile-test', | 56 | identifier: 'the-profile-test', |
51 | type: 'Person' | 57 | type: 'Person' |
52 | }; | 58 | }; |
53 | - | ||
54 | helpers.mocks.profileService.getCurrentProfile = () => { | 59 | helpers.mocks.profileService.getCurrentProfile = () => { |
55 | return helpers.mocks.promiseResultTemplate(profile); | 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 | import {Component, Inject, provide} from "ng-forward"; | 1 | import {Component, Inject, provide} from "ng-forward"; |
2 | import {ProfileService} from "../../../lib/ng-noosfero-api/http/profile.service"; | 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 | @Component({ | 5 | @Component({ |
5 | selector: "content-viewer-actions", | 6 | selector: "content-viewer-actions", |
6 | templateUrl: "app/article/content-viewer/navbar-actions.html", | 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 | export class ContentViewerActionsComponent { | 14 | export class ContentViewerActionsComponent { |
11 | 15 | ||
12 | article: noosfero.Article; | 16 | article: noosfero.Article; |
13 | profile: noosfero.Profile; | 17 | profile: noosfero.Profile; |
18 | + parentId: number; | ||
14 | 19 | ||
15 | - constructor(profileService: ProfileService) { | 20 | + constructor(profileService: ProfileService, articleService: ArticleService) { |
16 | profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | 21 | profileService.getCurrentProfile().then((profile: noosfero.Profile) => { |
17 | this.profile = profile; | 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,11 +28,12 @@ export class ContentViewerComponent { | ||
28 | } | 28 | } |
29 | 29 | ||
30 | activate() { | 30 | activate() { |
31 | - this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | 31 | + this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { |
32 | this.profile = profile; | 32 | this.profile = profile; |
33 | return this.articleService.getArticleByProfileAndPath(this.profile, this.$stateParams["page"]); | 33 | return this.articleService.getArticleByProfileAndPath(this.profile, this.$stateParams["page"]); |
34 | }).then((result: noosfero.RestResult<any>) => { | 34 | }).then((result: noosfero.RestResult<any>) => { |
35 | this.article = <noosfero.Article>result.data; | 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 | <ul class="nav navbar-nav"> | 1 | <ul class="nav navbar-nav"> |
2 | <li ng-show="vm.profile"> | 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 | <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} | 9 | <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} |
5 | </a> | 10 | </a> |
6 | </li> | 11 | </li> |
src/app/article/index.ts
1 | /* Module Index Entry - generated using the script npm run generate-index */ | 1 | /* Module Index Entry - generated using the script npm run generate-index */ |
2 | export * from "./article-default-view.component"; | 2 | export * from "./article-default-view.component"; |
3 | -export * from "./basic-editor.component"; |
@@ -0,0 +1,25 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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,12 +28,16 @@ $primary-color-dark: #0288d1; | ||
28 | $default-bg-hover-color: #f8f8f8; | 28 | $default-bg-hover-color: #f8f8f8; |
29 | $red-color: #e84e40; | 29 | $red-color: #e84e40; |
30 | $red-color-dark: #dd191d; | 30 | $red-color-dark: #dd191d; |
31 | +$green-color: #8bc34a; | ||
32 | +$green-color-dark: #689f38; | ||
31 | 33 | ||
32 | //GRID - media queries breakpoints | 34 | //GRID - media queries breakpoints |
33 | $break-xxs-min: 420px; | 35 | $break-xxs-min: 420px; |
34 | $break-xs-min: 768px; | 36 | $break-xs-min: 768px; |
37 | +$break-sm-min: 992px; | ||
35 | 38 | ||
36 | $break-xxs-max: ($break-xxs-min - 1); | 39 | $break-xxs-max: ($break-xxs-min - 1); |
40 | +$break-sm-max: ($break-sm-min - 1); | ||
37 | $break-xs-max: ($break-xs-min - 1); | 41 | $break-xs-max: ($break-xs-min - 1); |
38 | 42 | ||
39 | 43 | ||
@@ -76,4 +80,5 @@ h1, h2, h3, h4, h5 { | @@ -76,4 +80,5 @@ h1, h2, h3, h4, h5 { | ||
76 | @import "layout/scss/mixins"; | 80 | @import "layout/scss/mixins"; |
77 | @import "layout/scss/bootstrap-overrides"; | 81 | @import "layout/scss/bootstrap-overrides"; |
78 | @import "layout/scss/layout"; | 82 | @import "layout/scss/layout"; |
83 | +@import "layout/scss/sidebar"; | ||
79 | @import "layout/scss/tables"; | 84 | @import "layout/scss/tables"; |
src/app/index.ts
@@ -5,7 +5,7 @@ import {noosferoAngularRunBlock} from "./index.run"; | @@ -5,7 +5,7 @@ import {noosferoAngularRunBlock} from "./index.run"; | ||
5 | import {MainComponent} from "./main/main.component"; | 5 | import {MainComponent} from "./main/main.component"; |
6 | import {bootstrap, bundle} from "ng-forward"; | 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 | import {AuthController} from "./login/auth.controller"; | 9 | import {AuthController} from "./login/auth.controller"; |
10 | 10 | ||
11 | import {Navbar} from "./layout/navbar/navbar"; | 11 | import {Navbar} from "./layout/navbar/navbar"; |
@@ -14,16 +14,17 @@ declare var moment: any; | @@ -14,16 +14,17 @@ declare var moment: any; | ||
14 | 14 | ||
15 | let noosferoApp: any = bundle("noosferoApp", MainComponent, ["ngAnimate", "ngCookies", "ngStorage", "ngTouch", | 15 | let noosferoApp: any = bundle("noosferoApp", MainComponent, ["ngAnimate", "ngCookies", "ngStorage", "ngTouch", |
16 | "ngSanitize", "ngMessages", "ngAria", "restangular", | 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 | "angular-timeline", "duScroll", "oitozero.ngSweetAlert", | 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 | NoosferoApp.angularModule = noosferoApp; | 23 | NoosferoApp.angularModule = noosferoApp; |
23 | 24 | ||
24 | 25 | ||
25 | NoosferoApp.addConstants("moment", moment); | 26 | NoosferoApp.addConstants("moment", moment); |
26 | -NoosferoApp.addConstants("AUTH_EVENTS", AUTH_EVENTS); | 27 | +NoosferoApp.addConstants("AuthEvents", AuthEvents); |
27 | 28 | ||
28 | NoosferoApp.addConfig(noosferoModuleConfig); | 29 | NoosferoApp.addConfig(noosferoModuleConfig); |
29 | NoosferoApp.run(noosferoAngularRunBlock); | 30 | NoosferoApp.run(noosferoAngularRunBlock); |
src/app/layout/blocks/block.component.ts
@@ -11,7 +11,7 @@ export class BlockComponent { | @@ -11,7 +11,7 @@ export class BlockComponent { | ||
11 | @Input() owner: any; | 11 | @Input() owner: any; |
12 | 12 | ||
13 | ngOnInit() { | 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 | this.$element.replaceWith(this.$compile('<noosfero-' + blockName + ' [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-' + blockName + '>')(this.$scope)); | 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,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,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
@@ -1,16 +0,0 @@ | @@ -1,16 +0,0 @@ | ||
1 | -.communities-block { | ||
2 | - .profile { | ||
3 | - margin: 10px; | ||
4 | - img, i.profile-image { | ||
5 | - width: 60px; | ||
6 | - } | ||
7 | - img { | ||
8 | - display: inline-block; | ||
9 | - vertical-align: top; | ||
10 | - } | ||
11 | - i.profile-image { | ||
12 | - text-align: center; | ||
13 | - font-size: 4.5em; | ||
14 | - } | ||
15 | - } | ||
16 | -} |
src/app/layout/blocks/communities/communities-block.component.spec.ts
0 → 100644
@@ -0,0 +1,51 @@ | @@ -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 @@ | @@ -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
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +.communities-block { | ||
2 | + .profile { | ||
3 | + margin: 10px; | ||
4 | + img, i.profile-image { | ||
5 | + width: 60px; | ||
6 | + } | ||
7 | + img { | ||
8 | + display: inline-block; | ||
9 | + vertical-align: top; | ||
10 | + } | ||
11 | + i.profile-image { | ||
12 | + text-align: center; | ||
13 | + font-size: 4.5em; | ||
14 | + } | ||
15 | + } | ||
16 | +} |
src/app/layout/blocks/link-list/index.ts
src/app/layout/blocks/link-list/link-list-block.component.spec.ts
0 → 100644
@@ -0,0 +1,65 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/layout/blocks/link-list/link-list.component.ts
@@ -1,20 +0,0 @@ | @@ -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,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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/layout/blocks/main-block/main-block.component.ts
@@ -1,10 +0,0 @@ | @@ -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 +0,0 @@ | ||
1 | -<div ui-view="mainBlockContent" autoscroll></div> |
@@ -0,0 +1,40 @@ | @@ -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 | \ No newline at end of file | 41 | \ No newline at end of file |
@@ -0,0 +1,10 @@ | @@ -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 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/layout/blocks/members-block/members-block.component.ts
@@ -1,25 +0,0 @@ | @@ -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,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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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,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 | \ No newline at end of file | 0 | \ No newline at end of file |
src/app/layout/blocks/people-block/people-block.component.ts
@@ -1,26 +0,0 @@ | @@ -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,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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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,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 | -}); |