Commit 2259690d92c4ced0e7a5a8a08be67b27d8112932
1 parent
28d86107
Exists in
master
and in
27 other branches
Create article and associate it to parent
Showing
11 changed files
with
89 additions
and
20 deletions
Show diff stats
src/app/article/basic-editor.component.spec.ts
@@ -9,6 +9,7 @@ describe("Article BasicEditor", () => { | @@ -9,6 +9,7 @@ describe("Article BasicEditor", () => { | ||
9 | let articleServiceMock: any; | 9 | let articleServiceMock: any; |
10 | let profileServiceMock: any; | 10 | let profileServiceMock: any; |
11 | let $state: any; | 11 | let $state: any; |
12 | + let $stateParams: any; | ||
12 | let profile = { id: 1 }; | 13 | let profile = { id: 1 }; |
13 | let notification: any; | 14 | let notification: any; |
14 | 15 | ||
@@ -20,9 +21,10 @@ describe("Article BasicEditor", () => { | @@ -20,9 +21,10 @@ describe("Article BasicEditor", () => { | ||
20 | 21 | ||
21 | beforeEach(() => { | 22 | beforeEach(() => { |
22 | $state = jasmine.createSpyObj("$state", ["transitionTo"]); | 23 | $state = jasmine.createSpyObj("$state", ["transitionTo"]); |
24 | + $stateParams = jasmine.createSpyObj("$stateParams", ["parent_id"]); | ||
23 | notification = jasmine.createSpyObj("notification", ["success"]); | 25 | notification = jasmine.createSpyObj("notification", ["success"]); |
24 | profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]); | 26 | profileServiceMock = jasmine.createSpyObj("profileServiceMock", ["getCurrentProfile"]); |
25 | - articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInProfile"]); | 27 | + articleServiceMock = jasmine.createSpyObj("articleServiceMock", ["createInParent"]); |
26 | 28 | ||
27 | let getCurrentProfileResponse = $q.defer(); | 29 | let getCurrentProfileResponse = $q.defer(); |
28 | getCurrentProfileResponse.resolve(profile); | 30 | getCurrentProfileResponse.resolve(profile); |
@@ -31,20 +33,20 @@ describe("Article BasicEditor", () => { | @@ -31,20 +33,20 @@ describe("Article BasicEditor", () => { | ||
31 | articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } }); | 33 | articleCreate.resolve({ data: { path: "path", profile: { identifier: "profile" } } }); |
32 | 34 | ||
33 | profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); | 35 | profileServiceMock.getCurrentProfile = jasmine.createSpy("getCurrentProfile").and.returnValue(getCurrentProfileResponse.promise); |
34 | - articleServiceMock.createInProfile = jasmine.createSpy("createInProfile").and.returnValue(articleCreate.promise); | 36 | + articleServiceMock.createInParent = jasmine.createSpy("createInParent").and.returnValue(articleCreate.promise); |
35 | }); | 37 | }); |
36 | 38 | ||
37 | it("create an article in the current profile when save", done => { | 39 | it("create an article in the current profile when save", done => { |
38 | - let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification); | 40 | + let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams); |
3 |
|
||
39 | component.save(); | 41 | component.save(); |
40 | $rootScope.$apply(); | 42 | $rootScope.$apply(); |
41 | expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); | 43 | expect(profileServiceMock.getCurrentProfile).toHaveBeenCalled(); |
42 | - expect(articleServiceMock.createInProfile).toHaveBeenCalledWith(profile, component.article); | 44 | + expect(articleServiceMock.createInParent).toHaveBeenCalledWith($stateParams.parent_id, component.article); |
43 | done(); | 45 | done(); |
44 | }); | 46 | }); |
45 | 47 | ||
46 | it("got to the new article page and display an alert when saving sucessfully", done => { | 48 | 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); | 49 | + let component: BasicEditorComponent = new BasicEditorComponent(articleServiceMock, profileServiceMock, $state, notification, $stateParams); |
48 | component.save(); | 50 | component.save(); |
49 | $rootScope.$apply(); | 51 | $rootScope.$apply(); |
50 | expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); | 52 | expect($state.transitionTo).toHaveBeenCalledWith("main.profile.page", { page: "path", profile: "profile" }); |
src/app/article/basic-editor.component.ts
@@ -12,19 +12,25 @@ import {NotificationService} from "../shared/services/notification.service.ts"; | @@ -12,19 +12,25 @@ import {NotificationService} from "../shared/services/notification.service.ts"; | ||
12 | provide('notification', { useClass: NotificationService }) | 12 | provide('notification', { useClass: NotificationService }) |
13 | ] | 13 | ] |
14 | }) | 14 | }) |
15 | -@Inject(ArticleService, ProfileService, "$state", NotificationService) | 15 | +@Inject(ArticleService, ProfileService, "$state", NotificationService, "$stateParams") |
16 | export class BasicEditorComponent { | 16 | export class BasicEditorComponent { |
17 | 17 | ||
18 | article: noosfero.Article = <noosfero.Article>{}; | 18 | article: noosfero.Article = <noosfero.Article>{}; |
19 | + parentId: number; | ||
20 | + | ||
21 | + editorOptions = {}; | ||
19 | 22 | ||
20 | constructor(private articleService: ArticleService, | 23 | constructor(private articleService: ArticleService, |
21 | private profileService: ProfileService, | 24 | private profileService: ProfileService, |
22 | private $state: ng.ui.IStateService, | 25 | private $state: ng.ui.IStateService, |
23 | - private notification: NotificationService) { } | 26 | + private notification: NotificationService, |
27 | + private $stateParams: ng.ui.IStateParamsService) { | ||
28 | + this.parentId = this.$stateParams['parent_id']; | ||
29 | + } | ||
24 | 30 | ||
25 | save() { | 31 | save() { |
26 | this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { | 32 | this.profileService.getCurrentProfile().then((profile: noosfero.Profile) => { |
27 | - return this.articleService.createInProfile(profile, this.article); | 33 | + return this.articleService.createInParent(this.parentId, this.article); |
28 | }).then((response: noosfero.RestResult<noosfero.Article>) => { | 34 | }).then((response: noosfero.RestResult<noosfero.Article>) => { |
29 | let article = (<noosfero.Article>response.data); | 35 | let article = (<noosfero.Article>response.data); |
30 | this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier }); | 36 | this.$state.transitionTo('main.profile.page', { page: article.path, profile: article.profile.identifier }); |
src/app/article/content-viewer/content-viewer-actions.component.spec.ts
@@ -20,6 +20,9 @@ describe('Content Viewer Actions Component', () => { | @@ -20,6 +20,9 @@ describe('Content Viewer Actions Component', () => { | ||
20 | return <any>[ | 20 | return <any>[ |
21 | provide('ProfileService', { | 21 | provide('ProfileService', { |
22 | useValue: helpers.mocks.profileService | 22 | useValue: helpers.mocks.profileService |
23 | + }), | ||
24 | + provide('ArticleService', { | ||
25 | + useValue: helpers.mocks.articleService | ||
23 | }) | 26 | }) |
24 | ]; | 27 | ]; |
25 | }); | 28 | }); |
@@ -44,6 +47,33 @@ describe('Content Viewer Actions Component', () => { | @@ -44,6 +47,33 @@ describe('Content Viewer Actions Component', () => { | ||
44 | }); | 47 | }); |
45 | }); | 48 | }); |
46 | 49 | ||
50 | + it('return article parent as container when it is not a folder', (done: Function) => { | ||
51 | + buildComponent().then((fixture: ComponentFixture) => { | ||
52 | + let component = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
2 |
|
||
53 | + let article = <noosfero.Article>({ id: 1, type: 'TextArticle', parent: { id: 2 } }); | ||
54 | + expect(component.getArticleContainer(article)).toEqual(2); | ||
55 | + done(); | ||
56 | + }); | ||
57 | + }); | ||
58 | + | ||
59 | + it('return article as container when it is a folder', (done: Function) => { | ||
60 | + buildComponent().then((fixture: ComponentFixture) => { | ||
61 | + let component = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
62 | + let article = <noosfero.Article>({ id: 1, type: 'Folder' }); | ||
63 | + expect(component.getArticleContainer(article)).toEqual(1); | ||
64 | + done(); | ||
65 | + }); | ||
66 | + }); | ||
67 | + | ||
68 | + it('return article as container when it is a blog', (done: Function) => { | ||
69 | + buildComponent().then((fixture: ComponentFixture) => { | ||
70 | + let component = fixture.debugElement.componentViewChildren[0].componentInstance; | ||
71 | + let article = <noosfero.Article>({ id: 1, type: 'Blog' }); | ||
72 | + expect(component.getArticleContainer(article)).toEqual(1); | ||
73 | + done(); | ||
74 | + }); | ||
75 | + }); | ||
76 | + | ||
47 | it('check if profile was loaded', (done: Function) => { | 77 | it('check if profile was loaded', (done: Function) => { |
48 | let profile: any = { | 78 | let profile: any = { |
49 | id: 1, | 79 | id: 1, |
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 }), | ||
3 |
|
||
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.profile.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}} | 4 | <i class="fa fa-file fa-fw fa-lg"></i> {{"navbar.content_viewer_actions.new_post" | translate}} |
5 | </a> | 5 | </a> |
6 | </li> | 6 | </li> |
src/app/profile/profile.component.ts
@@ -46,7 +46,7 @@ import {MyProfileComponent} from "./myprofile.component"; | @@ -46,7 +46,7 @@ import {MyProfileComponent} from "./myprofile.component"; | ||
46 | }, | 46 | }, |
47 | { | 47 | { |
48 | name: 'main.profile.cms', | 48 | name: 'main.profile.cms', |
49 | - url: "^/myprofile/:profile/cms", | 49 | + url: "^/myprofile/:profile/cms?parent_id", |
50 | component: BasicEditorComponent, | 50 | component: BasicEditorComponent, |
51 | views: { | 51 | views: { |
52 | "mainBlockContent": { | 52 | "mainBlockContent": { |
src/lib/ng-noosfero-api/http/article.service.ts
@@ -32,6 +32,14 @@ export class ArticleService extends RestangularService<noosfero.Article> { | @@ -32,6 +32,14 @@ export class ArticleService extends RestangularService<noosfero.Article> { | ||
32 | return this.create(article, <noosfero.RestModel>profileElement, null, headers); | 32 | return this.create(article, <noosfero.RestModel>profileElement, null, headers); |
33 | } | 33 | } |
34 | 34 | ||
35 | + createInParent(parentId: number, article: noosfero.Article): ng.IPromise<noosfero.RestResult<noosfero.Article>> { | ||
36 | + let headers = { | ||
37 | + 'Content-Type': 'application/json' | ||
38 | + }; | ||
39 | + | ||
40 | + let parent = this.getElement(parentId); | ||
41 | + return this.create(article, parent, null, headers, true, "children"); | ||
42 | + } | ||
35 | 43 | ||
36 | getAsCollectionChildrenOf<C>(rootElement: noosfero.Environment | noosfero.Article | noosfero.Profile, path: string, queryParams?: any, headers?: any): restangular.ICollectionPromise<C> { | 44 | getAsCollectionChildrenOf<C>(rootElement: noosfero.Environment | noosfero.Article | noosfero.Profile, path: string, queryParams?: any, headers?: any): restangular.ICollectionPromise<C> { |
37 | return rootElement.getList<C>(path, queryParams, headers); | 45 | return rootElement.getList<C>(path, queryParams, headers); |
src/lib/ng-noosfero-api/http/restangular_service.ts
@@ -236,7 +236,7 @@ export abstract class RestangularService<T extends noosfero.RestModel> { | @@ -236,7 +236,7 @@ export abstract class RestangularService<T extends noosfero.RestModel> { | ||
236 | * Creates a new Resource into the resource collection | 236 | * Creates a new Resource into the resource collection |
237 | * calls POST /resourcePath | 237 | * calls POST /resourcePath |
238 | */ | 238 | */ |
239 | - public create(obj: T, rootElement?: noosfero.RestModel, queryParams?: any, headers?: any, isSub: boolean = true): ng.IPromise<noosfero.RestResult<T>> { | 239 | + public create(obj: T, rootElement?: noosfero.RestModel, queryParams?: any, headers?: any, isSub: boolean = true, path?: string): ng.IPromise<noosfero.RestResult<T>> { |
240 | let deferred = this.$q.defer<noosfero.RestResult<T>>(); | 240 | let deferred = this.$q.defer<noosfero.RestResult<T>>(); |
241 | 241 | ||
242 | let restRequest: ng.IPromise<noosfero.RestResult<T>>; | 242 | let restRequest: ng.IPromise<noosfero.RestResult<T>>; |
@@ -248,8 +248,9 @@ export abstract class RestangularService<T extends noosfero.RestModel> { | @@ -248,8 +248,9 @@ export abstract class RestangularService<T extends noosfero.RestModel> { | ||
248 | data = obj; | 248 | data = obj; |
249 | } | 249 | } |
250 | 250 | ||
251 | + let subpath = path || this.getResourcePath(); | ||
251 | if (rootElement) { | 252 | if (rootElement) { |
252 | - restRequest = rootElement.all(this.getResourcePath()).post(data, queryParams, headers); | 253 | + restRequest = rootElement.all(subpath).post(data, queryParams, headers); |
253 | } else { | 254 | } else { |
254 | restRequest = this.baseResource.post(data, queryParams, headers); | 255 | restRequest = this.baseResource.post(data, queryParams, headers); |
255 | } | 256 | } |
src/lib/ng-noosfero-api/interfaces/article.ts
1 | 1 | ||
2 | namespace noosfero { | 2 | namespace noosfero { |
3 | - export interface Article extends RestModel { | 3 | + export interface Article extends RestModel { |
4 | path: string; | 4 | path: string; |
5 | profile: Profile; | 5 | profile: Profile; |
6 | type: string; | 6 | type: string; |
7 | + parent: Article; | ||
7 | } | 8 | } |
8 | -} | ||
9 | \ No newline at end of file | 9 | \ No newline at end of file |
10 | +} |
src/spec/mocks.ts
1 | const DEBUG = false; | 1 | const DEBUG = false; |
2 | 2 | ||
3 | -let log = (message: string, ...args: any[]) => { | 3 | +let log = (message: string, ...args: any[]) => { |
4 | if (DEBUG) { | 4 | if (DEBUG) { |
5 | console.log(message); | 5 | console.log(message); |
6 | } | 6 | } |
@@ -70,7 +70,9 @@ export var mocks = { | @@ -70,7 +70,9 @@ export var mocks = { | ||
70 | return { | 70 | return { |
71 | then: (func?: Function) => { if (func) func(); } | 71 | then: (func?: Function) => { if (func) func(); } |
72 | }; | 72 | }; |
73 | - } | 73 | + }, |
74 | + setCurrent: (article: noosfero.Article) => { }, | ||
75 | + getCurrent: () => { return Promise.resolve({}); } | ||
74 | }, | 76 | }, |
75 | environmentService: { | 77 | environmentService: { |
76 | getEnvironmentPeople: (params: any) => { | 78 | getEnvironmentPeople: (params: any) => { |