Commit 2417cffc30d329bc61d747789090b4b0ba43cc40
Exists in
master
and in
26 other branches
Merge branch 'remove-article' into 'master'
Remove article See merge request !22
Showing
13 changed files
with
342 additions
and
103 deletions
Show diff stats
src/app/article/article-default-view-component.spec.ts
1 | import {Input, provide, Component} from 'ng-forward'; | 1 | import {Input, provide, Component} from 'ng-forward'; |
2 | import {ArticleViewComponent, ArticleDefaultViewComponent} from './article-default-view.component'; | 2 | import {ArticleViewComponent, ArticleDefaultViewComponent} from './article-default-view.component'; |
3 | +import {ComponentTestHelper, createClass} from './../../spec/component-test-helper'; | ||
3 | 4 | ||
4 | import * as helpers from "../../spec/helpers"; | 5 | import * as helpers from "../../spec/helpers"; |
5 | 6 | ||
@@ -9,113 +10,82 @@ const htmlTemplate: string = '<noosfero-article [article]="ctrl.article" [profil | @@ -9,113 +10,82 @@ const htmlTemplate: string = '<noosfero-article [article]="ctrl.article" [profil | ||
9 | 10 | ||
10 | describe("Components", () => { | 11 | describe("Components", () => { |
11 | 12 | ||
12 | - describe("ArticleView 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 Noosfero ArtileView will be load on our tests | ||
18 | - beforeEach(angular.mock.module("templates")); | ||
19 | - | ||
20 | - it("renders the default component when no specific component is found", (done: Function) => { | ||
21 | - // Creating a container component (ArticleContainerComponent) to include | ||
22 | - // the component under test (ArticleView) | ||
23 | - @Component({ | ||
24 | - selector: 'test-container-component', | ||
25 | - template: htmlTemplate, | ||
26 | - directives: [ArticleViewComponent], | ||
27 | - providers: [ | ||
28 | - helpers.createProviderToValue('CommentService', helpers.mocks.commentService), | ||
29 | - helpers.provideFilters("translateFilter"), | ||
30 | - helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService), | ||
31 | - helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})) | ||
32 | - ] | ||
33 | - }) | ||
34 | - class ArticleContainerComponent { | ||
35 | - article = { type: 'anyArticleType' }; | ||
36 | - profile = { name: 'profile-name' }; | 13 | + // the karma preprocessor html2js transform the templates html into js files which put |
14 | + // the templates to the templateCache into the module templates | ||
15 | + // we need to load the module templates here as the template for the | ||
16 | + // component Noosfero ArtileView will be load on our tests | ||
17 | + beforeEach(angular.mock.module("templates")); | ||
18 | + | ||
19 | + describe("Article Default View Component", () => { | ||
20 | + let helper: ComponentTestHelper<ArticleDefaultViewComponent>; | ||
21 | + const defaultViewTemplate: string = '<noosfero-default-article [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-default-article>'; | ||
22 | + let articleService: any = helpers.mocks.articleService; | ||
23 | + let article = <noosfero.Article>{ | ||
24 | + id: 1, | ||
25 | + profile: { | ||
26 | + identifier: "1" | ||
37 | } | 27 | } |
38 | - | ||
39 | - helpers.createComponentFromClass(ArticleContainerComponent).then((fixture) => { | ||
40 | - // and here we can inspect and run the test assertions | ||
41 | - | ||
42 | - // gets the children component of ArticleContainerComponent | ||
43 | - let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
44 | - | ||
45 | - // and checks if the article View rendered was the Default Article View | ||
46 | - expect(articleView.constructor.prototype).toEqual(ArticleDefaultViewComponent.prototype); | ||
47 | - | ||
48 | - // done needs to be called (it isn't really needed, as we can read in | ||
49 | - // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | ||
50 | - // because createAsync in ng-forward is not really async, but as the intention | ||
51 | - // here is write tests in angular 2 ways, this is recommended | ||
52 | - done(); | 28 | + }; |
29 | + let state = <ng.ui.IStateService>jasmine.createSpyObj("state", ["go", "transitionTo"]); | ||
30 | + let providers = [ | ||
31 | + provide('$state', { useValue: state }), | ||
32 | + provide('ArticleService', { useValue: articleService }) | ||
33 | + ].concat(helpers.provideFilters("translateFilter")); | ||
34 | + | ||
35 | + /** | ||
36 | + * The beforeEach procedure will initialize the helper and parse | ||
37 | + * the component according to the given providers. Unfortunetly, in | ||
38 | + * this mode, the providers and properties given to the construtor | ||
39 | + * can't be overriden. | ||
40 | + */ | ||
41 | + beforeEach((done) => { | ||
42 | + // Create the component bed for the test. Optionally, this could be done | ||
43 | + // in each test if one needs customization of these parameters per test | ||
44 | + let cls = createClass({ | ||
45 | + template: defaultViewTemplate, | ||
46 | + directives: [ArticleDefaultViewComponent], | ||
47 | + providers: providers, | ||
48 | + properties: { | ||
49 | + article: article | ||
50 | + } | ||
53 | }); | 51 | }); |
54 | - | 52 | + helper = new ComponentTestHelper<ArticleDefaultViewComponent>(cls, done); |
55 | }); | 53 | }); |
56 | 54 | ||
57 | - it("receives the article and profile as inputs", (done: Function) => { | ||
58 | - | ||
59 | - // Creating a container component (ArticleContainerComponent) to include | ||
60 | - // the component under test (ArticleView) | ||
61 | - @Component({ | ||
62 | - selector: 'test-container-component', | ||
63 | - template: htmlTemplate, | ||
64 | - directives: [ArticleViewComponent], | ||
65 | - providers: [ | ||
66 | - helpers.createProviderToValue('CommentService', helpers.mocks.commentService), | ||
67 | - helpers.provideFilters("translateFilter"), | ||
68 | - helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService), | ||
69 | - helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})) | ||
70 | - ] | ||
71 | - }) | ||
72 | - class ArticleContainerComponent { | ||
73 | - article = { type: 'anyArticleType' }; | ||
74 | - profile = { name: 'profile-name' }; | ||
75 | - } | ||
76 | - | ||
77 | - // uses the TestComponentBuilder instance to initialize the component | ||
78 | - helpers.createComponentFromClass(ArticleContainerComponent).then((fixture) => { | ||
79 | - // and here we can inspect and run the test assertions | ||
80 | - let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | 55 | + function getArticle() { |
56 | + return this.article; | ||
57 | + } | ||
81 | 58 | ||
82 | - // assure the article object inside the ArticleView matches | ||
83 | - // the provided through the parent component | ||
84 | - expect(articleView.article.type).toEqual("anyArticleType"); | ||
85 | - expect(articleView.profile.name).toEqual("profile-name"); | ||
86 | - | ||
87 | - // done needs to be called (it isn't really needed, as we can read in | ||
88 | - // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | ||
89 | - // because createAsync in ng-forward is not really async, but as the intention | ||
90 | - // here is write tests in angular 2 ways, this is recommended | ||
91 | - done(); | ||
92 | - }); | 59 | + it("it should delete article when delete is activated", () => { |
60 | + expect(helper.component.article).toEqual(article); | ||
61 | + // Spy the state service | ||
62 | + doDeleteArticle(); | ||
63 | + expect(state.transitionTo).toHaveBeenCalled(); | ||
93 | }); | 64 | }); |
94 | 65 | ||
95 | - | ||
96 | - it("renders a article view which matches to the article type", done => { | ||
97 | - // NoosferoTinyMceArticle component created to check if it will be used | ||
98 | - // when a article with type 'TinyMceArticle' is provided to the noosfero-article (ArticleView) | ||
99 | - // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | ||
100 | - @Component({ selector: 'noosfero-tiny-mce-article', template: "<h1>TinyMceArticle</h1>" }) | ||
101 | - class TinyMceArticleView { | ||
102 | - @Input() article: any; | ||
103 | - @Input() profile: any; | ||
104 | - } | ||
105 | - | ||
106 | - // Creating a container component (ArticleContainerComponent) to include our NoosferoTinyMceArticle | ||
107 | - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent, TinyMceArticleView] }) | ||
108 | - class CustomArticleType { | ||
109 | - article = { type: 'TinyMceArticle' }; | ||
110 | - profile = { name: 'profile-name' }; | ||
111 | - } | ||
112 | - helpers.createComponentFromClass(CustomArticleType).then(fixture => { | ||
113 | - let myComponent: CustomArticleType = fixture.componentInstance; | ||
114 | - expect(myComponent.article.type).toEqual("TinyMceArticle"); | ||
115 | - expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("TinyMceArticle"); | ||
116 | - done(); | 66 | + /** |
67 | + * Execute the delete method on the target component | ||
68 | + */ | ||
69 | + function doDeleteArticle() { | ||
70 | + // Create a mock for the ArticleService removeArticle method | ||
71 | + spyOn(helper.component.articleService, 'removeArticle').and.callFake(function(param: noosfero.Article) { | ||
72 | + return { | ||
73 | + catch: () => {} | ||
74 | + }; | ||
117 | }); | 75 | }); |
118 | - }); | ||
119 | - | 76 | + helper.component.delete(); |
77 | + expect(articleService.removeArticle).toHaveBeenCalled(); | ||
78 | + // After the component delete method execution, fire the | ||
79 | + // ArticleEvent.removed event | ||
80 | + simulateRemovedEvent(); | ||
81 | + } | ||
82 | + | ||
83 | + /** | ||
84 | + * Simulate the ArticleService ArticleEvent.removed event | ||
85 | + */ | ||
86 | + function simulateRemovedEvent() { | ||
87 | + helper.component.articleService["notifyArticleRemovedListeners"](article); | ||
88 | + } | ||
120 | }); | 89 | }); |
90 | + | ||
121 | }); | 91 | }); |
src/app/article/article-default-view.component.ts
@@ -4,6 +4,7 @@ import {CommentsComponent} from "./comment/comments.component"; | @@ -4,6 +4,7 @@ import {CommentsComponent} from "./comment/comments.component"; | ||
4 | import {MacroDirective} from "./macro/macro.directive"; | 4 | import {MacroDirective} from "./macro/macro.directive"; |
5 | import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot.component"; | 5 | import {ArticleToolbarHotspotComponent} from "../hotspot/article-toolbar-hotspot.component"; |
6 | import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot.component"; | 6 | import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot.component"; |
7 | +import {ArticleService} from "./../../lib/ng-noosfero-api/http/article.service"; | ||
7 | 8 | ||
8 | /** | 9 | /** |
9 | * @ngdoc controller | 10 | * @ngdoc controller |
@@ -16,11 +17,29 @@ import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot | @@ -16,11 +17,29 @@ import {ArticleContentHotspotComponent} from "../hotspot/article-content-hotspot | ||
16 | selector: 'noosfero-default-article', | 17 | selector: 'noosfero-default-article', |
17 | templateUrl: 'app/article/article.html' | 18 | templateUrl: 'app/article/article.html' |
18 | }) | 19 | }) |
20 | +@Inject("$state", ArticleService) | ||
19 | export class ArticleDefaultViewComponent { | 21 | export class ArticleDefaultViewComponent { |
20 | 22 | ||
21 | @Input() article: noosfero.Article; | 23 | @Input() article: noosfero.Article; |
22 | @Input() profile: noosfero.Profile; | 24 | @Input() profile: noosfero.Profile; |
23 | 25 | ||
26 | + constructor(private $state: ng.ui.IStateService, public articleService: ArticleService) { | ||
27 | + // Subscribe to the Article Removed Event | ||
28 | + this.articleService.subscribeToArticleRemoved((article: noosfero.Article) => { | ||
29 | + if (this.article.parent) { | ||
30 | + this.$state.transitionTo('main.profile.page', { page: this.article.parent.path, profile: this.article.profile.identifier }); | ||
31 | + } else { | ||
32 | + this.$state.transitionTo('main.profile.info', { profile: this.article.profile.identifier }); | ||
33 | + } | ||
34 | + }); | ||
35 | + } | ||
36 | + | ||
37 | + delete() { | ||
38 | + this.articleService.removeArticle(this.article).catch((cause: any) => { | ||
39 | + throw new Error(`Problem removing the article: ${cause}`); | ||
40 | + }); | ||
41 | + } | ||
42 | + | ||
24 | } | 43 | } |
25 | 44 | ||
26 | /** | 45 | /** |
@@ -60,4 +79,5 @@ export class ArticleViewComponent { | @@ -60,4 +79,5 @@ export class ArticleViewComponent { | ||
60 | private $injector: ng.auto.IInjectorService, | 79 | private $injector: ng.auto.IInjectorService, |
61 | private $compile: ng.ICompileService) { | 80 | private $compile: ng.ICompileService) { |
62 | } | 81 | } |
82 | + | ||
63 | } | 83 | } |
@@ -0,0 +1,124 @@ | @@ -0,0 +1,124 @@ | ||
1 | +import {Input, provide, Component} from 'ng-forward'; | ||
2 | +import {ArticleViewComponent, ArticleDefaultViewComponent} from './article-default-view.component'; | ||
3 | + | ||
4 | +import * as helpers from "../../spec/helpers"; | ||
5 | + | ||
6 | +// this htmlTemplate will be re-used between the container components in this spec file | ||
7 | +const htmlTemplate: string = '<noosfero-article [article]="ctrl.article" [profile]="ctrl.profile"></noosfero-article>'; | ||
8 | + | ||
9 | + | ||
10 | +describe("Components", () => { | ||
11 | + | ||
12 | + // the karma preprocessor html2js transform the templates html into js files which put | ||
13 | + // the templates to the templateCache into the module templates | ||
14 | + // we need to load the module templates here as the template for the | ||
15 | + // component Noosfero ArtileView will be load on our tests | ||
16 | + beforeEach(angular.mock.module("templates")); | ||
17 | + | ||
18 | + describe("ArticleView Component", () => { | ||
19 | + let state = <ng.ui.IStateService>jasmine.createSpyObj("state", ["go", "transitionTo"]); | ||
20 | + it("renders the default component when no specific component is found", (done: Function) => { | ||
21 | + // Creating a container component (ArticleContainerComponent) to include | ||
22 | + // the component under test (ArticleView) | ||
23 | + @Component({ | ||
24 | + selector: 'test-container-component', | ||
25 | + template: htmlTemplate, | ||
26 | + directives: [ArticleViewComponent], | ||
27 | + providers: [ | ||
28 | + helpers.createProviderToValue('CommentService', helpers.mocks.commentService), | ||
29 | + helpers.provideFilters("translateFilter"), | ||
30 | + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService), | ||
31 | + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})), | ||
32 | + helpers.createProviderToValue('ArticleService', helpers.mocks.articleService), | ||
33 | + helpers.createProviderToValue('$state', state) | ||
34 | + ] | ||
35 | + }) | ||
36 | + class ArticleContainerComponent { | ||
37 | + article = { type: 'anyArticleType' }; | ||
38 | + profile = { name: 'profile-name' }; | ||
39 | + } | ||
40 | + | ||
41 | + helpers.createComponentFromClass(ArticleContainerComponent).then((fixture) => { | ||
42 | + // and here we can inspect and run the test assertions | ||
43 | + | ||
44 | + // gets the children component of ArticleContainerComponent | ||
45 | + let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
46 | + | ||
47 | + // and checks if the article View rendered was the Default Article View | ||
48 | + expect(articleView.constructor.prototype).toEqual(ArticleDefaultViewComponent.prototype); | ||
49 | + | ||
50 | + // done needs to be called (it isn't really needed, as we can read in | ||
51 | + // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | ||
52 | + // because createAsync in ng-forward is not really async, but as the intention | ||
53 | + // here is write tests in angular 2 ways, this is recommended | ||
54 | + done(); | ||
55 | + }); | ||
56 | + }); | ||
57 | + | ||
58 | + it("receives the article and profile as inputs", (done: Function) => { | ||
59 | + | ||
60 | + // Creating a container component (ArticleContainerComponent) to include | ||
61 | + // the component under test (ArticleView) | ||
62 | + @Component({ | ||
63 | + selector: 'test-container-component', | ||
64 | + template: htmlTemplate, | ||
65 | + directives: [ArticleViewComponent], | ||
66 | + providers: [ | ||
67 | + helpers.createProviderToValue('CommentService', helpers.mocks.commentService), | ||
68 | + helpers.provideFilters("translateFilter"), | ||
69 | + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService), | ||
70 | + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})), | ||
71 | + helpers.createProviderToValue('ArticleService', helpers.mocks.articleService), | ||
72 | + helpers.createProviderToValue('$state', state) | ||
73 | + ] | ||
74 | + }) | ||
75 | + class ArticleContainerComponent { | ||
76 | + article = { type: 'anyArticleType' }; | ||
77 | + profile = { name: 'profile-name' }; | ||
78 | + } | ||
79 | + | ||
80 | + // uses the TestComponentBuilder instance to initialize the component | ||
81 | + helpers.createComponentFromClass(ArticleContainerComponent).then((fixture) => { | ||
82 | + // and here we can inspect and run the test assertions | ||
83 | + let articleView: ArticleViewComponent = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
84 | + | ||
85 | + // assure the article object inside the ArticleView matches | ||
86 | + // the provided through the parent component | ||
87 | + expect(articleView.article.type).toEqual("anyArticleType"); | ||
88 | + expect(articleView.profile.name).toEqual("profile-name"); | ||
89 | + | ||
90 | + // done needs to be called (it isn't really needed, as we can read in | ||
91 | + // here (https://github.com/ngUpgraders/ng-forward/blob/master/API.md#createasync) | ||
92 | + // because createAsync in ng-forward is not really async, but as the intention | ||
93 | + // here is write tests in angular 2 ways, this is recommended | ||
94 | + done(); | ||
95 | + }); | ||
96 | + }); | ||
97 | + | ||
98 | + | ||
99 | + it("renders a article view which matches to the article type", done => { | ||
100 | + // NoosferoTinyMceArticle component created to check if it will be used | ||
101 | + // when a article with type 'TinyMceArticle' is provided to the noosfero-article (ArticleView) | ||
102 | + // *** Important *** - the selector is what ng-forward uses to define the name of the directive provider | ||
103 | + @Component({ selector: 'noosfero-tiny-mce-article', template: "<h1>TinyMceArticle</h1>" }) | ||
104 | + class TinyMceArticleView { | ||
105 | + @Input() article: any; | ||
106 | + @Input() profile: any; | ||
107 | + } | ||
108 | + | ||
109 | + // Creating a container component (ArticleContainerComponent) to include our NoosferoTinyMceArticle | ||
110 | + @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [ArticleViewComponent, TinyMceArticleView] }) | ||
111 | + class CustomArticleType { | ||
112 | + article = { type: 'TinyMceArticle' }; | ||
113 | + profile = { name: 'profile-name' }; | ||
114 | + } | ||
115 | + helpers.createComponentFromClass(CustomArticleType).then(fixture => { | ||
116 | + let myComponent: CustomArticleType = fixture.componentInstance; | ||
117 | + expect(myComponent.article.type).toEqual("TinyMceArticle"); | ||
118 | + expect(fixture.debugElement.componentViewChildren[0].text()).toEqual("TinyMceArticle"); | ||
119 | + done(); | ||
120 | + }); | ||
121 | + }); | ||
122 | + | ||
123 | + }); | ||
124 | +}); |
src/app/article/article.html
@@ -7,6 +7,9 @@ | @@ -7,6 +7,9 @@ | ||
7 | <a href="#" class="btn btn-default btn-xs" ui-sref="main.cmsEdit({profile: ctrl.profile.identifier, id: ctrl.article.id})"> | 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}} | 8 | <i class="fa fa-pencil-square-o fa-fw fa-lg"></i> {{"article.actions.edit" | translate}} |
9 | </a> | 9 | </a> |
10 | + <a href="#" class="btn btn-default btn-xs" ng-click="ctrl.delete()"> | ||
11 | + <i class="fa fa-trash-o fa-fw fa-lg" ng-click="ctrl.delete()"></i> {{"article.actions.delete" | translate}} | ||
12 | + </a> | ||
10 | <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar> | 13 | <noosfero-hotspot-article-toolbar [article]="ctrl.article"></noosfero-hotspot-article-toolbar> |
11 | <div class="page-info pull-right small text-muted"> | 14 | <div class="page-info pull-right small text-muted"> |
12 | <span class="time"> | 15 | <span class="time"> |
src/app/shared/models/interfaces.ts
@@ -6,7 +6,6 @@ export interface UserResponse { | @@ -6,7 +6,6 @@ export interface UserResponse { | ||
6 | user: noosfero.User; | 6 | user: noosfero.User; |
7 | } | 7 | } |
8 | 8 | ||
9 | - | ||
10 | export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { | 9 | export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { |
11 | currentUser: noosfero.User; | 10 | currentUser: noosfero.User; |
12 | } | 11 | } |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +export class ArrayUtils { | ||
2 | + | ||
3 | + /** | ||
4 | + * Returns if two arrays are equal | ||
5 | + */ | ||
6 | + static arraysEqual(a: any, b: any): boolean { | ||
7 | + if (a === b) return true; | ||
8 | + if (a == null || b == null) return false; | ||
9 | + if (a.length !== b.length) return false; | ||
10 | + | ||
11 | + // If you don't care about the order of the elements inside | ||
12 | + // the array, you should sort both arrays here. | ||
13 | + for (let i = 0; i < a.length; ++i) { | ||
14 | + if (a[i] !== b[i]) return false; | ||
15 | + } | ||
16 | + return true; | ||
17 | + } | ||
18 | + | ||
19 | +} | ||
0 | \ No newline at end of file | 20 | \ No newline at end of file |
@@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
1 | +export class HashMap<K extends EqualsObject, V> { | ||
2 | + private values: Array<HashItem<K, V>> = new Array(); | ||
3 | + | ||
4 | + get(key: K): V { | ||
5 | + let found = this.values.find( function(value: HashItem<K, V>) { | ||
6 | + return value.key.equals(key); | ||
7 | + }); | ||
8 | + if (found) { | ||
9 | + return found.value; | ||
10 | + } | ||
11 | + return null; | ||
12 | + } | ||
13 | + | ||
14 | + put(key: K, value: V): void { | ||
15 | + this.values.push(new HashItem(key, value)); | ||
16 | + } | ||
17 | + | ||
18 | + clear() { | ||
19 | + this.values = new Array(); | ||
20 | + } | ||
21 | +} | ||
22 | + | ||
23 | +export class HashItem<K, V> { | ||
24 | + key: K; | ||
25 | + value: V; | ||
26 | + constructor(key: K, value: V) { | ||
27 | + this.key = key; | ||
28 | + this.value = value; | ||
29 | + } | ||
30 | +} | ||
31 | + | ||
32 | +export abstract class EqualsObject { | ||
33 | + | ||
34 | + abstract equals(other: any): boolean; | ||
35 | + | ||
36 | +} | ||
0 | \ No newline at end of file | 37 | \ No newline at end of file |
src/languages/en.json
@@ -37,6 +37,7 @@ | @@ -37,6 +37,7 @@ | ||
37 | "comment.post.success.message": "Comment saved!", | 37 | "comment.post.success.message": "Comment saved!", |
38 | "comment.reply": "reply", | 38 | "comment.reply": "reply", |
39 | "article.actions.edit": "Edit", | 39 | "article.actions.edit": "Edit", |
40 | + "article.actions.delete": "Delete", | ||
40 | "article.actions.read_more": "Read More", | 41 | "article.actions.read_more": "Read More", |
41 | "article.basic_editor.title": "Title", | 42 | "article.basic_editor.title": "Title", |
42 | "article.basic_editor.body": "Body", | 43 | "article.basic_editor.body": "Body", |
src/languages/pt.json
@@ -37,6 +37,7 @@ | @@ -37,6 +37,7 @@ | ||
37 | "comment.post.success.message": "Comentário salvo com sucesso!", | 37 | "comment.post.success.message": "Comentário salvo com sucesso!", |
38 | "comment.reply": "responder", | 38 | "comment.reply": "responder", |
39 | "article.actions.edit": "Editar", | 39 | "article.actions.edit": "Editar", |
40 | + "article.actions.delete": "Excluir", | ||
40 | "article.actions.read_more": "Ler mais", | 41 | "article.actions.read_more": "Ler mais", |
41 | "article.basic_editor.title": "Título", | 42 | "article.basic_editor.title": "Título", |
42 | "article.basic_editor.body": "Corpo", | 43 | "article.basic_editor.body": "Corpo", |
src/lib/ng-noosfero-api/http/article.service.spec.ts
@@ -20,6 +20,15 @@ describe("Services", () => { | @@ -20,6 +20,15 @@ describe("Services", () => { | ||
20 | 20 | ||
21 | describe("Succesfull requests", () => { | 21 | describe("Succesfull requests", () => { |
22 | 22 | ||
23 | + it("should remove article", (done) => { | ||
24 | + let articleId = 1; | ||
25 | + $httpBackend.expectDELETE(`/api/v1/articles/${articleId}`).respond(200, { success: "true" }); | ||
26 | + articleService.removeArticle(<noosfero.Article>{id: articleId}); | ||
27 | + $httpBackend.flush(); | ||
28 | + $httpBackend.verifyNoOutstandingExpectation(); | ||
29 | + done(); | ||
30 | + }); | ||
31 | + | ||
23 | it("should return article children", (done) => { | 32 | it("should return article children", (done) => { |
24 | let articleId = 1; | 33 | let articleId = 1; |
25 | $httpBackend.expectGET(`/api/v1/articles/${articleId}/children`).respond(200, { articles: [{ name: "article1" }] }); | 34 | $httpBackend.expectGET(`/api/v1/articles/${articleId}/children`).respond(200, { articles: [{ name: "article1" }] }); |
src/lib/ng-noosfero-api/http/article.service.ts
1 | -import { Injectable, Inject } from "ng-forward"; | 1 | +import { Injectable, Inject, EventEmitter } from "ng-forward"; |
2 | import {RestangularService} from "./restangular_service"; | 2 | import {RestangularService} from "./restangular_service"; |
3 | import {ProfileService} from "./profile.service"; | 3 | import {ProfileService} from "./profile.service"; |
4 | +import {NoosferoRootScope} from "./../../../app/shared/models/interfaces"; | ||
4 | 5 | ||
5 | @Injectable() | 6 | @Injectable() |
6 | @Inject("Restangular", "$q", "$log", ProfileService) | 7 | @Inject("Restangular", "$q", "$log", ProfileService) |
7 | - | ||
8 | export class ArticleService extends RestangularService<noosfero.Article> { | 8 | export class ArticleService extends RestangularService<noosfero.Article> { |
9 | 9 | ||
10 | + private articleRemoved: EventEmitter<noosfero.Article> = new EventEmitter<noosfero.Article>(); | ||
11 | + | ||
10 | constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService, protected profileService: ProfileService) { | 12 | constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService, protected profileService: ProfileService) { |
11 | super(Restangular, $q, $log); | 13 | super(Restangular, $q, $log); |
12 | } | 14 | } |
@@ -22,6 +24,31 @@ export class ArticleService extends RestangularService<noosfero.Article> { | @@ -22,6 +24,31 @@ export class ArticleService extends RestangularService<noosfero.Article> { | ||
22 | }; | 24 | }; |
23 | } | 25 | } |
24 | 26 | ||
27 | + removeArticle(article: noosfero.Article) { | ||
28 | + let restRequest: ng.IPromise<noosfero.RestResult<noosfero.Article>> = this.remove(article); | ||
29 | + let deferred = this.$q.defer<noosfero.RestResult<noosfero.Article>>(); | ||
30 | + restRequest.then((result: any) => { | ||
31 | + this.notifyArticleRemovedListeners(article); | ||
32 | + }).catch(this.getHandleErrorFunction(deferred)); | ||
33 | + return deferred.promise; | ||
34 | + } | ||
35 | + | ||
36 | + /** | ||
37 | + * Notify listeners that this article has been removed | ||
38 | + */ | ||
39 | + private notifyArticleRemovedListeners(article: noosfero.Article) { | ||
40 | + // let listener = this.events.get(this.removed); | ||
41 | + // listener.next(article); | ||
42 | + this.articleRemoved.next(article); | ||
43 | + } | ||
44 | + | ||
45 | + /** | ||
46 | + * subscribes to the ArticleRemoved event emitter | ||
47 | + */ | ||
48 | + subscribeToArticleRemoved(fn: Function) { | ||
49 | + this.articleRemoved.subscribe(fn); | ||
50 | + } | ||
51 | + | ||
25 | updateArticle(article: noosfero.Article) { | 52 | updateArticle(article: noosfero.Article) { |
26 | let headers = { | 53 | let headers = { |
27 | 'Content-Type': 'application/json' | 54 | 'Content-Type': 'application/json' |
@@ -103,3 +130,4 @@ export class ArticleService extends RestangularService<noosfero.Article> { | @@ -103,3 +130,4 @@ export class ArticleService extends RestangularService<noosfero.Article> { | ||
103 | 130 | ||
104 | 131 | ||
105 | } | 132 | } |
133 | + |
src/spec/mocks.ts
@@ -76,6 +76,32 @@ export var mocks: any = { | @@ -76,6 +76,32 @@ export var mocks: any = { | ||
76 | isAuthenticated: () => { } | 76 | isAuthenticated: () => { } |
77 | }, | 77 | }, |
78 | articleService: { | 78 | articleService: { |
79 | + articleRemovedFn: null, | ||
80 | + subscribeToArticleRemoved: (fn: Function) => { | ||
81 | + mocks.articleService.articleRemovedFn = fn; | ||
82 | + }, | ||
83 | + articleRemoved: | ||
84 | + { | ||
85 | + subscribe: (fn: Function) => { | ||
86 | + mocks.articleService.articleRemovedFn = fn; | ||
87 | + }, | ||
88 | + next: (param: any) => { | ||
89 | + mocks.articleService.articleRemovedFn(param); | ||
90 | + } | ||
91 | + } | ||
92 | + , | ||
93 | + removeArticle: (article: noosfero.Article) => { | ||
94 | + return { | ||
95 | + catch: (func?: Function) => { | ||
96 | + } | ||
97 | + }; | ||
98 | + }, | ||
99 | + notifyArticleRemovedListeners: (article: noosfero.Article) => { | ||
100 | + mocks.articleService.articleRemoved.next(article); | ||
101 | + }, | ||
102 | + subscribe: (eventType: any, fn: Function) => { | ||
103 | + mocks.articleService.articleRemoved.subscribe(fn); | ||
104 | + }, | ||
79 | getByProfile: (profileId: number, params?: any) => { | 105 | getByProfile: (profileId: number, params?: any) => { |
80 | return { | 106 | return { |
81 | then: (func?: Function) => { | 107 | then: (func?: Function) => { |