omgwtf so many changes. #865

pull/1510/head
tidusjar 7 years ago
parent 64d37c37a3
commit 2e8f08fef1

@ -1,5 +0,0 @@
{
"sdk": {
"version": "2.0.0"
}
}

@ -10,6 +10,7 @@ namespace Ombi.Core.Models.UI
public List<ClaimCheckboxes> Claims { get; set; } public List<ClaimCheckboxes> Claims { get; set; }
public string EmailAddress { get; set; } public string EmailAddress { get; set; }
public string Password { get; set; } public string Password { get; set; }
public bool IsSetup { get; set; }
public UserType UserType { get; set; } public UserType UserType { get; set; }
} }

@ -135,6 +135,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IJobSetup, JobSetup>(); services.AddTransient<IJobSetup, JobSetup>();
services.AddTransient<IRadarrCacher, RadarrCacher>(); services.AddTransient<IRadarrCacher, RadarrCacher>();
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>(); services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
} }
} }
} }

@ -1,6 +1,7 @@
using Hangfire; using Hangfire;
using Ombi.Schedule.Jobs; using Ombi.Schedule.Jobs;
using Ombi.Schedule.Jobs.Emby; using Ombi.Schedule.Jobs.Emby;
using Ombi.Schedule.Jobs.Plex;
using Ombi.Schedule.Jobs.Radarr; using Ombi.Schedule.Jobs.Radarr;
using Ombi.Schedule.Ombi; using Ombi.Schedule.Ombi;
@ -9,17 +10,19 @@ namespace Ombi.Schedule
public class JobSetup : IJobSetup public class JobSetup : IJobSetup
{ {
public JobSetup(IPlexContentCacher plexContentCacher, IRadarrCacher radarrCacher, public JobSetup(IPlexContentCacher plexContentCacher, IRadarrCacher radarrCacher,
IOmbiAutomaticUpdater updater, IEmbyContentCacher embyCacher) IOmbiAutomaticUpdater updater, IEmbyContentCacher embyCacher, IPlexUserImporter userImporter)
{ {
PlexContentCacher = plexContentCacher; PlexContentCacher = plexContentCacher;
RadarrCacher = radarrCacher; RadarrCacher = radarrCacher;
Updater = updater; Updater = updater;
EmbyContentCacher = embyCacher; EmbyContentCacher = embyCacher;
PlexUserImporter = userImporter;
} }
private IPlexContentCacher PlexContentCacher { get; } private IPlexContentCacher PlexContentCacher { get; }
private IRadarrCacher RadarrCacher { get; } private IRadarrCacher RadarrCacher { get; }
private IOmbiAutomaticUpdater Updater { get; } private IOmbiAutomaticUpdater Updater { get; }
private IPlexUserImporter PlexUserImporter { get; }
private IEmbyContentCacher EmbyContentCacher { get; } private IEmbyContentCacher EmbyContentCacher { get; }
public void Setup() public void Setup()
@ -27,9 +30,10 @@ namespace Ombi.Schedule
RecurringJob.AddOrUpdate(() => PlexContentCacher.CacheContent(), Cron.Hourly); RecurringJob.AddOrUpdate(() => PlexContentCacher.CacheContent(), Cron.Hourly);
RecurringJob.AddOrUpdate(() => EmbyContentCacher.Start(), Cron.Hourly); RecurringJob.AddOrUpdate(() => EmbyContentCacher.Start(), Cron.Hourly);
RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly); RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly);
//RecurringJob.AddOrUpdate(() => Updater.Update(), Cron.Daily); RecurringJob.AddOrUpdate(() => PlexUserImporter.Start(), Cron.Daily);
RecurringJob.AddOrUpdate(() => Updater.Update(), Cron.Daily);
BackgroundJob.Enqueue(() => Updater.Update()); //BackgroundJob.Enqueue(() => PlexUserImporter.Start());
} }
} }
} }

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Ombi.Schedule.Jobs.Plex
{
public interface IPlexUserImporter
{
Task Start();
}
}

