From e3964f244d5c99b6cb467cb3ba56333f813840e8 Mon Sep 17 00:00:00 2001 From: Gid Date: Thu, 25 Jan 2018 15:45:39 +0200 Subject: [PATCH 1/2] Fixes and enhancements --- eform-client/e2e/spec.e2e-spec.ts | 1 + eform-client/gulpfile.js | 38 ++++++++++++- eform-client/package.json | 3 + eform-client/protractor.conf.js | 12 ++-- .../app/components/auth/auth.component.css | 12 +++- .../app/components/auth/auth.component.html | 41 ++++++++++---- .../src/app/components/auth/auth.component.ts | 28 ++++++++++ .../src/app/modules/cases/cases.module.ts | 4 +- .../case-edit-switch.component.html | 2 +- .../element-picture.component.html | 2 +- .../element-picture.component.ts | 5 +- .../element-signature.component.css | 7 +++ .../element-signature.component.html | 11 +++- .../element-signature.component.ts | 56 +++++++++++++++---- .../cases-table/cases-table.component.css | 5 ++ .../cases-table/cases-table.component.html | 39 ++++++++++++- .../cases-table/cases-table.component.ts | 27 ++++++++- .../src/app/modules/helpers/app.constants.ts | 3 +- .../src/app/services/accounts/auth.service.ts | 7 +++ .../src/app/services/cases/cases.service.ts | 4 ++ 20 files changed, 264 insertions(+), 43 deletions(-) create mode 100644 eform-client/src/app/modules/cases/components/case-elements/element-signature/element-signature.component.css diff --git a/eform-client/e2e/spec.e2e-spec.ts b/eform-client/e2e/spec.e2e-spec.ts index 9804dbe257..4bcce68fc0 100644 --- a/eform-client/e2e/spec.e2e-spec.ts +++ b/eform-client/e2e/spec.e2e-spec.ts @@ -24,6 +24,7 @@ describe('Header image button', () => { settingsPage.saveButton.click(); browser.refresh(); expect(browser.isElementPresent(settingsPage.headerImageMatcher)).toBeFalsy(); + browser.sleep(2000); settingsPage.SiteHeader.resetButton.click(); done(); }); diff --git a/eform-client/gulpfile.js b/eform-client/gulpfile.js index 966a2a3c23..5c7cdf595b 100644 --- a/eform-client/gulpfile.js +++ b/eform-client/gulpfile.js @@ -1,9 +1,41 @@ -const gulp = require('gulp'); const protractor = require('gulp-protractor').protractor; +const gulp = require('gulp'); +const runSequence = require('run-sequence'); +const spawn = require('child_process').spawn; + +const runSpawn = function(done, task, opt_arg, opt_io) { + opt_arg = typeof opt_arg !== 'undefined' ? opt_arg : []; + var stdio = 'inherit'; + if (opt_io === 'ignore') { + stdio = 'ignore'; + } + var child = spawn(task, opt_arg, {stdio: stdio}); + var running = false; + child.on('close', function() { + if (!running) { + running = true; + done(); + } + }); + child.on('error', function() { + if (!running) { + console.error('gulp encountered a child error'); + running = true; + done(); + } + }); +}; +gulp.task('webdriver:update', function(done) { + runSpawn(done, 'node', ['./node_modules/protractor/bin/webdriver-manager', 'update']); +}); + +gulp.task('tests', function(done) { + runSequence(['webdriver:update'], "e2e-tests" ,done); +}); -gulp.task("tests", function (done) { - gulp.src(['e2e/**/*.js'], {read: false}) +gulp.task("e2e-tests", function (done) { + gulp.src(['e2e/!**!/!*.js'], {read: false}) .pipe(protractor({ configFile: 'protractor.conf.js' }) diff --git a/eform-client/package.json b/eform-client/package.json index 720a2ef539..4d4c3d7ae1 100644 --- a/eform-client/package.json +++ b/eform-client/package.json @@ -46,6 +46,7 @@ "ngx-gallery": "^3.1.4", "pnotify": "^3.2.0", "rxjs": "^5.4.2", + "selenium-standalone-jar": "^3.0.1", "trumbowyg": "^2.5.1", "ts-helpers": "^1.1.1", "wowjs": "^1.1.3", @@ -72,6 +73,8 @@ "path": "^0.12.7", "phantomjs-prebuilt": "^2.1.16", "protractor": "~5.1.0", + "run-sequence": "^2.2.1", + "selenium-server-standalone-jar": "^3.8.1", "ts-node": "~2.0.0", "tslint": "~4.5.0", "typescript": "~2.2.0" diff --git a/eform-client/protractor.conf.js b/eform-client/protractor.conf.js index aab788f392..57a7711671 100644 --- a/eform-client/protractor.conf.js +++ b/eform-client/protractor.conf.js @@ -3,30 +3,30 @@ /*global jasmine */ var SpecReporter = require('jasmine-spec-reporter'); - exports.config = { - allScriptsTimeout: 11000, + allScriptsTimeout: 20000, specs: [ './e2e/**/*.e2e-spec.ts' ], capabilities: { 'browserName': 'chrome' }, - directConnect: true, + // directConnect: false, baseUrl: 'http://localhost:4200/', framework: 'jasmine', jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 60000, - print: function() {} + print: function () { + } }, useAllAngular2AppRoots: true, - beforeLaunch: function() { + beforeLaunch: function () { require('ts-node').register({ project: 'e2e' }); }, - onPrepare: function() { + onPrepare: function () { jasmine.getEnv().addReporter(new SpecReporter()); } }; diff --git a/eform-client/src/app/components/auth/auth.component.css b/eform-client/src/app/components/auth/auth.component.css index 99d51b8c82..35acce5160 100644 --- a/eform-client/src/app/components/auth/auth.component.css +++ b/eform-client/src/app/components/auth/auth.component.css @@ -10,7 +10,7 @@ .panel { border: 0; -} +} .user-addon { padding-left: 13px; @@ -22,6 +22,11 @@ padding-right: 12px; } +.lock-addon { + padding-left: 15px; + padding-right: 14px; +} + .p-header-wrapper { margin-top: 35px; margin-bottom: 15px; @@ -34,3 +39,8 @@ .p-description { font-weight: 500; } + +.link-block { + margin-bottom: 5px; + display: block; +} diff --git a/eform-client/src/app/components/auth/auth.component.html b/eform-client/src/app/components/auth/auth.component.html index 2e7988ee0e..7bb5af9bdb 100644 --- a/eform-client/src/app/components/auth/auth.component.html +++ b/eform-client/src/app/components/auth/auth.component.html @@ -34,17 +34,36 @@ -
-
- - -
-
- -
- - Back to login - + +
+
+ + +
+
+ +
+ + Reset admin password + + + Back to login + +
+ +
+
+ + +
+
+ +
+ + Back to login + +
+
diff --git a/eform-client/src/app/components/auth/auth.component.ts b/eform-client/src/app/components/auth/auth.component.ts index 7737608c46..44bf6082cf 100644 --- a/eform-client/src/app/components/auth/auth.component.ts +++ b/eform-client/src/app/components/auth/auth.component.ts @@ -16,13 +16,18 @@ import {LoginPageSettingsModel} from 'app/models/settings/login-page-settings.mo export class AuthComponent implements OnInit { formLogin: FormGroup; formRestore: FormGroup; + formReset: FormGroup; + username: AbstractControl; email: AbstractControl; password: AbstractControl; + secretKey: AbstractControl; + loginPageSettings: LoginPageSettingsModel = new LoginPageSettingsModel; loginImage: any; showLoginForm: boolean = true; + showAdminResetForm = false; error: string; constructor(private router: Router, @@ -56,6 +61,21 @@ export class AuthComponent implements OnInit { ); } + + submitResetAdminForm() { + debugger; + const secretKey = this.formReset.getRawValue().secretKey; + this.authService.resetAdminPassword(secretKey).subscribe((result) => { + if (result && result.success) { + this.notifyService.success({text: result.message}); + this.secretKey.reset(); + this.toggleLoginForm(true); + } else { + this.notifyService.error({text: result.message}); + } + }); + } + ngOnInit() { this.settingsService.connectionStringExist().subscribe((result) => { if (result && result.success === false) { @@ -79,9 +99,16 @@ export class AuthComponent implements OnInit { [Validators.required, Validators.email] ] }); + this.formReset = this.fb.group({ + secretKey: [ + '', + [Validators.required] + ] + }); this.username = this.formLogin.get('username'); this.password = this.formLogin.get('password'); this.email = this.formRestore.get('email'); + this.secretKey = this.formReset.get('secretKey') } getSettings() { @@ -99,5 +126,6 @@ export class AuthComponent implements OnInit { toggleLoginForm(toggle: boolean) { this.showLoginForm = toggle; + this.showAdminResetForm = false; } } diff --git a/eform-client/src/app/modules/cases/cases.module.ts b/eform-client/src/app/modules/cases/cases.module.ts index 79b31f0460..61387b2a6a 100644 --- a/eform-client/src/app/modules/cases/cases.module.ts +++ b/eform-client/src/app/modules/cases/cases.module.ts @@ -26,6 +26,7 @@ import { TrumbowygComponent } from './components/case-elements'; import {CollapseModule} from 'ngx-bootstrap'; +import {Ng2Bs3ModalModule} from 'ng2-bs3-modal/ng2-bs3-modal'; @NgModule({ imports: [ @@ -33,7 +34,8 @@ import {CollapseModule} from 'ngx-bootstrap'; NgxGalleryModule, FormsModule, CasesRoutingModule, - CollapseModule.forRoot() + CollapseModule.forRoot(), + Ng2Bs3ModalModule ], declarations: [CasesComponent, TrumbowygComponent, diff --git a/eform-client/src/app/modules/cases/components/case-edit-switch/case-edit-switch.component.html b/eform-client/src/app/modules/cases/components/case-edit-switch/case-edit-switch.component.html index f3d00bc4d0..435cb8c9c5 100644 --- a/eform-client/src/app/modules/cases/components/case-edit-switch/case-edit-switch.component.html +++ b/eform-client/src/app/modules/cases/components/case-edit-switch/case-edit-switch.component.html @@ -45,7 +45,7 @@
- +
diff --git a/eform-client/src/app/modules/cases/components/case-elements/element-picture/element-picture.component.html b/eform-client/src/app/modules/cases/components/case-elements/element-picture/element-picture.component.html index 0bdd6ef5f9..816c938d81 100644 --- a/eform-client/src/app/modules/cases/components/case-elements/element-picture/element-picture.component.html +++ b/eform-client/src/app/modules/cases/components/case-elements/element-picture/element-picture.component.html @@ -4,7 +4,7 @@
- +
@@ -122,7 +126,6 @@

.PDF

- @@ -130,3 +133,35 @@

+ + + + + +
+
+
+
{{selectedCase.id}}
+
+
+
+
{{selectedCase.createdAt | date:'dd.MM.y HH:mm:ss'}}
+
+
+
+
{{selectedCase.doneAt | date:'dd.MM.y HH:mm:ss'}}
+
+
+
+
{{selectedCase.workerName}}
+
+
+
+ + + + +
+ diff --git a/eform-client/src/app/modules/cases/components/cases-table/cases-table.component.ts b/eform-client/src/app/modules/cases/components/cases-table/cases-table.component.ts index bea26268f3..82f94f98e1 100644 --- a/eform-client/src/app/modules/cases/components/cases-table/cases-table.component.ts +++ b/eform-client/src/app/modules/cases/components/cases-table/cases-table.component.ts @@ -1,9 +1,11 @@ import {ActivatedRoute, Router} from '@angular/router'; -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, ViewChild} from '@angular/core'; import {EFormService} from 'app/services/eform/eform.service'; import {TemplateDto} from 'app/models/dto'; import {CaseModel} from 'app/models/cases'; import {CasesService} from 'app/services/cases/cases.service'; +import {ModalComponent} from 'ng2-bs3-modal/ng2-bs3-modal'; +import {NotifyService} from 'app/services/notify.service'; @Component({ selector: 'app-cases-table', @@ -11,15 +13,20 @@ import {CasesService} from 'app/services/cases/cases.service'; styleUrls: ['./cases-table.component.css'] }) export class CasesTableComponent implements OnInit { + @ViewChild('deleteCaseModal') + deleteCaseModal: ModalComponent; + id: number; caseModels: Array = []; currentTemplate: TemplateDto = new TemplateDto; + selectedCase: CaseModel = new CaseModel; spinnerStatus: boolean; constructor(private activateRoute: ActivatedRoute, private router: Router, private casesService: CasesService, - private eFormService: EFormService) { + private eFormService: EFormService, + private notifyService: NotifyService) { this.activateRoute.params.subscribe(params => { this.id = +params['id']; }); @@ -56,4 +63,20 @@ export class CasesTableComponent implements OnInit { })); } + submitCaseDelete(id: number) { + this.casesService.deleteCase(id).subscribe((data => { + if (data && data.success) { + this.loadAllCases(); + this.notifyService.success({text: data.message || 'Error'}); + } else { + this.notifyService.error({text: data.message || 'Error'}); + } + this.deleteCaseModal.close(); + })); + } + + showCaseDeleteModal(model: CaseModel) { + this.selectedCase = model; + this.deleteCaseModal.open(); + } } diff --git a/eform-client/src/app/modules/helpers/app.constants.ts b/eform-client/src/app/modules/helpers/app.constants.ts index abdd725b32..b436d96781 100644 --- a/eform-client/src/app/modules/helpers/app.constants.ts +++ b/eform-client/src/app/modules/helpers/app.constants.ts @@ -64,7 +64,8 @@ export let TemplateColumnMethods = { export let CasesMethods = { EditById: '/api/cases/edit', GetCases: '/api/cases/index', - UpdateCase: '/api/cases/update' + UpdateCase: '/api/cases/update', + DeleteCase: '/api/cases/delete' }; export let AdvSearchableEntityMethods = { diff --git a/eform-client/src/app/services/accounts/auth.service.ts b/eform-client/src/app/services/accounts/auth.service.ts index 210d12daca..9b1b56a30d 100644 --- a/eform-client/src/app/services/accounts/auth.service.ts +++ b/eform-client/src/app/services/accounts/auth.service.ts @@ -15,6 +15,7 @@ export let AuthMethods = { ChangePassword: 'api/account/change-password', RestoreUserPassword: '/api/account/reset-password', EmailRecoveryLink: '/api/account/forgot-password', + ResetAdminPassword: '/api/account/reset-admin-password' }; @Injectable() @@ -39,6 +40,12 @@ export class AuthService extends BaseService { }); } + resetAdminPassword(code: string): Observable { + return this.get(AuthMethods.ResetAdminPassword + '?code=' + code).map((result) => { + return result; + }); + } + sendEmailRecoveryLink(rawValue: any): Observable { return this.post(AuthMethods.EmailRecoveryLink, {email: rawValue.email}).map((result) => { return result; diff --git a/eform-client/src/app/services/cases/cases.service.ts b/eform-client/src/app/services/cases/cases.service.ts index faf72cc327..a6eaeda1e1 100644 --- a/eform-client/src/app/services/cases/cases.service.ts +++ b/eform-client/src/app/services/cases/cases.service.ts @@ -31,4 +31,8 @@ export class CasesService extends BaseService { return this.postModelOperationResult(CasesMethods.UpdateCase, model); } + public deleteCase = (id: number): Observable => { + return this.getWithOperationResult(CasesMethods.DeleteCase + '/' + id); + } + } From 3d6c6e1fab2b0152703de7f30557b5e3a24e1e48 Mon Sep 17 00:00:00 2001 From: Gid Date: Thu, 25 Jan 2018 15:51:34 +0200 Subject: [PATCH 2/2] Added fixes and enhancements for backend --- eFormAPI/eFormAPI/App_Start/AutofacConfig.cs | 2 + .../eFormAPI/Controllers/AccountController.cs | 65 ++++++++++++++----- .../eFormAPI/Controllers/CasesController.cs | 17 +++++ .../Controllers/TemplateFilesController.cs | 22 +++++-- eFormAPI/eFormAPI/Web.config | 2 + 5 files changed, 86 insertions(+), 22 deletions(-) diff --git a/eFormAPI/eFormAPI/App_Start/AutofacConfig.cs b/eFormAPI/eFormAPI/App_Start/AutofacConfig.cs index 2da54b6799..b4c8655724 100644 --- a/eFormAPI/eFormAPI/App_Start/AutofacConfig.cs +++ b/eFormAPI/eFormAPI/App_Start/AutofacConfig.cs @@ -3,6 +3,7 @@ using Autofac; using Autofac.Integration.WebApi; using eFormAPI.Web.Infrastructure.Data; +using eFormAPI.Web.Infrastructure.Identity; namespace eFormAPI.Web { @@ -21,6 +22,7 @@ public static void ConfigureContainer() builder.RegisterWebApiFilterProvider(config); // Set the dependency resolver to be Autofac. builder.RegisterType().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); Container = builder.Build(); } } diff --git a/eFormAPI/eFormAPI/Controllers/AccountController.cs b/eFormAPI/eFormAPI/Controllers/AccountController.cs index 74173c1af7..06f2d1acad 100644 --- a/eFormAPI/eFormAPI/Controllers/AccountController.cs +++ b/eFormAPI/eFormAPI/Controllers/AccountController.cs @@ -1,4 +1,5 @@ using System.Configuration; +using System.Data.Entity; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -6,12 +7,12 @@ using eFormAPI.Common.API; using eFormAPI.Common.Models.Auth; using eFormAPI.Common.Models.User; +using eFormAPI.Web.Infrastructure.Consts; using eFormAPI.Web.Infrastructure.Data; using eFormAPI.Web.Infrastructure.Data.Entities; using eFormAPI.Web.Infrastructure.Identity; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; -using Microsoft.Owin.Security; namespace eFormAPI.Web.Controllers { @@ -20,25 +21,19 @@ namespace eFormAPI.Web.Controllers public class AccountController : ApiController { private EformUserManager _userManager; + private readonly EformRoleManager _eformRoleManager; + private readonly BaseDbContext _dbContext; - public AccountController() + public AccountController(BaseDbContext dbContext) { + _eformRoleManager = new EformRoleManager( + new EformRoleStore(new BaseDbContext())); + ; + _dbContext = dbContext; } - public AccountController(EformUserManager userManager, - ISecureDataFormat accessTokenFormat) - { - UserManager = userManager; - AccessTokenFormat = accessTokenFormat; - } - - public EformUserManager UserManager - { - get => _userManager ?? Request.GetOwinContext().GetUserManager(); - private set => _userManager = value; - } - - public ISecureDataFormat AccessTokenFormat { get; private set; } + private EformUserManager UserManager => + _userManager ?? Request.GetOwinContext().GetUserManager(); // GET api/account/user-info [Route("user-info")] @@ -106,6 +101,44 @@ await UserManager.SendEmailAsync(user.Id, "Reset Password", return new OperationResult(false); } + + [HttpGet] + [AllowAnonymous] + [Route("reset-admin-password")] + public async Task ResetAdminPassword(string code) + { + var securityCode = ConfigurationManager.AppSettings["restore:securityCode"]; + if (string.IsNullOrEmpty(securityCode)) + { + return new OperationResult(false, "Please setup security code on server."); + } + var defaultPassword = ConfigurationManager.AppSettings["restore:defaultPassword"]; + if (code != securityCode) + { + return new OperationResult(false, "Invalid security code."); + } + var role = await _eformRoleManager.FindByNameAsync(EformRoles.Admin); + var user = _dbContext.Users.Include(x => x.Roles) + .FirstOrDefault(x => x.Roles.Any(y => y.RoleId == role.Id)); + if (user == null) + { + return new OperationResult(false, "Admin user not found"); + } + var removeResult = await UserManager.RemovePasswordAsync(user.Id); + if (!removeResult.Succeeded) + { + return new OperationResult(false, + "Error while removing old password. \n" + string.Join(" ", removeResult.Errors)); + } + var addPasswordResult = await UserManager.AddPasswordAsync(user.Id, defaultPassword); + if (!addPasswordResult.Succeeded) + { + return new OperationResult(false, + "Error while adding new password. \n" + string.Join(" ", addPasswordResult.Errors)); + } + return new OperationResult(true, $"Your email: {user.Email}. Password has been reset."); + } + // POST: /account/reset-password [HttpPost] [Route("reset-password")] diff --git a/eFormAPI/eFormAPI/Controllers/CasesController.cs b/eFormAPI/eFormAPI/Controllers/CasesController.cs index f0b995a24c..a42adfdbbb 100644 --- a/eFormAPI/eFormAPI/Controllers/CasesController.cs +++ b/eFormAPI/eFormAPI/Controllers/CasesController.cs @@ -51,6 +51,23 @@ public OperationDataResult Edit(int id) } } + [HttpGet] + public OperationResult Delete(int id) + { + try + { + var core = _coreHelper.GetCore(); + + return core.CaseDeleteResult(id) + ? new OperationResult(true, $"Case #{id} deleted successfully") + : new OperationResult(false, "Case could not be removed"); + } + catch (Exception) + { + return new OperationResult(false, "Case could not be removed"); + } + } + [HttpPost] public OperationResult Update(ReplyRequest model) { diff --git a/eFormAPI/eFormAPI/Controllers/TemplateFilesController.cs b/eFormAPI/eFormAPI/Controllers/TemplateFilesController.cs index 89d4541853..4366481b34 100644 --- a/eFormAPI/eFormAPI/Controllers/TemplateFilesController.cs +++ b/eFormAPI/eFormAPI/Controllers/TemplateFilesController.cs @@ -7,6 +7,7 @@ using System.Net.Http.Headers; using System.Web; using System.Web.Http; +using Castle.Components.DictionaryAdapter.Xml; using eFormAPI.Common.API; using eFormAPI.Web.Infrastructure.Helpers; @@ -73,12 +74,21 @@ public OperationResult RotateImage(string fileName) { return new OperationResult(false, "File not found"); } - - var img = Image.FromFile(filePath); - img.RotateFlip(RotateFlipType.Rotate90FlipNone); - img.Save(filePath); - img.Dispose(); - + try + { + var img = Image.FromFile(filePath); + img.RotateFlip(RotateFlipType.Rotate90FlipNone); + img.Save(filePath); + img.Dispose(); + } + catch (Exception e) + { + if (e.Message == "A generic error occurred in GDI+.") + { + return new OperationResult(true); + } + return new OperationResult(false, "Error while rotate image."); + } return new OperationResult(true, "Image rotated successfully."); } diff --git a/eFormAPI/eFormAPI/Web.config b/eFormAPI/eFormAPI/Web.config index 1780c4e0a5..59270217af 100644 --- a/eFormAPI/eFormAPI/Web.config +++ b/eFormAPI/eFormAPI/Web.config @@ -32,6 +32,8 @@ + +