Compare View

switch
from
...
to
 
Commits (8)
@@ -35,9 +35,10 @@ @@ -35,9 +35,10 @@
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",  
40 - "ng-ckeditor": "^0.2.1" 38 + "angular-bind-html-compile": "^1.2.1",
  39 + "angular-click-outside": "^2.7.1",
  40 + "ng-ckeditor": "^0.2.1",
  41 + "angular-bootstrap-toggle-switch": "^0.5.6"
41 }, 42 },
42 "devDependencies": { 43 "devDependencies": {
43 "angular-mocks": "~1.5.0" 44 "angular-mocks": "~1.5.0"
src/app/admin/layout-edit/designMode.service.spec.ts 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +import {DesignModeService} from './designMode.service';
  2 +
  3 +describe('DesignMode Service', () => {
  4 + let service: DesignModeService;
  5 +
  6 + beforeEach(() => {
  7 + service = new DesignModeService();
  8 + });
  9 +
  10 + it('has the designModeOn equals false as default', () => {
  11 + expect(service.isInDesignMode()).toBeFalsy();
  12 + });
  13 +
  14 + it('allows set the designMode value', () => {
  15 + spyOn(service.onToggle, 'next').and.stub();
  16 + service.setInDesignMode(true);
  17 + expect(service.isInDesignMode).toBeTruthy();
  18 + });
  19 +
  20 + it('emits the onToggle event when changing the designModeOn property', () => {
  21 + spyOn(service.onToggle, 'next').and.stub();
  22 + service.setInDesignMode(true);
  23 + expect(service.onToggle.next).toHaveBeenCalled();
  24 + });
  25 +
  26 + it('does not emit onToggle event when there is no change on designModeOn property', () => {
  27 + spyOn(service.onToggle, 'next').and.stub();
  28 + service.setInDesignMode(false);
  29 + expect(service.onToggle.next).not.toHaveBeenCalled();
  30 + });
  31 +});
0 \ No newline at end of file 32 \ No newline at end of file
src/app/admin/layout-edit/designMode.service.ts 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +import {Component, Injectable, Output, EventEmitter} from 'ng-forward';
  2 +import {BodyStateClassesService} from '../../layout/services/body-state-classes.service';
  3 +
  4 +@Injectable()
  5 +export class DesignModeService {
  6 + @Output() onToggle: EventEmitter<boolean> = new EventEmitter<boolean>();
  7 +
  8 + private designModeOn: boolean = false;
  9 +
  10 + isInDesignMode(): boolean {
  11 + return this.designModeOn;
  12 + }
  13 +
  14 + setInDesignMode(value: boolean) {
  15 + if (this.designModeOn !== value) {
  16 + this.designModeOn = value;
  17 + this.onToggle.next(this.designModeOn);
  18 + }
  19 + }
  20 +
  21 + constructor() {
  22 + }
  23 +}
0 \ No newline at end of file 24 \ No newline at end of file
src/app/admin/layout-edit/designModeToggler.component.spec.ts 0 → 100644
@@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
  1 +import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper';
  2 +import * as helpers from '../../../spec/helpers';
  3 +import {DesignModeTogglerComponent} from './designModeToggler.component';
  4 +import {DesignModeService} from './designMode.service';
  5 +
  6 +describe('DesignModeToggler Component', () => {
  7 + const htmlTemplate: string = '<noosfero-design-toggler></noosfero-design-toggler>';
  8 +
  9 + let helper: ComponentTestHelper<DesignModeTogglerComponent>;
  10 + beforeEach(() => {
  11 + angular.mock.module('templates');
  12 + angular.mock.module('ngSanitize');
  13 + angular.mock.module('toggle-switch');
  14 + });
  15 +
  16 + let designModeService: DesignModeService;
  17 + beforeEach((done) => {
  18 + designModeService = new DesignModeService();
  19 + let cls = createClass({
  20 + template: htmlTemplate,
  21 + directives: [DesignModeTogglerComponent],
  22 + providers: [
  23 + helpers.createProviderToValue('DesignModeService', designModeService)
  24 + ]
  25 + });
  26 + helper = new ComponentTestHelper<DesignModeTogglerComponent>(cls, done);
  27 + });
  28 +
  29 + it('changes css classes representing the switch is on or off', () => {
  30 + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-off')).toBeTruthy();
  31 + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-on')).toBeFalsy();
  32 + helper.component.inDesignMode = true;
  33 + helper.detectChanges();
  34 + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-on')).toBeTruthy();
  35 + expect(helper.debugElement.query('div.switch-animate').hasClass('switch-off')).toBeFalsy();
  36 + });
  37 +
  38 + it('emits event with value "true" when changing inDesignMode to On', (done) => {
  39 + designModeService.onToggle.subscribe((designModeOn: boolean) => {
  40 + expect(designModeOn).toBeTruthy();
  41 + done();
  42 + });
  43 + helper.component.inDesignMode = true;
  44 + helper.detectChanges();
  45 + });
  46 +
  47 + it('emits events with value "false" when changing inDesignMode to Off', (done) => {
  48 + helper.component.inDesignMode = true;
  49 + helper.detectChanges();
  50 +
  51 + designModeService.onToggle.subscribe((designModeOn: boolean) => {
  52 + expect(designModeOn).toBeFalsy();
  53 + done();
  54 + });
  55 +
  56 + helper.component.inDesignMode = false;
  57 + helper.detectChanges();
  58 + });
  59 +});
0 \ No newline at end of file 60 \ No newline at end of file
src/app/admin/layout-edit/designModeToggler.component.ts 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +import {Component, Inject} from 'ng-forward';
  2 +import {DesignModeService} from './designMode.service';
  3 +@Component({
  4 + selector: 'noosfero-design-toggler',
  5 + templateUrl: 'app/admin/layout-edit/designModeToggler.html'
  6 +})
  7 +@Inject(DesignModeService)
  8 +export class DesignModeTogglerComponent {
  9 +
  10 + icon: string = "&nbsp;<i class='glyphicon glyphicon-wrench'></i>&nbsp;";
  11 +
  12 + constructor(private designModeService: DesignModeService) {
  13 + }
  14 +
  15 + private _inDesignMode: boolean = false;
  16 +
  17 + get inDesignMode(): boolean {
  18 + return this.designModeService.isInDesignMode();
  19 + };
  20 +
  21 + set inDesignMode(value: boolean) {
  22 + this.designModeService.setInDesignMode(value);
  23 + };
  24 +}
0 \ No newline at end of file 25 \ No newline at end of file
src/app/admin/layout-edit/designModeToggler.html 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +<toggle-switch
  2 + html="true"
  3 + ng-model="ctrl.inDesignMode"
  4 + on-label="{{'designMode.toggle.ON' | translate}}"
  5 + off-label="{{'designMode.toggle.OFF' | translate}}"
  6 + class="switch-small"
  7 + knob-label="{{ ctrl.icon + ('designMode.label' | translate) }}">
  8 +</toggle-switch>
0 \ No newline at end of file 9 \ No newline at end of file
src/app/article/content-viewer/content-viewer.component.ts
@@ -14,7 +14,7 @@ import {ProfileService} from &quot;../../../lib/ng-noosfero-api/http/profile.service&quot; @@ -14,7 +14,7 @@ import {ProfileService} from &quot;../../../lib/ng-noosfero-api/http/profile.service&quot;
14 provide('profileService', { useClass: ProfileService }) 14 provide('profileService', { useClass: ProfileService })
15 ] 15 ]
16 }) 16 })
17 -@Inject(ArticleService, ProfileService, "$log", "$stateParams") 17 +@Inject(ArticleService, ProfileService, "$stateParams")
18 export class ContentViewerComponent { 18 export class ContentViewerComponent {
19 19
20 @Input() 20 @Input()
@@ -23,7 +23,10 @@ export class ContentViewerComponent { @@ -23,7 +23,10 @@ export class ContentViewerComponent {
23 @Input() 23 @Input()
24 profile: noosfero.Profile = null; 24 profile: noosfero.Profile = null;
25 25
26 - constructor(private articleService: ArticleService, private profileService: ProfileService, private $log: ng.ILogService, private $stateParams: angular.ui.IStateParamsService) { 26 + constructor(
  27 + private articleService: ArticleService,
  28 + private profileService: ProfileService,
  29 + private $stateParams: angular.ui.IStateParamsService) {
27 this.activate(); 30 this.activate();
28 } 31 }
29 32
src/app/layout/navbar/navbar.html
@@ -26,19 +26,20 @@ @@ -26,19 +26,20 @@
26 <span ng-bind="ctrl.currentUser.person.name"></span> <b class="caret"></b> 26 <span ng-bind="ctrl.currentUser.person.name"></span> <b class="caret"></b>
27 </a> 27 </a>
28 <ul class="dropdown-menu" uib-dropdown-menu> 28 <ul class="dropdown-menu" uib-dropdown-menu>
29 - <li>  
30 - <a ui-sref="main.profile.info({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-user"></i> {{"navbar.profile" | translate}}</a>  
31 - </li>  
32 - <li>  
33 - <a target="_self" ui-sref="main.profile.settings({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-gear"></i> {{"navbar.settings" | translate}}</a>  
34 - </li>  
35 - <li class="divider"></li>  
36 - <li>  
37 - <a href="#" ng-click="ctrl.logout()"><i class="fa fa-fw fa-power-off"></i> {{"navbar.logout" | translate}}</a>  
38 - </li> 29 + <li>
  30 + <a ui-sref="main.profile.info({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-user"></i> {{"navbar.profile" | translate}}</a>
  31 + </li>
  32 + <li>
  33 + <a target="_self" ui-sref="main.profile.settings({profile: ctrl.currentUser.person.identifier})"><i class="fa fa-fw fa-gear"></i> {{"navbar.settings" | translate}}</a>
  34 + </li>
  35 + <li class="divider"></li>
  36 + <li>
  37 + <a href="#" ng-click="ctrl.logout()"><i class="fa fa-fw fa-power-off"></i> {{"navbar.logout" | translate}}</a>
  38 + </li>
39 </ul> 39 </ul>
40 </li> 40 </li>
41 </ul> 41 </ul>
  42 +
42 <ul class="nav navbar-nav navbar-right"> 43 <ul class="nav navbar-nav navbar-right">
43 <language-selector class="nav navbar-nav navbar-right"></language-selector> 44 <language-selector class="nav navbar-nav navbar-right"></language-selector>
44 </ul> 45 </ul>
@@ -49,3 +50,5 @@ @@ -49,3 +50,5 @@
49 </div> 50 </div>
50 </div> 51 </div>
51 </nav> 52 </nav>
  53 +<div ui-view="toolbar">
  54 +</div>
52 \ No newline at end of file 55 \ No newline at end of file
src/app/layout/navbar/navbar.ts
@@ -4,11 +4,13 @@ import {SessionService, AuthService, AuthController, AuthEvents} from &quot;./../../l @@ -4,11 +4,13 @@ import {SessionService, AuthService, AuthController, AuthEvents} from &quot;./../../l
4 import {EnvironmentService} from "./../../../lib/ng-noosfero-api/http/environment.service"; 4 import {EnvironmentService} from "./../../../lib/ng-noosfero-api/http/environment.service";
5 import {SidebarNotificationService} from "../sidebar/sidebar.notification.service"; 5 import {SidebarNotificationService} from "../sidebar/sidebar.notification.service";
6 import {BodyStateClassesService} from '../services/body-state-classes.service'; 6 import {BodyStateClassesService} from '../services/body-state-classes.service';
  7 +import {DesignModeTogglerComponent} from './../../admin/layout-edit/designModeToggler.component';
  8 +import {BootstrapSwitcherComponent, BootstrapSwitcherItem} from './../../shared/components/bootstrap-switcher/bootstrap-switcher.component';
7 9
8 @Component({ 10 @Component({
9 selector: "acme-navbar", 11 selector: "acme-navbar",
10 templateUrl: "app/layout/navbar/navbar.html", 12 templateUrl: "app/layout/navbar/navbar.html",
11 - directives: [LanguageSelectorComponent], 13 + directives: [LanguageSelectorComponent, DesignModeTogglerComponent, BootstrapSwitcherComponent],
12 providers: [AuthService, SessionService, SidebarNotificationService, EnvironmentService] 14 providers: [AuthService, SessionService, SidebarNotificationService, EnvironmentService]
13 }) 15 })
14 @Inject("$uibModal", AuthService, "SessionService", "$state", SidebarNotificationService, BodyStateClassesService, EnvironmentService) 16 @Inject("$uibModal", AuthService, "SessionService", "$state", SidebarNotificationService, BodyStateClassesService, EnvironmentService)
@@ -18,7 +20,6 @@ export class Navbar { @@ -18,7 +20,6 @@ export class Navbar {
18 private modalInstance: any = null; 20 private modalInstance: any = null;
19 public showHamburger: boolean = false; 21 public showHamburger: boolean = false;
20 public currentEnvironment: noosfero.Environment = <any>{ name: '' }; 22 public currentEnvironment: noosfero.Environment = <any>{ name: '' };
21 -  
22 /** 23 /**
23 * 24 *
24 */ 25 */
src/app/layout/scss/_layout.scss
@@ -35,3 +35,24 @@ @@ -35,3 +35,24 @@
35 padding: 0 20px 20px 20px; 35 padding: 0 20px 20px 20px;
36 } 36 }
37 } 37 }
  38 +
  39 +
  40 +body.noosfero-design-on {
  41 +
  42 + div.content-wrapper {
  43 + opacity: 0.5;
  44 + }
  45 +}
  46 +
  47 +ul.nav > li.toggler-container {
  48 + position: relative;
  49 + padding-top: 12px;
  50 +}
  51 +
  52 +.noosfero-main-toolbar {
  53 + padding: 5px;
  54 + @include make-row();
  55 + margin-left: 0px;
  56 + margin-right: 0px;
  57 + background-color: #edecec;
  58 +}
38 \ No newline at end of file 59 \ No newline at end of file
src/app/layout/scss/skins/_whbl.scss
@@ -281,6 +281,31 @@ $whbl-font-color: #16191c; @@ -281,6 +281,31 @@ $whbl-font-color: #16191c;
281 .pace .pace-progress { 281 .pace .pace-progress {
282 background-color: #fff; 282 background-color: #fff;
283 } 283 }
  284 +
  285 + noosfero-design-toggler .ats-switch .knob i {
  286 + color: #999999;
  287 + }
  288 +
  289 + .ats-switch .knob {
  290 + padding-right: 15px;
  291 + }
  292 +
  293 + .ats-switch .glyphicon {
  294 + top: 2px;
  295 + }
  296 +
  297 + .ats-switch .switch-left {
  298 + background-color: #41b941;
  299 + font-weight: bold;
  300 + color: white;
  301 + }
  302 +
  303 + .ats-switch .switch-right {
  304 + background-color: #ce3b3b;
  305 + font-weight: bold;
  306 + color: white;
  307 + }
  308 +
284 } 309 }
285 .rtl.skin-whbl #content-wrapper { 310 .rtl.skin-whbl #content-wrapper {
286 border-left: 0; 311 border-left: 0;
src/app/layout/services/body-state-classes.service.spec.ts
@@ -4,7 +4,7 @@ import {AuthService} from &quot;./../../login/auth.service&quot;; @@ -4,7 +4,7 @@ import {AuthService} from &quot;./../../login/auth.service&quot;;
4 import {AuthEvents} from "./../../login/auth-events"; 4 import {AuthEvents} from "./../../login/auth-events";
5 5
6 import {EventEmitter} from 'ng-forward'; 6 import {EventEmitter} from 'ng-forward';
7 - 7 +import {DesignModeService} from './../../admin/layout-edit/designMode.service';
8 8
9 describe("BodyStateClasses Service", () => { 9 describe("BodyStateClasses Service", () => {
10 10
@@ -19,10 +19,13 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; { @@ -19,10 +19,13 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
19 }, 19 },
20 authService: any = helpers.mocks.authService, 20 authService: any = helpers.mocks.authService,
21 bodyEl: { className: string }, 21 bodyEl: { className: string },
22 - bodyElJq: any; 22 + bodyElJq: any,
  23 + designModeService = new DesignModeService();
  24 +
  25 +
23 26
24 let getService = (): BodyStateClassesService => { 27 let getService = (): BodyStateClassesService => {
25 - return new BodyStateClassesService($rootScope, $document, $state, authService); 28 + return new BodyStateClassesService($rootScope, $document, $state, authService, designModeService);
26 }; 29 };
27 30
28 beforeEach(() => { 31 beforeEach(() => {
@@ -168,4 +171,30 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; { @@ -168,4 +171,30 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
168 171
169 expect(contentWrapperMock.removeClass).toHaveBeenCalledWith(BodyStateClassesService.CONTENT_WRAPPER_FULL); 172 expect(contentWrapperMock.removeClass).toHaveBeenCalledWith(BodyStateClassesService.CONTENT_WRAPPER_FULL);
170 }); 173 });
  174 +
  175 + it("should add the class noosfero-design-on when designMode is changed to true", () => {
  176 + let fnOnToggle: Function = null;
  177 + designModeService.onToggle = <any> {
  178 + subscribe: (fn: Function) => {
  179 + fnOnToggle = fn;
  180 + },
  181 + next: (value: boolean) => {
  182 + fnOnToggle.apply(designModeService, [value]);
  183 + }
  184 + };
  185 +
  186 + let service = getService();
  187 +
  188 + bodyElJq.addClass = jasmine.createSpy("addClass");
  189 + bodyElJq.removeClass = jasmine.createSpy("removeClass");
  190 + service["bodyElement"] = bodyElJq;
  191 +
  192 + service.start();
  193 +
  194 + debugger;
  195 + designModeService.setInDesignMode(true);
  196 +
  197 +
  198 + expect(bodyElJq.addClass).toHaveBeenCalledWith(BodyStateClassesService.DESIGN_MODE_ON_CLASSNAME);
  199 + });
171 }); 200 });
src/app/layout/services/body-state-classes.service.ts
@@ -3,6 +3,7 @@ import {AuthEvents} from &quot;../../login/auth-events&quot;; @@ -3,6 +3,7 @@ import {AuthEvents} from &quot;../../login/auth-events&quot;;
3 import {AuthService} from "./../../login/auth.service"; 3 import {AuthService} from "./../../login/auth.service";
4 import {HtmlUtils} from "../html-utils"; 4 import {HtmlUtils} from "../html-utils";
5 import {INgForwardJQuery} from 'ng-forward/cjs/util/jqlite-extensions'; 5 import {INgForwardJQuery} from 'ng-forward/cjs/util/jqlite-extensions';
  6 +import {DesignModeService} from './../../admin/layout-edit/designMode.service';