@ -7,29 +7,37 @@ using Ombi.Api.Plex;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; using Ombi.Core.Settings.Models.External;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
public class PlexUserImporter public class PlexUserImporter : IPlexUserImporter
{ {
public PlexUserImporter(IPlexApi api, UserManager<OmbiUser> um, ILogger<PlexUserImporter> log, public PlexUserImporter(IPlexApi api, UserManager<OmbiUser> um, ILogger<PlexUserImporter> log,
ISettingsService<PlexSettings> plexSettings) ISettingsService<PlexSettings> plexSettings, ISettingsService<UserManagementSettings> ums)
{ {
_api = api; _api = api;
_userManager = um; _userManager = um;
_log = log; _log = log;
_plexSettings = plexSettings; _plexSettings = plexSettings;
_userManagementSettings = ums;
} }
private readonly IPlexApi _api; private readonly IPlexApi _api;
private readonly UserManager<OmbiUser> _userManager; private readonly UserManager<OmbiUser> _userManager;
private readonly ILogger<PlexUserImporter> _log; private readonly ILogger<PlexUserImporter> _log;
private readonly ISettingsService<PlexSettings> _plexSettings; private readonly ISettingsService<PlexSettings> _plexSettings;
private readonly ISettingsService<UserManagementSettings> _userManagementSettings;
public async Task Start() public async Task Start()
{ {
var userManagementSettings = await _userManagementSettings.GetSettingsAsync();
if (!userManagementSettings.ImportMediaServerUsers)
{
return;
}
var settings = await _plexSettings.GetSettingsAsync(); var settings = await _plexSettings.GetSettingsAsync();
if (!settings.Enable) if (!settings.Enable)
{ {
@ -72,7 +80,13 @@ namespace Ombi.Schedule.Jobs.Plex
continue; continue;
} }
// TODO Set default permissions/roles // TODO Set default permissions/roles
if (userManagementSettings.DefaultRoles.Any())
{
foreach (var defaultRole in userManagementSettings.DefaultRoles)
{
await _userManager.AddToRoleAsync(newUser, defaultRole);
}
}
} }
else else
{ {

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Ombi.Settings.Settings.Models
{
public class UserManagementSettings : Settings
{
public bool ImportMediaServerUsers { get; set; }
public List<string> DefaultRoles { get; set; }
}
}

@ -7,9 +7,8 @@ import { RouterModule, Routes } from '@angular/router';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
// Third Party // Third Party
import { ButtonModule, DialogModule, CaptchaModule } from 'primeng/primeng'; import { ButtonModule, DialogModule, CaptchaModule,DataTableModule, SharedModule } from 'primeng/primeng';
import { GrowlModule } from 'primeng/components/growl/growl'; import { GrowlModule } from 'primeng/components/growl/growl';
import { DataTableModule, SharedModule } from 'primeng/primeng';
//import { DragulaModule, DragulaService } from 'ng2-dragula/ng2-dragula'; //import { DragulaModule, DragulaService } from 'ng2-dragula/ng2-dragula';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@ -41,6 +40,7 @@ import { WizardModule } from './wizard/wizard.module';
import { SearchModule } from './search/search.module'; import { SearchModule } from './search/search.module';
import { UserManagementModule } from './usermanagement/usermanagement.module'; import { UserManagementModule } from './usermanagement/usermanagement.module';
import { RequestsModule } from './requests/requests.module'; import { RequestsModule } from './requests/requests.module';
//import { PipeModule } from './pipes/pipe.module';
const routes: Routes = [ const routes: Routes = [
{ path: '*', component: PageNotFoundComponent }, { path: '*', component: PageNotFoundComponent },
@ -79,7 +79,7 @@ const routes: Routes = [
ReactiveFormsModule, ReactiveFormsModule,
UserManagementModule, UserManagementModule,
RequestsModule, RequestsModule,
CaptchaModule CaptchaModule,
], ],
declarations: [ declarations: [
AppComponent, AppComponent,
@ -87,7 +87,7 @@ const routes: Routes = [
LoginComponent, LoginComponent,
LandingPageComponent, LandingPageComponent,
ResetPasswordComponent, ResetPasswordComponent,
TokenResetPasswordComponent TokenResetPasswordComponent,
], ],
providers: [ providers: [
RequestService, RequestService,

@ -1,6 +1,7 @@
export interface IUserLogin { export interface IUserLogin {
username: string, username: string,
password:string password: string,
rememberMe:boolean,
} }
export interface ILocalUser { export interface ILocalUser {

@ -1,4 +1,5 @@
export interface IRequestEngineResult { export interface IRequestEngineResult {
requestAdded: boolean, requestAdded: boolean,
message: string message: string,
errorMessage: string,
} }

@ -6,8 +6,7 @@
emailAddress: string, emailAddress: string,
password: string, password: string,
userType: UserType, userType: UserType,
isSetup:boolean,
// FOR UI // FOR UI
checked: boolean, checked: boolean,
} }

@ -19,7 +19,10 @@ include the remember me checkbox
<input type="password" id="inputPassword" class="form-control" formControlName="password" placeholder="Password"> <input type="password" id="inputPassword" class="form-control" formControlName="password" placeholder="Password">
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" id="RememberMe" formControlName="rememberMe"> Remember Me <input type="checkbox" id="RememberMe" formControlName="rememberMe" >
<label for="RememberMe"> Remember Me</label>
</div> </div>
</div> </div>
<button class="btn btn-success" type="submit">Sign in</button> <button class="btn btn-success" type="submit">Sign in</button>

@ -47,7 +47,6 @@ export class LoginComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.settingsService.getLandingPage().subscribe(x => { this.settingsService.getLandingPage().subscribe(x => {
debugger;
if (x.enabled && !this.landingFlag) { if (x.enabled && !this.landingFlag) {
this.router.navigate(['landingpage']); this.router.navigate(['landingpage']);
} }
@ -62,10 +61,10 @@ export class LoginComponent implements OnInit {
onSubmit(form: FormGroup): void { onSubmit(form: FormGroup): void {
if (form.invalid) { if (form.invalid) {
this.notify.error("Validation", "Please check your entered values"); this.notify.error("Validation", "Please check your entered values");
return return;
} }
var value = form.value; var value = form.value;
this.authService.login({ password: value.password, username: value.username }) this.authService.login({ password: value.password, username: value.username, rememberMe:value.rememberMe })
.subscribe(x => { .subscribe(x => {
localStorage.setItem("id_token", x.access_token); localStorage.setItem("id_token", x.access_token);

@ -0,0 +1,17 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { HumanizePipe } from './HumanizePipe';
@NgModule({
imports: [],
declarations: [HumanizePipe],
exports: [HumanizePipe],
})
export class PipeModule {
static forRoot() : ModuleWithProviders {
return {
ngModule: PipeModule,
providers: [],
};
}
}

@ -11,7 +11,7 @@ import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map'; import 'rxjs/add/operator/map';
import { RequestService } from '../services/request.service'; import { RequestService } from '../services/request.service';
import { IdentityService } from '../services/identity.service'; import { AuthService } from '../auth/auth.service';
import { IMovieRequests } from '../interfaces/IRequestModel'; import { IMovieRequests } from '../interfaces/IRequestModel';
@ -20,7 +20,8 @@ import { IMovieRequests } from '../interfaces/IRequestModel';
templateUrl: './movierequests.component.html' templateUrl: './movierequests.component.html'
}) })
export class MovieRequestsComponent implements OnInit, OnDestroy { export class MovieRequestsComponent implements OnInit, OnDestroy {
constructor(private requestService: RequestService, private identityService: IdentityService) { constructor(private requestService: RequestService,
private auth:AuthService) {
this.searchChanged this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event .debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value .distinctUntilChanged() // only emit if value is different from previous value
@ -54,6 +55,7 @@ export class MovieRequestsComponent implements OnInit, OnDestroy {
this.amountToLoad = 5; this.amountToLoad = 5;
this.currentlyLoaded = 5; this.currentlyLoaded = 5;
this.loadInit(); this.loadInit();
this.isAdmin = this.auth.hasRole("admin");
} }
@ -109,7 +111,6 @@ export class MovieRequestsComponent implements OnInit, OnDestroy {
this.requestService.getMovieRequests(this.amountToLoad, 0) this.requestService.getMovieRequests(this.amountToLoad, 0)
.takeUntil(this.subscriptions) .takeUntil(this.subscriptions)
.subscribe(x => this.movieRequests = x); .subscribe(x => this.movieRequests = x);
this.isAdmin = this.identityService.hasRole("Admin");
} }
private resetSearch() { private resetSearch() {

@ -8,8 +8,7 @@
</div> </div>
<div class="col-md-1 col-md-push-9"> <div class="col-md-1 col-md-push-9" *ngIf="isAdmin">
<!--// TODO ADMIN-->
<form> <form>
<button style="text-align: right" *ngIf="child.CanApprove" (click)="approve(child)" class="btn btn-sm btn-success-outline" type="submit"><i class="fa fa-plus"></i> Approve</button> <button style="text-align: right" *ngIf="child.CanApprove" (click)="approve(child)" class="btn btn-sm btn-success-outline" type="submit"><i class="fa fa-plus"></i> Approve</button>
</form> </form>
@ -69,14 +68,7 @@
<span *ngIf="ep.available" class="label label-success">Available</span> <span *ngIf="ep.available" class="label label-success">Available</span>
<span *ngIf="ep.approved && !ep.available" class="label label-info">Processing Request</span> <span *ngIf="ep.approved && !ep.available" class="label label-info">Processing Request</span>
<div *ngIf="!ep.approved"> <div *ngIf="!ep.approved">
<div *ngIf="ep.requested && !ep.available; then requested else notRequested"></div> <div *ngIf="!ep.available"><span class="label label-warning">Pending Approval</span></div>
<ng-template #requested>
<span class="label label-warning">Pending Approval</span>
</ng-template>
<ng-template #notRequested>
<span class="label label-danger">Not Yet Requested</span>
</ng-template>
</div> </div>
</td> </td>
</tr> </tr>

@ -7,7 +7,7 @@ import { IChildRequests, IEpisodesRequests } from '../interfaces/IRequestModel';
templateUrl: './tvrequest-children.component.html' templateUrl: './tvrequest-children.component.html'
}) })
export class TvRequestChildrenComponent { export class TvRequestChildrenComponent {
constructor(private requestService: RequestService) { constructor(private requestService: RequestService ) {
} }
@Input() childRequests: IChildRequests[]; @Input() childRequests: IChildRequests[];

@ -10,7 +10,7 @@ import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map'; import 'rxjs/add/operator/map';
import { RequestService } from '../services/request.service'; import { RequestService } from '../services/request.service';
import { IdentityService } from '../services/identity.service'; import { AuthService } from '../auth/auth.service';
import { ITvRequests, IChildRequests, INewSeasonRequests, IEpisodesRequests } from '../interfaces/IRequestModel'; import { ITvRequests, IChildRequests, INewSeasonRequests, IEpisodesRequests } from '../interfaces/IRequestModel';
import { TreeNode, } from "primeng/primeng"; import { TreeNode, } from "primeng/primeng";
@ -25,7 +25,8 @@ import { TreeNode, } from "primeng/primeng";
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class TvRequestsComponent implements OnInit, OnDestroy { export class TvRequestsComponent implements OnInit, OnDestroy {
constructor(private requestService: RequestService, private identityService: IdentityService) { constructor(private requestService: RequestService,
private auth:AuthService) {
this.searchChanged this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event .debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value .distinctUntilChanged() // only emit if value is different from previous value
@ -111,6 +112,7 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
this.currentlyLoaded = 5; this.currentlyLoaded = 5;
this.tvRequests = []; this.tvRequests = [];
this.loadInit(); this.loadInit();
this.isAdmin = this.auth.hasRole("admin");
} }
public loadMore() { public loadMore() {
@ -208,7 +210,6 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
.subscribe(x => { .subscribe(x => {
this.tvRequests = this.transformData(x); this.tvRequests = this.transformData(x);
}); });
this.isAdmin = this.identityService.hasRole("Admin");
} }
private resetSearch() { private resetSearch() {

@ -56,7 +56,8 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
this.movieResults = []; this.movieResults = [];
this.result = { this.result = {
message: "", message: "",
requestAdded: false requestAdded: false,
errorMessage: ""
} }
} }
@ -79,7 +80,9 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
this.notificationService.success("Request Added", this.notificationService.success("Request Added",
`Request for ${searchResult.title} has been added successfully`); `Request for ${searchResult.title} has been added successfully`);
} else { } else {
this.notificationService.warning("Request Added", this.result.message); this.notificationService.warning("Request Added", this.result.message ? this.result.message : this.result.errorMessage);
searchResult.requested = false;
searchResult.approved = false;
} }
}); });
} }

@ -32,14 +32,14 @@
<div class="form-group"> <div class="form-group">
<label for="ApiKey" class="control-label">Api Key</label> <label for="ApiKey" class="control-label">Api Key</label>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control form-control-custom" id="ApiKey" name="ApiKey" formControlName="apiKey"> <input type="text" class="form-control form-control-custom" id="ApiKey" name="ApiKey" formControlName="apiKey" readonly="readonly" #apiKey>
<div class="input-group-addon"> <div class="input-group-addon">
<div (click)="refreshApiKey()" id="refreshKey" class="fa fa-refresh" title="Reset API Key" pTooltip="This will invalidate the old API key"></div> <div (click)="refreshApiKey()" id="refreshKey" class="fa fa-refresh" title="Reset API Key" pTooltip="This will invalidate the old API key" ></div>
</div> </div>
<div class="input-group-addon"> <div class="input-group-addon">
<div class="fa fa-clipboard"></div> <div ngxClipboard [ngxClipboard]="apiKey" class="fa fa-clipboard" (cbOnSuccess)="successfullyCopied()"></div>
</div> </div>
</div> </div>
</div> </div>

@ -20,7 +20,7 @@ export class OmbiComponent implements OnInit {
this.form = this.fb.group({ this.form = this.fb.group({
port: [x.port], port: [x.port],
collectAnalyticData: [x.collectAnalyticData], collectAnalyticData: [x.collectAnalyticData],
apiKey: [{ value: x.apiKey, disabled: true }], apiKey: [x.apiKey],
externalUrl: [x.externalUrl], externalUrl: [x.externalUrl],
allowExternalUsersToAuthenticate: [x.allowExternalUsersToAuthenticate] allowExternalUsersToAuthenticate: [x.allowExternalUsersToAuthenticate]
}); });
@ -49,4 +49,8 @@ export class OmbiComponent implements OnInit {
} }
}); });
} }
successfullyCopied() {
this.notificationService.success("Copied", "Copied the Api Key to the clipboard!");
}
} }

@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { NgbModule, NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule, NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { ClipboardModule } from 'ngx-clipboard/dist';
import { AuthService } from '../auth/auth.service'; import { AuthService } from '../auth/auth.service';
import { AuthGuard } from '../auth/auth.guard'; import { AuthGuard } from '../auth/auth.guard';
@ -27,9 +28,9 @@ import { PushoverComponent } from './notifications/pushover.component';
import { PushbulletComponent } from './notifications/pushbullet.component'; import { PushbulletComponent } from './notifications/pushbullet.component';
import { MattermostComponent } from './notifications/mattermost.component'; import { MattermostComponent } from './notifications/mattermost.component';
import { NotificationTemplate } from './notifications/notificationtemplate.component'; import { NotificationTemplate } from './notifications/notificationtemplate.component';
import { PipeModule } from '../pipes/pipe.module';
import { SettingsMenuComponent } from './settingsmenu.component'; import { SettingsMenuComponent } from './settingsmenu.component';
import { HumanizePipe } from '../pipes/HumanizePipe';
import { MenuModule, InputSwitchModule, InputTextModule, TooltipModule, AutoCompleteModule, CalendarModule } from 'primeng/primeng'; import { MenuModule, InputSwitchModule, InputTextModule, TooltipModule, AutoCompleteModule, CalendarModule } from 'primeng/primeng';
@ -65,6 +66,8 @@ const routes: Routes = [
NgbAccordionModule, NgbAccordionModule,
AutoCompleteModule, AutoCompleteModule,
CalendarModule, CalendarModule,
ClipboardModule,
PipeModule,
], ],
declarations: [ declarations: [
SettingsMenuComponent, SettingsMenuComponent,
@ -78,7 +81,6 @@ const routes: Routes = [
SlackComponent, SlackComponent,
RadarrComponent, RadarrComponent,
EmailNotificationComponent, EmailNotificationComponent,
HumanizePipe,
NotificationTemplate, NotificationTemplate,
PushoverComponent, PushoverComponent,
MattermostComponent, MattermostComponent,

@ -2,52 +2,65 @@
<h3>Create User</h3> <h3>Create User</h3>
<button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button> <button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button>
<div class="modal-body" style="margin-top: 45px;">
<div class="modal-body" style="margin-top:45px;"> <div class="col-md-6">
<div class="form-group"> <h4>User Details</h4>
<label for="username" class="control-label">Username</label>
<div>
<input type="text" [(ngModel)]="user.username" class="form-control form-control-custom " id="username" name="username" value="{{user?.username}}">
</div>
</div> </div>
<div class="form-group"> <div class="col-md-6">
<label for="alias" class="control-label">Alias</label> <h4>Roles</h4>
<div>
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}">
</div>
</div> </div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="username" class="control-label">Username</label>
<div>
<input type="text" [(ngModel)]="user.username" class="form-control form-control-custom " id="username" name="username" value="{{user?.username}}">
</div>
</div>
<div class="form-group">
<label for="alias" class="control-label">Alias</label>
<div>
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="emailAddress" class="control-label">Email Address</label> <label for="emailAddress" class="control-label">Email Address</label>
<div> <div>
<input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}"> <input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}">
</div>
</div> </div>
</div>
<div class="form-group">
<div class="form-group"> <label for="password" class="control-label">Password</label>
<label for="password" class="control-label">Password</label> <div>
<div> <input type="password" [(ngModel)]="user.password" class="form-control form-control-custom " id="password" name="password">
<input type="password" [(ngModel)]="user.password" class="form-control form-control-custom " id="password" name="password"> </div>
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<label for="confirmPass" class="control-label">Confirm Password</label> <label for="confirmPass" class="control-label">Confirm Password</label>
<div> <div>
<input type="password" [(ngModel)]="confirmPass" class="form-control form-control-custom " id="confirmPass" name="confirmPass"> <input type="password" [(ngModel)]="confirmPass" class="form-control form-control-custom " id="confirmPass" name="confirmPass">
</div>
</div> </div>
</div> </div>
<div *ngFor="let c of availableClaims"> <div class="col-md-6">
<div class="form-group"> <div *ngFor="let c of availableClaims">
<div class="checkbox"> <div class="form-group">
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled"> <div class="checkbox">
<label for="create{{c.value}}">{{c.value}}</label> <input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled">
<label for="create{{c.value}}">{{c.value | humanize}}</label>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div> <div class="row">
<button type="button" class="btn btn-danger-outline" (click)="create()">Create</button> <div class="col-md-12">
<button type="button" class="btn btn-danger-outline" (click)="create()">Create</button>
</div>
</div> </div>

@ -28,7 +28,8 @@ export class UserManagementAddComponent implements OnInit {
password: "", password: "",
username: "", username: "",
userType: UserType.LocalUser, userType: UserType.LocalUser,
checked:false checked:false,
isSetup:false
} }
} }

@ -1,44 +1,53 @@
<div *ngIf="user"> <div *ngIf="user">
<h3>User: {{user.username}}</h3> <h3>User: {{user.username}}</h3>
<button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button> <button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button>
<div class="modal-body" style="margin-top:45px;"> <div class="modal-body" style="margin-top: 45px;">
<div class="form-group"> <div class="col-md-6">
<label for="username" class="control-label">Username</label> <h4>User Details</h4>
<div> </div>
<input type="text" [(ngModel)]="user.username" [readonly]="true" class="form-control form-control-custom " id="username" name="username" value="{{user?.username}}"> <div class="col-md-6">
</div> <h4>Roles</h4>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="username" class="control-label">Username</label>
<div>
<input type="text" [(ngModel)]="user.username" [readonly]="true" class="form-control form-control-custom " id="username" name="username" value="{{user?.username}}">
</div> </div>
<div class="form-group"> </div>
<label for="alias" class="control-label">Alias</label> <div class="form-group">
<div> <label for="alias" class="control-label">Alias</label>
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}"> <div>
</div> <input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}">
</div> </div>
</div>
<div class="form-group">
<label for="alias" class="control-label">Email Address</label> <div class="form-group">
<div> <label for="alias" class="control-label">Email Address</label>
<input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}"> <div>
</div> <input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}">
</div> </div>
<div *ngFor="let c of user.claims"> </div>
<div class="form-group"> </div>
<div class="checkbox">
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled">
<label for="create{{c.value}}">{{c.value}}</label>
</div> <div class="col-md-6">
</div> <div *ngFor="let c of user.claims">
<div class="form-group">
<div class="checkbox">
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled">
<label for="create{{c.value}}">{{c.value | humanize}}</label>
</div>
</div> </div>
</div> </div>
<div> </div>
<button type="button" class="btn btn-primary-outline" (click)="update()">Update</button> </div>
<button type="button" class="btn btn-danger-outline" (click)="delete()">Delete</button> <div>
<button type="button" class="btn btn-primary-outline" (click)="update()">Update</button>
<button type="button" class="btn btn-danger-outline" (click)="delete()">Delete</button>
<button type="button" style="float:right;" class="btn btn-info-outline" (click)="resetPassword()" pTooltip="You need your SMTP settings setup">Send Reset Password Link</button>
</div> </div>
</div> </div>

@ -41,6 +41,20 @@ export class UserManagementEditComponent {
}); });
} }
resetPassword(): void {
this.identityService.submitResetPassword(this.user.emailAddress).subscribe(x => {
if (x.successful) {
this.notificationSerivce.success("Reset", `Sent reset password email to ${this.user.emailAddress}`);
this.router.navigate(['usermanagement']);
} else {
x.errors.forEach((val) => {
this.notificationSerivce.error("Error", val);
});
}
});
}
update(): void { update(): void {
var hasClaims = this.user.claims.some((item) => { var hasClaims = this.user.claims.some((item) => {
if (item.enabled) { return true; } if (item.enabled) { return true; }

@ -82,6 +82,9 @@
<td> <td>
<a [routerLink]="['/usermanagement/edit/' + u.id]" class="btn btn-sm btn-info-outline">Details/Edit</a> <a [routerLink]="['/usermanagement/edit/' + u.id]" class="btn btn-sm btn-info-outline">Details/Edit</a>
</td> </td>
<td *ngIf="!u.isSetup">
<a (click)="welcomeEmail(u)" class="btn btn-sm btn-info-outline">Send Welcome Email</a>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

@ -18,6 +18,10 @@ export class UserManagementComponent implements OnInit {
} }
welcomeEmail(user: IUser): void {
}
users: IUser[]; users: IUser[];
checkAll = false; checkAll = false;

@ -2,7 +2,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MultiSelectModule } from 'primeng/primeng'; import { MultiSelectModule, TooltipModule, } from 'primeng/primeng';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@ -12,6 +12,7 @@ import { UserManagementAddComponent } from './usermanagement-add.component';
import { UpdateDetailsComponent } from './updatedetails.component'; import { UpdateDetailsComponent } from './updatedetails.component';
import { IdentityService } from '../services/identity.service'; import { IdentityService } from '../services/identity.service';
import { PipeModule } from '../pipes/pipe.module';
import { AuthGuard } from '../auth/auth.guard'; import { AuthGuard } from '../auth/auth.guard';
@ -29,13 +30,16 @@ const routes: Routes = [
ReactiveFormsModule, ReactiveFormsModule,
RouterModule.forChild(routes), RouterModule.forChild(routes),
NgbModule.forRoot(), NgbModule.forRoot(),
MultiSelectModule MultiSelectModule,
PipeModule,
TooltipModule,
], ],
declarations: [ declarations: [
UserManagementComponent, UserManagementComponent,
UserManagementAddComponent, UserManagementAddComponent,
UserManagementEditComponent, UserManagementEditComponent,
UpdateDetailsComponent UpdateDetailsComponent,
], ],
exports: [ exports: [
RouterModule RouterModule

@ -23,7 +23,7 @@ export class CreateAdminComponent {
this.identityService.createWizardUser(this.username, this.password).subscribe(x => { this.identityService.createWizardUser(this.username, this.password).subscribe(x => {
if (x) { if (x) {
// Log me in. // Log me in.
this.auth.login({ username: this.username, password: this.password }).subscribe(c => { this.auth.login({ username: this.username, password: this.password, rememberMe:false }).subscribe(c => {
localStorage.setItem("id_token", c.access_token); localStorage.setItem("id_token", c.access_token);

@ -10,9 +10,9 @@ namespace Ombi.Controllers
/// Indexes this instance. /// Indexes this instance.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task<IActionResult> Index() public IActionResult Index()
{ {
return View(); return View();
} }
} }
} }

@ -172,6 +172,7 @@ namespace Ombi.Controllers
EmailAddress = user.Email, EmailAddress = user.Email,
UserType = (Core.Models.UserType)(int)user.UserType, UserType = (Core.Models.UserType)(int)user.UserType,
Claims = new List<ClaimCheckboxes>(), Claims = new List<ClaimCheckboxes>(),
IsSetup = !string.IsNullOrEmpty(user.PasswordHash)
}; };
foreach (var role in userRoles) foreach (var role in userRoles)
@ -428,6 +429,11 @@ namespace Ombi.Controllers
return claims; return claims;
} }
//public async Task SendWelcomeEmail([FromBody] UserViewModel user)
//{
//}
/// <summary> /// <summary>
/// Send out the email with the reset link /// Send out the email with the reset link
/// </summary> /// </summary>

@ -88,7 +88,7 @@ namespace Ombi.Controllers
if (model.RememberMe) if (model.RememberMe)
{ {
// Save the token so we can refresh it later // Save the token so we can refresh it later
await _token.CreateToken(new Tokens() {Token = accessToken, User = user}); //await _token.CreateToken(new Tokens() {Token = accessToken, User = user});
} }
return new JsonResult(new return new JsonResult(new

@ -76,5 +76,11 @@
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" /> <ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
<ProjectReference Include="..\Ombi.Updater\Ombi.Updater.csproj" /> <ProjectReference Include="..\Ombi.Updater\Ombi.Updater.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="ClientApp\app\pipes\pipe.module.js">
<DependentUpon>pipe.module.ts</DependentUpon>
</None>
</ItemGroup>
</Project> </Project>

@ -201,10 +201,10 @@ namespace Ombi
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
//app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
//{ {
// HotModuleReplacement = true HotModuleReplacement = true
//}); });
} }
app.UseHangfireServer(); app.UseHangfireServer();
@ -255,10 +255,7 @@ namespace Ombi
{ {
public bool Authorize(DashboardContext context) public bool Authorize(DashboardContext context)
{ {
var httpContext = context.GetHttpContext(); return true;
// Allow all authenticated users to see the Dashboard (potentially dangerous).
return httpContext.User.IsInRole(OmbiRoles.Admin);
} }
} }
} }

@ -3889,11 +3889,24 @@
"dragula": "3.7.2" "dragula": "3.7.2"
} }
}, },
"ngx-clipboard": {
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/ngx-clipboard/-/ngx-clipboard-8.0.4.tgz",
"integrity": "sha1-l82jDXiOfcHSgONqdyj4ZgDEVf0=",
"requires": {
"ngx-window-token": "0.0.2"
}
},
"ngx-infinite-scroll": { "ngx-infinite-scroll": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-0.5.1.tgz", "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-0.5.1.tgz",
"integrity": "sha1-3ZRSxgL/fDIi8MoWhIGqFBQChrc=" "integrity": "sha1-3ZRSxgL/fDIi8MoWhIGqFBQChrc="
}, },
"ngx-window-token": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/ngx-window-token/-/ngx-window-token-0.0.2.tgz",
"integrity": "sha1-aA7phrvm+V0H2q3xVMDs815YmSA="
},
"no-case": { "no-case": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz",

@ -44,6 +44,7 @@
"intro.js-mit": "^3.0.0", "intro.js-mit": "^3.0.0",
"jquery": "3.2.1", "jquery": "3.2.1",
"ng2-dragula": "1.5.0", "ng2-dragula": "1.5.0",
"ngx-clipboard": "^8.0.4",
"ngx-infinite-scroll": "^0.5.1", "ngx-infinite-scroll": "^0.5.1",
"node-sass": "^4.5.3", "node-sass": "^4.5.3",
"npm": "^5.4.1", "npm": "^5.4.1",

@ -48,6 +48,7 @@ module.exports = function (env) {
'pace-progress/themes/orange/pace-theme-flash.css', 'pace-progress/themes/orange/pace-theme-flash.css',
'intro.js-mit/intro.js', 'intro.js-mit/intro.js',
'intro.js-mit/introjs.css', 'intro.js-mit/introjs.css',
'ngx-clipboard',
//'ng2-dragula', //'ng2-dragula',
//'dragula/dist/dragula.min.css' //'dragula/dist/dragula.min.css'
] ]

Loading…
Cancel
Save