6 7
7 export interface StartParams { 8 export interface StartParams {
8 skin?: string; 9 skin?: string;
@@ -22,12 +23,13 @@ export interface StartParams { @@ -22,12 +23,13 @@ export interface StartParams {
22 * - full-content 23 * - full-content
23 */ 24 */
24 @Injectable() 25 @Injectable()
25 -@Inject("$rootScope", "$document", "$state", AuthService) 26 +@Inject("$rootScope", "$document", "$state", AuthService, DesignModeService)
26 export class BodyStateClassesService { 27 export class BodyStateClassesService {
27 28
28 private started: boolean = false; 29 private started: boolean = false;
29 private skin: string; 30 private skin: string;
30 31
  32 + public static get DESIGN_MODE_ON_CLASSNAME(): string { return "noosfero-design-on"; }
31 public static get USER_LOGGED_CLASSNAME(): string { return "noosfero-user-logged"; } 33 public static get USER_LOGGED_CLASSNAME(): string { return "noosfero-user-logged"; }
32 public static get ROUTE_STATE_CLASSNAME_PREFIX(): string { return "noosfero-route-"; } 34 public static get ROUTE_STATE_CLASSNAME_PREFIX(): string { return "noosfero-route-"; }
33 public static get CONTENT_WRAPPER_FULL(): string { return "full-content"; } 35 public static get CONTENT_WRAPPER_FULL(): string { return "full-content"; }
@@ -38,16 +40,16 @@ export class BodyStateClassesService { @@ -38,16 +40,16 @@ export class BodyStateClassesService {
38 private $rootScope: ng.IRootScopeService, 40 private $rootScope: ng.IRootScopeService,
39 private $document: ng.IDocumentService, 41 private $document: ng.IDocumentService,
40 private $state: ng.ui.IStateService, 42 private $state: ng.ui.IStateService,
41 - private authService: AuthService 43 + private authService: AuthService,
  44 + private designModeService: DesignModeService
42 ) { 45 ) {
43 -  
44 } 46 }
45 47
46 start(config?: StartParams) { 48 start(config?: StartParams) {
47 if (!this.started) { 49 if (!this.started) {
48 this.setupUserLoggedClassToggle(); 50 this.setupUserLoggedClassToggle();
49 this.setupStateClassToggle(); 51 this.setupStateClassToggle();
50 - 52 + this.setupDesignModeClassToggle();
51 if (config) { 53 if (config) {
52 this.setThemeSkin(config.skin); 54 this.setThemeSkin(config.skin);
53 } 55 }
@@ -87,9 +89,19 @@ export class BodyStateClassesService { @@ -87,9 +89,19 @@ export class BodyStateClassesService {
87 } 89 }
88 90
89 /** 91 /**
90 - * Setup the initial class name on body element indicating the current route  
91 - * and adds event handler to swith this class when the current page/state changes 92 + * setup the listeners to the desigModeService to add class on the Body Element
  93 + * indicating the user activated the designMode
92 */ 94 */
  95 + private setupDesignModeClassToggle() {
  96 + this.designModeService.onToggle.subscribe((designOn: boolean) => {
  97 + if (designOn) {
  98 + this.getBodyElement().addClass(BodyStateClassesService.DESIGN_MODE_ON_CLASSNAME);
  99 + } else {
  100 + this.getBodyElement().removeClass(BodyStateClassesService.DESIGN_MODE_ON_CLASSNAME);
  101 + }
  102 + });
  103 + }
  104 +
93 private setupStateClassToggle() { 105 private setupStateClassToggle() {
94 let bodyElement = this.getBodyElement(); 106 let bodyElement = this.getBodyElement();
95 bodyElement.addClass(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + this.$state.current.name); 107 bodyElement.addClass(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + this.$state.current.name);
src/app/main/main.component.ts
@@ -111,7 +111,7 @@ export class EnvironmentContent { @@ -111,7 +111,7 @@ export class EnvironmentContent {
111 "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid", 111 "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid",
112 "angular-timeline", "duScroll", "oitozero.ngSweetAlert", 112 "angular-timeline", "duScroll", "oitozero.ngSweetAlert",
113 "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad", 113 "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad",
114 - "angular-click-outside", "noosfero.init"] 114 + "angular-click-outside", "toggle-switch", "noosfero.init"]
115 }) 115 })
116 @StateConfig([ 116 @StateConfig([
117 { 117 {
src/app/profile/profile-toolbar.component.ts 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +import {Component, Inject, provide} from "ng-forward";
  2 +import {ProfileService} from "../../lib/ng-noosfero-api/http/profile.service";
  3 +
  4 +@Component({
  5 + selector: "profile-toolbar",
  6 + templateUrl: "app/profile/toolbar.html",
  7 + providers: [
  8 + provide('profileService', { useClass: ProfileService })
  9 + ]
  10 +})
  11 +@Inject(ProfileService)
  12 +export class ProfileToolbarComponent {
  13 + profile: noosfero.Profile;
  14 + parentId: number;
  15 +
  16 + constructor(profileService: ProfileService) {
  17 + profileService.getCurrentProfile().then((profile: noosfero.Profile) => {
  18 + this.profile = profile;
  19 + });
  20 + }
  21 +}
src/app/profile/profile.component.ts
@@ -10,7 +10,7 @@ import {ProfileService} from &quot;../../lib/ng-noosfero-api/http/profile.service&quot;; @@ -10,7 +10,7 @@ import {ProfileService} from &quot;../../lib/ng-noosfero-api/http/profile.service&quot;;
10 import {NotificationService} from "../shared/services/notification.service"; 10 import {NotificationService} from "../shared/services/notification.service";
11 import {MyProfileComponent} from "./myprofile.component"; 11 import {MyProfileComponent} from "./myprofile.component";
12 import {ProfileActionsComponent} from "./profile-actions.component"; 12 import {ProfileActionsComponent} from "./profile-actions.component";
13 - 13 +import {ProfileToolbarComponent} from "./profile-toolbar.component";
14 /** 14 /**
15 * @ngdoc controller 15 * @ngdoc controller
16 * @name profile.Profile 16 * @name profile.Profile
@@ -42,6 +42,11 @@ import {ProfileActionsComponent} from &quot;./profile-actions.component&quot;; @@ -42,6 +42,11 @@ import {ProfileActionsComponent} from &quot;./profile-actions.component&quot;;
42 templateUrl: "app/profile/navbar-actions.html", 42 templateUrl: "app/profile/navbar-actions.html",
43 controller: ProfileActionsComponent, 43 controller: ProfileActionsComponent,
44 controllerAs: "vm" 44 controllerAs: "vm"
  45 + },
  46 + "toolbar@main": {
  47 + templateUrl: "app/profile/toolbar.html",
  48 + controller: ProfileToolbarComponent,
  49 + controllerAs: "vm"
45 } 50 }
46 } 51 }
47 }, 52 },
@@ -54,6 +59,11 @@ import {ProfileActionsComponent} from &quot;./profile-actions.component&quot;; @@ -54,6 +59,11 @@ import {ProfileActionsComponent} from &quot;./profile-actions.component&quot;;
54 templateUrl: "app/profile/navbar-actions.html", 59 templateUrl: "app/profile/navbar-actions.html",
55 controller: ProfileActionsComponent, 60 controller: ProfileActionsComponent,
56 controllerAs: "vm" 61 controllerAs: "vm"
  62 + },
  63 + "toolbar@main": {
  64 + templateUrl: "app/profile/toolbar.html",
  65 + controller: ProfileToolbarComponent,
  66 + controllerAs: "vm"
57 } 67 }
58 } 68 }
59 }, 69 },
@@ -106,6 +116,11 @@ import {ProfileActionsComponent} from &quot;./profile-actions.component&quot;; @@ -106,6 +116,11 @@ import {ProfileActionsComponent} from &quot;./profile-actions.component&quot;;
106 templateUrl: "app/article/content-viewer/navbar-actions.html", 116 templateUrl: "app/article/content-viewer/navbar-actions.html",
107 controller: ContentViewerActionsComponent, 117 controller: ContentViewerActionsComponent,
108 controllerAs: "vm" 118 controllerAs: "vm"
  119 + },
  120 + "toolbar@main": {
  121 + templateUrl: "app/profile/toolbar.html",
  122 + controller: ProfileToolbarComponent,
  123 + controllerAs: "vm"
109 } 124 }
110 } 125 }
111 } 126 }
src/app/profile/toolbar.html 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +<div class="noosfero-main-toolbar" permission="vm.profile.permissions" permission-action="allow_edit">
  2 + <noosfero-design-toggler class="pull-right"></noosfero-design-toggler>
  3 +
  4 +</div>
0 \ No newline at end of file 5 \ No newline at end of file
src/app/shared/components/bootstrap-switcher/bootstrap-switcher.component.ts 0 → 100644
@@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
  1 +import {Component, Input, Output, EventEmitter} from 'ng-forward';
  2 +
  3 +
  4 +export interface BootstrapSwitcherItem {
  5 + value: any;
  6 + label: string;
  7 +}
  8 +@Component({
  9 + selector: 'noosfero-bootstrap-switcher',
  10 + template: `
  11 + <span class="switcher-label" ng-bind="ctrl.label | translate"></span>
  12 + <div class="btn-group switcher">
  13 + <button ng-repeat="option in ctrl.options track by $index"
  14 + (click)="ctrl.switcherClick(option)"
  15 + ng-class="ctrl.getCssClassForItem(option)"
  16 + class="btn btn-xs" ng-bind="option.label | translate">
  17 + </button>
  18 + </div>
  19 + `,
  20 + inputs: ['activeClass', 'defaultClass', 'label', 'options', 'defaultOption'],
  21 + outputs: ['onSwitch']
  22 +})
  23 +export class BootstrapSwitcherComponent {
  24 + @Input() activeClass: string = 'active btn-danger';
  25 + @Input() defaultClass: string = 'btn-default';
  26 + @Input() label: string;
  27 + @Input() options: BootstrapSwitcherItem[];
  28 + @Input() defaultOption: BootstrapSwitcherItem;
  29 + @Output() onSwitch: EventEmitter<BootstrapSwitcherItem> = new EventEmitter<BootstrapSwitcherItem>();
  30 +
  31 + selectedOption: BootstrapSwitcherItem = null;
  32 +
  33 + constructor() { }
  34 +
  35 + ngOnInit() {
  36 + this.selectedOption = this.defaultOption;
  37 + }
  38 +
  39 + isSelectedOption(value: BootstrapSwitcherItem): boolean {
  40 + return this.selectedOption === value;
  41 + }
  42 +
  43 + getCssClassForItem(value: BootstrapSwitcherItem): string {
  44 + return this.isSelectedOption(value) ? this.activeClass : this.defaultClass;
  45 + }
  46 +
  47 + switcherClick(value: BootstrapSwitcherItem) {
  48 + this.selectedOption = value;
  49 + this.onSwitch.next(value);
  50 + }
  51 +}
0 \ No newline at end of file 52 \ No newline at end of file
src/app/shared/components/interfaces.ts 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +
  2 +
  3 +export interface IComponentWithPermissions {
  4 + permissions: () => string[];
  5 +}
0 \ No newline at end of file 6 \ No newline at end of file
src/app/shared/components/permission/permission.directive.ts
@@ -6,7 +6,7 @@ import {Directive, Inject, Input} from &quot;ng-forward&quot;; @@ -6,7 +6,7 @@ import {Directive, Inject, Input} from &quot;ng-forward&quot;;
6 @Inject('$attrs', '$scope', '$element') 6 @Inject('$attrs', '$scope', '$element')
7 export class PermissionDirective { 7 export class PermissionDirective {
8 8
9 - constructor($attrs: ng.IAttributes, $scope: ng.IScope, $element: any) { 9 + constructor($attrs: ng.IAttributes, $scope: ng.IScope, $element: ng.IAugmentedJQuery) {
10 $scope.$watch($attrs['permission'], () => { 10 $scope.$watch($attrs['permission'], () => {
11 let permissions = $scope.$eval($attrs['permission']); 11 let permissions = $scope.$eval($attrs['permission']);
12 let permissionAction = $attrs['permissionAction']; 12 let permissionAction = $attrs['permissionAction'];
src/languages/en.json
@@ -75,6 +75,9 @@ @@ -75,6 +75,9 @@
75 "custom_content.title": "Edit content", 75 "custom_content.title": "Edit content",
76 "profile.custom_header.label": "Header", 76 "profile.custom_header.label": "Header",
77 "profile.custom_footer.label": "Footer", 77 "profile.custom_footer.label": "Footer",
  78 + "designMode.label": "In Design",
  79 + "designMode.toggle.ON": "ON",
  80 + "designMode.toggle.OFF": "OFF",
78 "search.results.summary": "{results, plural, one{result} other{# results}}", 81 "search.results.summary": "{results, plural, one{result} other{# results}}",
79 "search.results.query.label": "Search for:", 82 "search.results.query.label": "Search for:",
80 "search.results.query.placeholder": "Search" 83 "search.results.query.placeholder": "Search"
src/languages/pt.json
@@ -75,6 +75,9 @@ @@ -75,6 +75,9 @@
75 "custom_content.title": "Editar conteúdo", 75 "custom_content.title": "Editar conteúdo",
76 "profile.custom_header.label": "Cabeçalho", 76 "profile.custom_header.label": "Cabeçalho",
77 "profile.custom_footer.label": "Rodapé", 77 "profile.custom_footer.label": "Rodapé",
  78 + "designMode.label": "Modo de Edição",
  79 + "designMode.toggle.ON": "Ligado",
  80 + "designMode.toggle.OFF": "Desligado",
78 "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}", 81 "search.results.summary": "{results, plural, one{# resultado} other{# resultados}}",
79 "search.results.query.label": "Buscar:", 82 "search.results.query.label": "Buscar:",
80 "search.results.query.placeholder": "Informe aqui sua busca" 83 "search.results.query.placeholder": "Informe aqui sua busca"
src/lib/ng-noosfero-api/http/profile.service.ts
@@ -22,7 +22,7 @@ export class ProfileService { @@ -22,7 +22,7 @@ export class ProfileService {
22 this._currentProfilePromise.resolve(profile); 22 this._currentProfilePromise.resolve(profile);
23 } 23 }
24 24
25 - setCurrentProfileByIdentifier(identifier: string) { 25 + setCurrentProfileByIdentifier(identifier: string): ng.IPromise<noosfero.Profile> {
26 this.resetCurrentProfile(); 26 this.resetCurrentProfile();
27 return this.getByIdentifier(identifier).then((profile: noosfero.Profile) => { 27 return this.getByIdentifier(identifier).then((profile: noosfero.Profile) => {
28 this.setCurrentProfile(profile); 28 this.setCurrentProfile(profile);
src/lib/ng-noosfero-api/interfaces/article.ts
@@ -14,5 +14,7 @@ namespace noosfero { @@ -14,5 +14,7 @@ namespace noosfero {
14 start_date: string; 14 start_date: string;
15 end_date: string; 15 end_date: string;
16 accept_comments: boolean; 16 accept_comments: boolean;
  17 +
  18 + permissions: string[];
17 } 19 }
18 } 20 }
src/lib/ng-noosfero-api/interfaces/modelWithPermissions.ts 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +namespace noosfero {
  2 + export interface ModelWithPermissions {
  3 + permissions: string[];
  4 + }
  5 +}
0 \ No newline at end of file 6 \ No newline at end of file
src/lib/ng-noosfero-api/interfaces/profile.ts
@@ -78,5 +78,7 @@ namespace noosfero { @@ -78,5 +78,7 @@ namespace noosfero {
78 * @returns {string} The Profile footer 78 * @returns {string} The Profile footer
79 */ 79 */
80 custom_footer: string; 80 custom_footer: string;
  81 +
  82 + permissions: string[];
81 } 83 }
82 } 84 }
themes/angular-participa-consulta/app/participa-consulta.scss
@@ -77,3 +77,8 @@ $selected-color: #f6c445; @@ -77,3 +77,8 @@ $selected-color: #f6c445;
77 background-color: #7E7E7E; 77 background-color: #7E7E7E;
78 } 78 }
79 } 79 }
  80 +
  81 +.ats-switch {
  82 + border: 0px;
  83 + border-color: transparent;
  84 +}