Unverified Commit fc19819e authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #29 from jumpserver/dev

Merge from dev
parents e7411d00 b62ba827
...@@ -22,9 +22,10 @@ ...@@ -22,9 +22,10 @@
"styles": [ "styles": [
"../node_modules/animate.css/animate.min.css", "../node_modules/animate.css/animate.min.css",
"../node_modules/xterm/dist/xterm.css", "../node_modules/xterm/dist/xterm.css",
"assets/elFinder/css/elfinder.min.css", "../node_modules/elfinder/css/elfinder.min.css",
"sass/style.scss", "sass/style.scss",
"styles.css" "styles.css",
"assets/ztree/awesomeStyle/awesome.css"
], ],
"scripts": [ "scripts": [
"../node_modules/jquery/dist/jquery.min.js", "../node_modules/jquery/dist/jquery.min.js",
...@@ -34,8 +35,10 @@ ...@@ -34,8 +35,10 @@
"../node_modules/bootstrap/dist/js/bootstrap.min.js", "../node_modules/bootstrap/dist/js/bootstrap.min.js",
"assets/inspinia/inspinia.js", "assets/inspinia/inspinia.js",
"assets/slimscroll/jquery.slimscroll.min.js", "assets/slimscroll/jquery.slimscroll.min.js",
"assets/elFinder/js/elfinder.min.js", "../node_modules/elfinder/js/elfinder.min.js",
"../node_modules/xterm/dist/xterm.js" "../node_modules/xterm/dist/xterm.js",
"assets/ztree/jquery.ztree.all.min.js",
"assets/ztree/jquery.ztree.exhide.min.js"
], ],
"environmentSource": "environments/environment.ts", "environmentSource": "environments/environment.ts",
"environments": { "environments": {
......
...@@ -48,5 +48,6 @@ ...@@ -48,5 +48,6 @@
"cancel": "取消", "cancel": "取消",
"confirm": "确认", "confirm": "确认",
"document": "文档", "document": "文档",
"support": "商业支持" "support": "商业支持",
"speed": "速度"
} }
cn.json
\ No newline at end of file
This diff is collapsed.
{ {
"name": "luna", "name": "luna",
"version": "0.5.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
...@@ -231,6 +231,14 @@ ...@@ -231,6 +231,14 @@
"tslib": "1.8.1" "tslib": "1.8.1"
} }
}, },
"@angular/flex-layout": {
"version": "2.0.0-beta.10-4905443",
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-2.0.0-beta.10-4905443.tgz",
"integrity": "sha512-jjr6mQ3X2vdEQbsyHD/mz1hfTBUUEOZVLFWEz/sbNoeU7uiA4lvqdp/ASrkZydGJHmTDUYrbBE/9kx0lherZ8Q==",
"requires": {
"tslib": "1.8.1"
}
},
"@angular/forms": { "@angular/forms": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.0.tgz", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.0.tgz",
...@@ -340,6 +348,27 @@ ...@@ -340,6 +348,27 @@
"resolved": "https://registry.npmjs.org/@swimlane/ngx-datatable/-/ngx-datatable-11.1.7.tgz", "resolved": "https://registry.npmjs.org/@swimlane/ngx-datatable/-/ngx-datatable-11.1.7.tgz",
"integrity": "sha512-TMDN26Q4J+Sh+OPqAx8BK5Q/3hAAmcTAUQ9wvC9nboSOAmYUaHGz8t21yGdeUtRtunIfMpjTnwLa+X2Pfoq42w==" "integrity": "sha512-TMDN26Q4J+Sh+OPqAx8BK5Q/3hAAmcTAUQ9wvC9nboSOAmYUaHGz8t21yGdeUtRtunIfMpjTnwLa+X2Pfoq42w=="
}, },
"@swimlane/ngx-ui": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@swimlane/ngx-ui/-/ngx-ui-20.2.0.tgz",
"integrity": "sha512-GPmuhcsLtogiBrITZUSUVEB8X7EtrmJQ+T5MEQqdA/rPsu6VHMPHmx+Dl+Sf2iUubydJByewRgBHNVyr5L+tMQ==",
"requires": {
"@angular/flex-layout": "2.0.0-beta.10-4905443",
"angular2-moment": "1.8.0",
"codemirror": "5.37.0",
"moment": "2.22.1",
"mousetrap": "1.6.1",
"ng2-file-upload": "1.3.0",
"normalize.css": "5.0.0"
},
"dependencies": {
"moment": {
"version": "2.22.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz",
"integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ=="
}
}
},
"@types/jasmine": { "@types/jasmine": {
"version": "2.8.4", "version": "2.8.4",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.4.tgz", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.4.tgz",
...@@ -518,6 +547,14 @@ ...@@ -518,6 +547,14 @@
} }
} }
}, },
"angular2-moment": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/angular2-moment/-/angular2-moment-1.8.0.tgz",
"integrity": "sha512-cBppm4FM9sOtZr4eSnodjbIhvn3bZzuw1VIb3VN53/aWKQVrNosQVG7l0dnX5ifdy08i9Yd/5eGYOXw+Lb63YA==",
"requires": {
"moment": "2.18.1"
}
},
"animate.css": { "animate.css": {
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/animate.css/-/animate.css-3.5.2.tgz", "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-3.5.2.tgz",
...@@ -1993,6 +2030,11 @@ ...@@ -1993,6 +2030,11 @@
"sprintf-js": "1.0.3" "sprintf-js": "1.0.3"
} }
}, },
"codemirror": {
"version": "5.37.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.37.0.tgz",
"integrity": "sha512-dQaayDJCLU4UJcwg2RM44oFrs0dMNndTp6qxQJF6XI71l1xN3RB4IqiKES0b0rccbARbrD/UBB4t8DNknfaOTw=="
},
"collection-visit": { "collection-visit": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
...@@ -2985,6 +3027,9 @@ ...@@ -2985,6 +3027,9 @@
"integrity": "sha1-SpJzTgBEyM8LFVO+V+riGkxuX6s=", "integrity": "sha1-SpJzTgBEyM8LFVO+V+riGkxuX6s=",
"dev": true "dev": true
}, },
"elfinder": {
"version": "git+https://github.com/Studio-42/elFinder.git#2d3a31f70e3c817417913ab54035717f26e3efce"
},
"elliptic": { "elliptic": {
"version": "6.4.0", "version": "6.4.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
...@@ -5789,11 +5834,6 @@ ...@@ -5789,11 +5834,6 @@
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
"dev": true "dev": true
}, },
"inconsolata": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/inconsolata/-/inconsolata-0.0.2.tgz",
"integrity": "sha1-uqFDP0PUKqHtsmvZ7odzjpKSUOs="
},
"indent-string": { "indent-string": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
...@@ -7629,6 +7669,11 @@ ...@@ -7629,6 +7669,11 @@
"resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
"integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8="
}, },
"mousetrap": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.1.tgz",
"integrity": "sha1-KghfXHUSlMdefoH27CVFspy/Qtk="
},
"move-concurrently": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
...@@ -8031,6 +8076,16 @@ ...@@ -8031,6 +8076,16 @@
"chart.js": "2.7.1" "chart.js": "2.7.1"
} }
}, },
"ng2-file-tree": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/ng2-file-tree/-/ng2-file-tree-0.4.3.tgz",
"integrity": "sha1-gpYDAd2GVlu9XLbirQJg4azElq4="
},
"ng2-file-upload": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ng2-file-upload/-/ng2-file-upload-1.3.0.tgz",
"integrity": "sha512-Pudxik6LWYsT8hNiEW7RfjgGWAnvfQywxwJYMdt1snTUe+KnlRc/QqPv3QEQW6plXTanuLkYz/TbqilSfSHOsw=="
},
"ngx-bootstrap": { "ngx-bootstrap": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/ngx-bootstrap/-/ngx-bootstrap-1.9.3.tgz", "resolved": "https://registry.npmjs.org/ngx-bootstrap/-/ngx-bootstrap-1.9.3.tgz",
...@@ -8478,6 +8533,11 @@ ...@@ -8478,6 +8533,11 @@
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
"dev": true "dev": true
}, },
"normalize.css": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-5.0.0.tgz",
"integrity": "sha1-fOyHXOgXilMzxN6Ato6pwYudfDc="
},
"npm-font-open-sans": { "npm-font-open-sans": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/npm-font-open-sans/-/npm-font-open-sans-1.1.0.tgz", "resolved": "https://registry.npmjs.org/npm-font-open-sans/-/npm-font-open-sans-1.1.0.tgz",
...@@ -9993,6 +10053,11 @@ ...@@ -9993,6 +10053,11 @@
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
"dev": true "dev": true
}, },
"requirejs": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.5.tgz",
"integrity": "sha512-svnO+aNcR/an9Dpi44C7KSAy5fFGLtmPbaaCeQaklUz8BQhS64tWWIIlvEA5jrWICzlO/X9KSzSeXFnZdBu8nw=="
},
"requires-port": { "requires-port": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
......
{ {
"name": "luna", "name": "luna",
"version": "0.5.0", "version": "1.0.0",
"license": "GPLv3", "license": "GPLv3",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
"@angular/common": "5.2.0", "@angular/common": "5.2.0",
"@angular/compiler": "5.2.0", "@angular/compiler": "5.2.0",
"@angular/core": "5.2.0", "@angular/core": "5.2.0",
"@angular/flex-layout": "^5.0.0-beta.14",
"@angular/forms": "5.2.0", "@angular/forms": "5.2.0",
"@angular/http": "5.2.0", "@angular/http": "5.2.0",
"@angular/material": "^5.1.1", "@angular/material": "^5.1.1",
...@@ -24,6 +25,7 @@ ...@@ -24,6 +25,7 @@
"@angular/platform-browser-dynamic": "5.2.0", "@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.2.0", "@angular/router": "5.2.0",
"@swimlane/ngx-datatable": "^11.1.7", "@swimlane/ngx-datatable": "^11.1.7",
"@swimlane/ngx-ui": "^20.2.0",
"ajv": "^6.2.1", "ajv": "^6.2.1",
"animate.css": "^3.5.2", "animate.css": "^3.5.2",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
...@@ -32,10 +34,11 @@ ...@@ -32,10 +34,11 @@
"compass-mixins": "^0.12.10", "compass-mixins": "^0.12.10",
"core-js": "2.5.3", "core-js": "2.5.3",
"directory-encoder": "^0.9.2", "directory-encoder": "^0.9.2",
"elfinder": "^2.1.33",
"filetree-css": "^1.0.0", "filetree-css": "^1.0.0",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"guacamole-common-js": "^0.9.14-b",
"handlebars": "^4.0.11", "handlebars": "^4.0.11",
"inconsolata": "0.0.2",
"intl": "1.2.5", "intl": "1.2.5",
"jquery": "3.2.1", "jquery": "3.2.1",
"jquery-slimscroll": "^1.3.8", "jquery-slimscroll": "^1.3.8",
...@@ -56,14 +59,15 @@ ...@@ -56,14 +59,15 @@
"npm-font-open-sans": "^1.1.0", "npm-font-open-sans": "^1.1.0",
"peity": "^3.2.1", "peity": "^3.2.1",
"popper.js": "1.12.9", "popper.js": "1.12.9",
"requirejs": "^2.3.5",
"roboto-fontface": "^0.8.0", "roboto-fontface": "^0.8.0",
"rxjs": "5.5.6", "rxjs": "5.5.6",
"sass-math": "^1.0.0", "sass-math": "^1.0.0",
"socket.io": "^2.0.3", "socket.io": "^2.0.3",
"socket.io-client": "^2.0.4", "socket.io-client": "^2.1.0",
"ssh-keygen": "^0.4.1", "ssh-keygen": "^0.4.1",
"tether": "^1.4.0", "tether": "^1.4.0",
"tslib": "1.8.1", "tslib": "^1.9.0",
"uuid-js": "^0.7.5", "uuid-js": "^0.7.5",
"xterm": "^2.9.2", "xterm": "^2.9.2",
"zone.js": "0.8.20" "zone.js": "0.8.20"
...@@ -85,7 +89,7 @@ ...@@ -85,7 +89,7 @@
"karma-coverage-istanbul-reporter": "1.3.3", "karma-coverage-istanbul-reporter": "1.3.3",
"karma-jasmine": "1.1.1", "karma-jasmine": "1.1.1",
"karma-jasmine-html-reporter": "0.2.2", "karma-jasmine-html-reporter": "0.2.2",
"node-sass": "^4.7.2", "node-sass": "^4.9.0",
"protractor": "5.2.2", "protractor": "5.2.2",
"ts-node": "3.3.0", "ts-node": "3.3.0",
"tslint": "5.9.1", "tslint": "5.9.1",
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"secure": false "secure": false
}, },
"/luna/i18n": { "/luna/i18n": {
"target": "http://127.0.0.1:5000", "target": "http://127.0.0.1:8088",
"secure": false "secure": false
}, },
"/socket.io/": { "/socket.io/": {
......
<div id="sidebar">
<div class="search">
<input class="left-search" placeholder=" {{'Search'| trans }} ..." maxlength="2048" name="q" autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" autofocus [(ngModel)]="q" (keyup.enter)="Search(q)">
</div>
<div class="overflow">
<ul class="filetree">
<li *ngFor="let hostGroup of HostGroups | SearchFilter: q; let i = index ">
<input type="checkbox" id="hostgroup-{{i}}">
<label for="hostgroup-{{i}}" matTooltip="{{hostGroup.name}}" [matTooltipPosition]="TooltipPosition">{{hostGroup.name}}</label>
<ul [ngClass]="{'insearch': q }">
<li *ngFor="let host of hostGroup.assets_granted | SearchFilter: q" (click)="Connect(host)"
(contextmenu)="onRightClick($event)" matTooltip="{{host.hostname}}"
[matTooltipPosition]="TooltipPosition">
<i class="fa" [ngClass]="'fa-'+(host.platform||'undefined').toLowerCase()" id="fa-{{i}}"></i>
{{host.hostname}}
</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="footer">
Version <strong>{{version}}</strong>
</div>
<!--<app-element-server-menu></app-element-server-menu>-->
div, app-element-term, app-element-iframe {
height: 100%;
padding-bottom: 30px;
}
div {
display: none;
}
.active {
display: block;
}
<app-controlnav></app-controlnav>
<div *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'active':i==NavList.Active}"
>
<app-element-term [host]="m.host"
[userid]="m.user.id"
[index]="i"
*ngIf="m.type=='ssh'">
</app-element-term>
<app-element-guacamole [host]="m.host"
[userid]="m.user.id"
[index]="i"
*ngIf="m.type=='rdp'">
</app-element-guacamole>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlnavComponent } from './controlnav.component';
describe('ControlnavComponent', () => {
let component: ControlnavComponent;
let fixture: ComponentFixture<ControlnavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlnavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlnavComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
<div class="container-fluid row">
<app-cleftbar class="col-md-2" *ngIf="DataStore.leftbarshow"></app-cleftbar>
<app-control [ngClass]="{'col-md-10':DataStore.leftbarshow,'col-md-12':!DataStore.leftbarshow}"></app-control>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { IndexPageComponent } from './index-page.component';
describe('IndexPageComponent', () => {
let component: IndexPageComponent;
let fixture: ComponentFixture<IndexPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ IndexPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IndexPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* app路由
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {environment} from '../environments/environment';
import {NotFoundComponent} from './BasicPage/not-found/not-found.component';
import {LoginComponent} from './BasicPage/login/login.component';
import {ControlPageComponent} from './ControlPage/controlpage.component';
import {ReplayPageComponent} from './replay-page/replay-page.component';
import {MonitorPageComponent} from './monitor-page/monitor-page.component';
import {ElementServerMenuComponent} from './elements/server-menu/server-menu.component';
import {BlankPageComponent} from './blank-page/blank-page.component';
import {TestPageComponent} from './test-page/test-page.component';
import {SettingPageComponent} from './setting-page/setting-page.component';
import {ConnectPageComponent} from './connect-page/connect-page.component';
const appRoutes: Routes = [
// {path: 'users/login', component: LoginComponent},
{path: 'replay/:token', component: ReplayPageComponent},
{path: 'monitor/:token', component: MonitorPageComponent},
// {path: 'test', component: TestPageComponent},
{path: 'connect', component: ConnectPageComponent},
// {path: 'setting', component: SettingPageComponent},
{path: 'undefined', component: BlankPageComponent},
{path: '', component: ControlPageComponent},
{path: '**', component: NotFoundComponent}
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{enableTracing: !environment.production} // <-- debugging purposes only
)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {
}
...@@ -8,126 +8,51 @@ ...@@ -8,126 +8,51 @@
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms'; // <-- NgModel lives here import {FormsModule, ReactiveFormsModule} from '@angular/forms'; // <-- NgModel lives here
import {LoggerModule, NGXLogger, NgxLoggerLevel} from 'ngx-logger'; import {NGXLogger} from 'ngx-logger';
import {HttpClientModule} from '@angular/common/http'; import {HttpClientModule} from '@angular/common/http';
import {AppRoutingModule} from './app-routing.module'; import {AppRouterModule} from './router/router.module';
import {AppComponent} from './app.component'; import {AppComponent} from './pages/app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
// service // service
import {AppService, HttpService, LocalStorageService, LogService, UUIDService} from './app.service'; import {AppService, HttpService, LocalStorageService, LogService, UUIDService} from './app.service';
import {DialogService, ElementDialogAlertComponent} from './elements/dialog/dialog.service';
// Elements
import {ElementFooterComponent} from './elements/footer/footer.component';
import {ElementTermComponent} from './elements/term/term.component';
import {ElementInteractiveComponent} from './elements/interactive/interactive.component';
import {ElementNavComponent} from './elements/nav/nav.component';
import {ElementPopupComponent} from './elements/popup/popup.component';
import {ElementRdpComponent} from './elements/rdp/rdp.component';
import {ElementServerMenuComponent} from './elements/server-menu/server-menu.component';
import {ElementIframeComponent} from './elements/iframe/iframe.component';
import {ElementElfinderComponent} from './elements/elfinder/elfinder.component';
// pages
import {LoginComponent} from './BasicPage/login/login.component';
import {IleftbarComponent} from './IndexPage/ileftbar/ileftbar.component';
import {SearchComponent, SearchFilter} from './ControlPage/search/search.component';
import {CleftbarComponent, CleftbarDialogComponent} from './ControlPage/cleftbar/cleftbar.component';
import {ControlComponent} from './ControlPage/control/control.component';
import {ControlnavComponent} from './ControlPage/control/controlnav/controlnav.component';
import {ControlPageComponent} from './ControlPage/controlpage.component';
import {IndexPageComponent} from './IndexPage/index-page.component';
import {NotFoundComponent} from './BasicPage/not-found/not-found.component';
import {ReplayPageComponent} from './replay-page/replay-page.component';
import {Mp4Component} from './replay-page/mp4/mp4.component';
import {JsonComponent} from './replay-page/json/json.component';
import {UtcDatePipe} from './app.pipe';
import {MonitorPageComponent} from './monitor-page/monitor-page.component';
import {LinuxComponent} from './monitor-page/linux/linux.component';
import {WindowsComponent} from './monitor-page/windows/windows.component';
import {NgProgressModule} from 'ngx-progressbar';
import {TestPageComponent} from './test-page/test-page.component';
import {BlankPageComponent} from './blank-page/blank-page.component';
import {MaterialModule} from './MaterialModule.component';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import {NgxDatatableModule} from '@swimlane/ngx-datatable';
import {ElementTableComponent} from './elements/table/table.component';
import {SettingPageComponent} from './setting-page/setting-page.component';
import {ElementLeftbarComponent} from './elements/leftbar/leftbar.component';
import {ElementOfooterComponent} from './elements/ofooter/ofooter.component';
import {SettingPageBasicComponent} from './setting-page/basic/basic.component';
import {SettingPageEmailComponent} from './setting-page/email/email.component';
import {SettingPageLdapComponent} from './setting-page/ldap/ldap.component';
import {SettingPageTerminalComponent} from './setting-page/terminal/terminal.component';
import {SettingPageS3Component} from './setting-page/s3/s3.component';
import {TransPipe} from './trans.pipe';
import {MAT_LABEL_GLOBAL_OPTIONS} from '@angular/material'; import {MAT_LABEL_GLOBAL_OPTIONS} from '@angular/material';
import {ElementGuacamoleComponent} from './elements/guacamole/guacamole.component';
import {ConnectPageComponent} from './connect-page/connect-page.component';
import {Pipes} from './pipes/pipes';
import {PagesComponents} from './pages/pages.component';
import {ElementComponents} from './elements/elements.component';
import {ChangLanWarningDialogComponent} from './elements/nav/nav.component';
import {DialogService, ElementDialogAlertComponent} from './elements/dialog/dialog.service';
import {PluginModules} from './plugins/plugins';
import {TestPageComponent} from './test-page/test-page.component';
import {AssetTreeDialogComponent} from './elements/asset-tree/asset-tree.component';
@NgModule({ @NgModule({
imports: [ imports: [
BrowserModule, BrowserModule,
BrowserAnimationsModule,
FormsModule, FormsModule,
AppRoutingModule,
NgProgressModule,
HttpClientModule, HttpClientModule,
ReactiveFormsModule, ReactiveFormsModule,
MaterialModule, AppRouterModule,
LoggerModule.forRoot({serverLoggingUrl: '/api/logs', level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.ERROR}), ...PluginModules
NgxDatatableModule
], ],
declarations: [ declarations: [
AppComponent, AppComponent,
ElementNavComponent,
ElementFooterComponent,
ElementPopupComponent,
ElementTermComponent,
ElementInteractiveComponent,
ElementRdpComponent,
ElementServerMenuComponent,
ElementIframeComponent,
ElementDialogAlertComponent,
ElementTableComponent,
ElementLeftbarComponent,
ElementOfooterComponent,
ElementGuacamoleComponent,
ElementElfinderComponent,
LoginComponent,
SearchComponent,
SearchFilter,
IleftbarComponent,
CleftbarComponent, CleftbarDialogComponent,
ControlComponent,
ControlnavComponent,
ControlPageComponent,
IndexPageComponent,
NotFoundComponent,
ReplayPageComponent,
Mp4Component,
JsonComponent,
UtcDatePipe,
MonitorPageComponent,
LinuxComponent,
WindowsComponent,
TestPageComponent, TestPageComponent,
BlankPageComponent, ...Pipes,
SettingPageComponent, ...ElementComponents,
SettingPageBasicComponent, ...PagesComponents,
SettingPageEmailComponent,
SettingPageLdapComponent,
SettingPageTerminalComponent,
SettingPageS3Component,
TransPipe,
ConnectPageComponent,
], ],
entryComponents: [ entryComponents: [
CleftbarDialogComponent, AssetTreeDialogComponent,
ElementDialogAlertComponent, ElementDialogAlertComponent,
ChangLanWarningDialogComponent,
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
providers: [ providers: [
......
...@@ -14,7 +14,7 @@ import {DataStore, User, Browser, i18n} from './globals'; ...@@ -14,7 +14,7 @@ import {DataStore, User, Browser, i18n} from './globals';
import {environment} from '../environments/environment'; import {environment} from '../environments/environment';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {NGXLogger} from 'ngx-logger'; import {NGXLogger} from 'ngx-logger';
import {HostGroup} from './ControlPage/cleftbar/cleftbar.component'; import {HostGroup} from './pages/control/cleftbar/cleftbar.component';
import * as UUID from 'uuid-js/lib/uuid.js'; import * as UUID from 'uuid-js/lib/uuid.js';
declare function unescape(s: string): string; declare function unescape(s: string): string;
...@@ -126,12 +126,19 @@ export class HttpService { ...@@ -126,12 +126,19 @@ export class HttpService {
return this.http.get('/api/terminal/v1/sessions/' + token + '/replay'); return this.http.get('/api/terminal/v1/sessions/' + token + '/replay');
} }
get_replay_json(token: string) {
return this.http.get('/api/terminal/v2/sessions/' + token + '/replay');
}
get_replay_data(src: string) {
return this.http.get(src);
}
get_user_id_from_token(token: string) { get_user_id_from_token(token: string) {
const params = new HttpParams() const params = new HttpParams()
.set('user-only', '1') .set('user-only', '1')
.set('token', token); .set('token', token);
return this.http.get('/api/users/v1/connection-token/', {params: params}); return this.http.get('/api/users/v1/connection-token/', {params: params});
} }
} }
...@@ -246,6 +253,8 @@ export class AppService implements OnInit { ...@@ -246,6 +253,8 @@ export class AppService implements OnInit {
this._http.get('/luna/i18n/' + this.lang + '.json').subscribe( this._http.get('/luna/i18n/' + this.lang + '.json').subscribe(
data => { data => {
this._localStorage.set('lang', JSON.stringify(data)); this._localStorage.set('lang', JSON.stringify(data));
},
err => {
} }
); );
} }
...@@ -264,7 +273,7 @@ export class AppService implements OnInit { ...@@ -264,7 +273,7 @@ export class AppService implements OnInit {
checklogin() { checklogin() {
this._logger.log('service.ts:AppService,checklogin'); this._logger.log('service.ts:AppService,checklogin');
if (DataStore.Path) { if (DataStore.Path) {
if (DataStore.Path['name'] === 'FOF' || DataStore.Path['name'] === 'Forgot') { if (document.location.pathname === '/luna/connect') {
} else { } else {
if (User.logined) { if (User.logined) {
if (document.location.pathname === '/login') { if (document.location.pathname === '/login') {
...@@ -273,8 +282,6 @@ export class AppService implements OnInit { ...@@ -273,8 +282,6 @@ export class AppService implements OnInit {
this._router.navigate([document.location.pathname]); this._router.navigate([document.location.pathname]);
} }
// jQuery('angular2').show(); // jQuery('angular2').show();
} else if (document.location.pathname === '/luna/connect') {
User.logined = true;
} else { } else {
this._http.get_user_profile() this._http.get_user_profile()
.subscribe( .subscribe(
......
@import "~@swimlane/ngx-ui/release/components/tree/tree.component";
@import "~@swimlane/ngx-ui/release/components/icon/icon.component";
.ztree {
height: 100%;
background-color: inherit;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ElementAssetTreeComponent } from './asset-tree.component';
describe('R ', () => {
let component: ElementAssetTreeComponent;
let fixture: ComponentFixture<ElementAssetTreeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ElementAssetTreeComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ElementAssetTreeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, Input, OnInit, Inject, SimpleChanges, OnChanges} from '@angular/core';
import {NavList, View} from '../../pages/control/control/control.component';
import {AppService, LogService} from '../../app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
declare var $: any;
@Component({
selector: 'elements-asset-tree',
templateUrl: './asset-tree.component.html',
styleUrls: ['./asset-tree.component.scss']
})
export class ElementAssetTreeComponent implements OnInit, OnChanges {
@Input() Data: any;
@Input() query: string;
nodes = [];
setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
callback: {
onClick: this.onCzTreeOnClick.bind(this)
},
};
hiddenNodes: any;
onCzTreeOnClick(event, treeId, treeNode, clickFlag) {
this.Connect(treeNode);
}
constructor(private _appService: AppService,
public _dialog: MatDialog,
public _logger: LogService) {
}
ngOnInit() {
if (this.Data) {
this.draw();
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes['Data'] && this.Data) {
this.draw();
}
if (changes['query'] && !changes['query'].firstChange) {
this.filter();
}
}
draw() {
const nodes = {};
const assets = {};
this.Data.forEach((v, i) => {
if (!nodes[v['id']]) {
nodes[v['id']] = true;
this.nodes.push({
'id': v['id'],
'key': v['key'],
'name': v['name'],
'value': v['value'],
'pId': v['parent'],
'assets_amount': v['assets_amount'],
'isParent': true,
'open': v['key'] === '0'
});
}
v['assets_granted'].forEach((vv, ii) => {
vv['nodes'].forEach((vvv, iii) => {
if (!nodes[vvv['id']]) {
this.nodes.push({
'id': vvv['id'],
'key': vvv['key'],
'name': vvv['value'],
'value': vvv['value'],
'pId': vvv['parent'],
'assets_amount': vvv['assets_amount'],
'isParent': true,
'open': vvv['key'] === '0'
});
nodes[vvv['id']] = true;
}
if (!assets[vv['id'] + '@' + vvv['id']]) {
this.nodes.push({
'id': vv['id'],
'name': vv['hostname'],
'value': vv['hostname'],
'system_users_granted': vv['system_users_granted'],
'platform': vv['platform'],
'comment': vv['comment'],
'isParent': false,
'pId': vvv['id'],
'iconSkin': vv['platform'].toLowerCase()
});
assets[vv['id'] + '@' + vvv['id']] = true;
}
});
});
});
this.nodes.sort(function(node1, node2) {
if (node1.isParent && !node2.isParent) {
return -1;
}
return node1.name < node2.name ? -1 : 1;
});
$.fn.zTree.init($('#ztree'), this.setting, this.nodes);
}
Connect(host) {
// console.log(host);
let user: any;
if (host.system_users_granted.length > 1) {
user = this.checkPriority(host.system_users_granted);
if (user) {
this.login(host, user);
} else {
const dialogRef = this._dialog.open(AssetTreeDialogComponent, {
height: '200px',
width: '300px',
data: {users: host.system_users_granted}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (const i of host.system_users_granted) {
if (i.id.toString() === result.toString()) {
user = i;
break;
}
}
this.login(host, user);
}
});
}
} else if (host.system_users_granted.length === 1) {
user = host.system_users_granted[0];
this.login(host, user);
}
}
login(host, user) {
const id = NavList.List.length - 1;
this._logger.debug(NavList);
this._logger.debug(host);
if (user) {
NavList.List[id].nick = host.name;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].host = host;
NavList.List[id].user = user;
if (user.protocol === 'ssh') {
NavList.List[id].type = 'ssh';
} else if (user.protocol === 'rdp') {
NavList.List[id].type = 'rdp';
}
NavList.List.push(new View());
NavList.Active = id;
}
this._logger.debug(NavList);
}
checkPriority(sysUsers) {
let priority = -1;
let user: any;
for (const u of sysUsers) {
if (u.priority > priority) {
user = u;
priority = u.priority;
} else if (u.priority === priority) {
return null;
}
}
return user;
}
filter() {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
zTreeObj.showNodes(this.hiddenNodes);
function filterFunc(node) {
const _keywords = $('#keyword').val();
if (node.isParent || node.name.indexOf(_keywords) !== -1) {
return false;
}
return true;
}
this.hiddenNodes = zTreeObj.getNodesByFilter(filterFunc);
zTreeObj.hideNodes(this.hiddenNodes);
zTreeObj.expandAll(true);
}
}
@Component({
selector: 'elements-asset-tree-dialog',
templateUrl: 'dialog.html',
})
export class AssetTreeDialogComponent implements OnInit {
UserSelectControl = new FormControl('', [Validators.required]);
selected: any;
constructor(public dialogRef: MatDialogRef<AssetTreeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private _logger: LogService) {
}
ngOnInit() {
this.selected = this.data.users[0].id;
this.UserSelectControl.setValue(this.selected);
// this._logger.debug(this.UserSelectControl);
}
onNoClick(): void {
this.dialogRef.close();
}
compareFn: ((f1: any, f2: any) => boolean) | null = this.compareByValue;
compareByValue(f1: any, f2: any) {
return f1 && f2 && f1.value === f2.value;
}
}
...@@ -43,7 +43,7 @@ export class DialogService { ...@@ -43,7 +43,7 @@ export class DialogService {
@Component({ @Component({
selector: 'app-alert', selector: 'elements-alert',
templateUrl: 'alert.html', templateUrl: 'alert.html',
styleUrls: ['./alert.scss'] styleUrls: ['./alert.scss']
}) })
......
// Elements
import {ElementTableComponent} from './table/table.component';
import {ElementLeftbarComponent} from './leftbar/leftbar.component';
import {ElementOfooterComponent} from './ofooter/ofooter.component';
import {ElementFooterComponent} from './footer/footer.component';
import {ElementTermComponent} from './term/term.component';
import {ElementInteractiveComponent} from './interactive/interactive.component';
import {ChangLanWarningDialogComponent, ElementNavComponent} from './nav/nav.component';
import {ElementPopupComponent} from './popup/popup.component';
import {ElementRdpComponent} from './rdp/rdp.component';
import {ElementServerMenuComponent} from './server-menu/server-menu.component';
import {ElementIframeComponent} from './iframe/iframe.component';
import {ElementDialogAlertComponent} from './dialog/dialog.service';
import {ElementGuacamoleComponent} from './guacamole/guacamole.component';
import {ElementSshTermComponent} from './ssh-term/ssh-term.component';
import {AssetTreeDialogComponent, ElementAssetTreeComponent} from './asset-tree/asset-tree.component';
export const ElementComponents = [
ElementLeftbarComponent,
ElementOfooterComponent,
ElementTableComponent,
ElementFooterComponent,
ElementTermComponent,
ElementInteractiveComponent,
ElementNavComponent, ChangLanWarningDialogComponent,
ElementPopupComponent,
ElementRdpComponent,
ElementServerMenuComponent,
ElementIframeComponent,
ElementDialogAlertComponent,
ElementGuacamoleComponent,
ElementAssetTreeComponent,
ElementSshTermComponent,
AssetTreeDialogComponent
];
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-element-elfinder',
templateUrl: './elfinder.component.html',
styleUrls: ['./elfinder.component.scss']
})
export class ElementElfinderComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}
...@@ -10,7 +10,7 @@ import {DataStore, User} from '../../globals'; ...@@ -10,7 +10,7 @@ import {DataStore, User} from '../../globals';
import {version} from '../../../environments/environment'; import {version} from '../../../environments/environment';
@Component({ @Component({
selector: 'app-element-footer', selector: 'elements-footer',
templateUrl: './footer.component.html', templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css'] styleUrls: ['./footer.component.css']
}) })
......
<iframe #rdp [src]="trust(target)" (mouseenter)="active()"></iframe> <!--<iframe #rdp [src]="trust('https://inews.gtimg.com/newsapp_bt/0/3460971429/1000')" width="100%" height="100%" (mouseenter)="active()"></iframe>-->
<iframe #rdp [src]="trust(target)" width="100%" height="100%" (mouseenter)="active()"></iframe>
iframe { iframe {
width: 100%;
height: 100%;
border: none; border: none;
background-color: white; background-color: white;
} }
...@@ -4,10 +4,10 @@ import {HttpService, LogService} from '../../app.service'; ...@@ -4,10 +4,10 @@ import {HttpService, LogService} from '../../app.service';
import {DataStore, User} from '../../globals'; import {DataStore, User} from '../../globals';
import {DomSanitizer} from '@angular/platform-browser'; import {DomSanitizer} from '@angular/platform-browser';
import {environment} from '../../../environments/environment'; import {environment} from '../../../environments/environment';
import {NavList} from '../../ControlPage/control/control.component'; import {NavList} from '../../pages/control/control/control.component';
@Component({ @Component({
selector: 'app-element-guacamole', selector: 'elements-guacamole',
templateUrl: './guacamole.component.html', templateUrl: './guacamole.component.html',
styleUrls: ['./guacamole.component.scss'] styleUrls: ['./guacamole.component.scss']
}) })
...@@ -63,7 +63,6 @@ export class ElementGuacamoleComponent implements OnInit { ...@@ -63,7 +63,6 @@ export class ElementGuacamoleComponent implements OnInit {
this.target = this._cookie.get('guacamole'); this.target = this._cookie.get('guacamole');
} }
} }
NavList.List[this.index].Rdp = this.el.nativeElement; NavList.List[this.index].Rdp = this.el.nativeElement;
} }
...@@ -76,7 +75,6 @@ export class ElementGuacamoleComponent implements OnInit { ...@@ -76,7 +75,6 @@ export class ElementGuacamoleComponent implements OnInit {
} }
active() { active() {
this._logger.debug('focus');
this.el.nativeElement.focus(); this.el.nativeElement.focus();
} }
......
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnInit} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser'; import {DomSanitizer} from '@angular/platform-browser';
import {NavList} from '../../ControlPage/control/control.component'; import {NavList} from '../../pages/control/control/control.component';
import {User, DataStore} from '../../globals'; import {User, DataStore} from '../../globals';
import {HttpService, LogService} from '../../app.service'; import {HttpService, LogService} from '../../app.service';
...@@ -8,7 +8,7 @@ import {environment} from '../../../environments/environment'; ...@@ -8,7 +8,7 @@ import {environment} from '../../../environments/environment';
import {CookieService} from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
@Component({ @Component({
selector: 'app-element-iframe', selector: 'elements-iframe',
templateUrl: './iframe.component.html', templateUrl: './iframe.component.html',
styleUrls: ['./iframe.component.scss'] styleUrls: ['./iframe.component.scss']
}) })
......
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'app-element-interactive', selector: 'elements-interactive',
templateUrl: './interactive.component.html', templateUrl: './interactive.component.html',
styleUrls: ['./interactive.component.scss'] styleUrls: ['./interactive.component.scss']
}) })
......
...@@ -3,7 +3,7 @@ import {FormBuilder, FormGroup} from '@angular/forms'; ...@@ -3,7 +3,7 @@ import {FormBuilder, FormGroup} from '@angular/forms';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
@Component({ @Component({
selector: 'app-element-leftbar', selector: 'elements-leftbar',
templateUrl: './leftbar.component.html', templateUrl: './leftbar.component.html',
styleUrls: ['./leftbar.component.scss'] styleUrls: ['./leftbar.component.scss']
}) })
......
<h1 mat-dialog-title>{{data.title}}</h1>
<h3>
{{data.note}}
</h3>
<div style="float: right">
<button mat-raised-button (click)="onNoClick()">{{data.cancel}}</button>
<button mat-raised-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>{{data.confirm}}</button>
</div>
...@@ -5,30 +5,34 @@ ...@@ -5,30 +5,34 @@
* @date 2017-11-07 * @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com> * @author liuzheng <liuzheng712@gmail.com>
*/ */
import {Component, OnInit} from '@angular/core'; import {Component, Inject, OnInit} from '@angular/core';
import {AppService, HttpService, LocalStorageService, LogService} from '../../app.service'; import {AppService, HttpService, LocalStorageService, LogService} from '../../app.service';
import {CleftbarComponent} from '../../ControlPage/cleftbar/cleftbar.component'; import {CleftbarComponent} from '../../pages/control/cleftbar/cleftbar.component';
import {ControlComponent, NavList} from '../../ControlPage/control/control.component'; import {ControlComponent, NavList, View} from '../../pages/control/control/control.component';
import {DataStore, i18n} from '../../globals'; import {DataStore, i18n} from '../../globals';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
// import * as layer from 'layui-layer/src/layer.js'; // import * as layer from 'layui-layer/src/layer.js';
declare let layer: any; declare let layer: any;
@Component({ @Component({
selector: 'app-element-nav', selector: 'elements-nav',
templateUrl: './nav.component.html', templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css'], styleUrls: ['./nav.component.css'],
}) })
export class ElementNavComponent implements OnInit { export class ElementNavComponent implements OnInit {
DataStore = DataStore; DataStore = DataStore;
ChangeLanWarningDialog: any;
static Hide() { static Hide() {
jQuery('app-element-nav').hide(); jQuery('elements-nav').hide();
} }
constructor(private _appService: AppService, constructor(private _appService: AppService,
private _http: HttpService, private _http: HttpService,
private _logger: LogService, private _logger: LogService,
public _dialog: MatDialog,
private _localStorage: LocalStorageService) { private _localStorage: LocalStorageService) {
this._logger.log('nav.ts:NavComponent'); this._logger.log('nav.ts:NavComponent');
this.getnav(); this.getnav();
...@@ -49,6 +53,10 @@ export class ElementNavComponent implements OnInit { ...@@ -49,6 +53,10 @@ export class ElementNavComponent implements OnInit {
CleftbarComponent.Hide(); CleftbarComponent.Hide();
break; break;
} }
case 'Settings': {
this.Settings();
break;
}
case 'ShowLeft': { case 'ShowLeft': {
CleftbarComponent.Show(); CleftbarComponent.Show();
break; break;
...@@ -95,11 +103,43 @@ export class ElementNavComponent implements OnInit { ...@@ -95,11 +103,43 @@ export class ElementNavComponent implements OnInit {
break; break;
} }
case 'English': { case 'English': {
const dialog = this._dialog.open(
ChangLanWarningDialogComponent,
{
height: '200px',
width: '300px',
data: {
title: 'Warning',
note: 'The page will be reload, can you acceptable?',
cancel: 'Cancel',
confirm: 'Confirm',
},
});
dialog.afterClosed().subscribe(result => {
if (result) {
this.English(); this.English();
}
});
break; break;
} }
case 'Chinese': { case 'Chinese': {
const dialog = this._dialog.open(
ChangLanWarningDialogComponent,
{
height: '200px',
width: '300px',
data: {
title: '警告',
note: '此页将被重载,是否确认?',
cancel: '取消',
confirm: '确认',
},
});
dialog.afterClosed().subscribe(result => {
if (result) {
this.Language('cn'); this.Language('cn');
}
});
break; break;
} }
default: { default: {
...@@ -217,10 +257,9 @@ export class ElementNavComponent implements OnInit { ...@@ -217,10 +257,9 @@ export class ElementNavComponent implements OnInit {
'disable': true 'disable': true
}, },
{ {
'id': 'Language', 'id': 'Settings',
'href': '', 'click': 'Settings',
'name': 'Language', 'name': 'Settings'
'disable': true
}] }]
}, { }, {
'id': 'Help', 'id': 'Help',
...@@ -303,4 +342,34 @@ export class ElementNavComponent implements OnInit { ...@@ -303,4 +342,34 @@ export class ElementNavComponent implements OnInit {
} }
location.reload(); location.reload();
} }
Settings() {
const id = NavList.List.length - 1;
NavList.List[id].nick = 'Setting';
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].type = 'settings';
NavList.List.push(new View());
NavList.Active = id;
}
}
@Component({
selector: 'elements-nav-dialog',
templateUrl: 'changeLanWarning.html',
})
export class ChangLanWarningDialogComponent implements OnInit {
constructor(public dialogRef: MatDialogRef<ChangLanWarningDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) {
}
ngOnInit() {
}
onNoClick(): void {
this.dialogRef.close();
}
} }
...@@ -3,7 +3,7 @@ import {version} from '../../../environments/environment'; ...@@ -3,7 +3,7 @@ import {version} from '../../../environments/environment';
import {DataStore} from '../../globals'; import {DataStore} from '../../globals';
@Component({ @Component({
selector: 'app-element-ofooter', selector: 'elements-ofooter',
templateUrl: './ofooter.component.html', templateUrl: './ofooter.component.html',
styleUrls: ['./ofooter.component.scss'] styleUrls: ['./ofooter.component.scss']
}) })
......
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'app-element-popup', selector: 'elements-popup',
templateUrl: './popup.component.html', templateUrl: './popup.component.html',
styleUrls: ['./popup.component.css'] styleUrls: ['./popup.component.css']
}) })
......
...@@ -11,7 +11,7 @@ import {DataStore} from '../../globals'; ...@@ -11,7 +11,7 @@ import {DataStore} from '../../globals';
declare let Mstsc: any; declare let Mstsc: any;
@Component({ @Component({
selector: 'app-element-rdp', selector: 'elements-rdp',
templateUrl: './rdp.component.html', templateUrl: './rdp.component.html',
styleUrls: ['./rdp.component.scss'] styleUrls: ['./rdp.component.scss']
}) })
......
...@@ -9,7 +9,7 @@ export class Menu { ...@@ -9,7 +9,7 @@ export class Menu {
} }
@Component({ @Component({
selector: 'app-element-server-menu', selector: 'elements-server-menu',
templateUrl: './server-menu.component.html', templateUrl: './server-menu.component.html',
styleUrls: ['./server-menu.component.scss'], styleUrls: ['./server-menu.component.scss'],
}) })
......
<elements-term (winSizeChangeTrigger)="changeWinSize($event)" [term]="term"></elements-term>
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { WindowsComponent } from './windows.component'; import { SshTermComponent } from './ssh-term.component';
describe('WindowsComponent', () => { describe('SshTermComponent', () => {
let component: WindowsComponent; let component: SshTermComponent;
let fixture: ComponentFixture<WindowsComponent>; let fixture: ComponentFixture<SshTermComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ WindowsComponent ] declarations: [ SshTermComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(WindowsComponent); fixture = TestBed.createComponent(SshTermComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
import {AfterViewInit, Component, Input, OnInit } from '@angular/core';
import * as io from 'socket.io-client';
// import {ws} from '../../globals';
import * as Terminal from 'xterm/dist/xterm';
import {NavList} from '../../pages/control/control/control.component';
import {UUIDService} from '../../app.service';
const ws = io.connect('/ssh');
@Component({
selector: 'elements-ssh-term',
templateUrl: './ssh-term.component.html',
styleUrls: ['./ssh-term.component.scss']
})
export class ElementSshTermComponent implements OnInit, AfterViewInit {
@Input() host: any;
@Input() userid: any;
@Input() index: number;
@Input() token: string;
term: Terminal;
secret: string;
constructor(private _uuid: UUIDService) {
}
ngOnInit() {
this.secret = this._uuid.gen();
this.term = new Terminal({
// cols: 80,
// rows: 24,
useStyle: true,
screenKeys: true,
});
}
ngAfterViewInit() {
this.joinRoom();
}
changeWinSize(size: Array<number>) {
ws.emit('resize', {'cols': size[0], 'rows': size[1]});
}
joinRoom() {
NavList.List[this.index].Term = this.term;
if (this.host) {
ws.emit('host', {'uuid': this.host.id, 'userid': this.userid, 'secret': this.secret});
}
if (this.token) {
ws.emit('token', {'token': this.token, 'secret': this.secret});
}
const that = this;
this.term.on('data', function (data) {
ws.emit('data', {'data': data, 'room': NavList.List[that.index].room});
});
ws.on('data', data => {
if (data['room'] === NavList.List[that.index].room) {
that.term.write(data['data']);
}
});
ws.on('disconnect', () => {
that.disconnect();
});
ws.on('logout', (data) => {
if (data['room'] === NavList.List[that.index].room) {
NavList.List[that.index].connected = false;
// this.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
});
ws.on('room', data => {
console.log('Compile secret: ', data['secret'], this.secret);
if (data['secret'] === this.secret) {
console.log('Set room: ', data['room']);
NavList.List[that.index].room = data['room'];
console.log('get', that.index, 'room: ', NavList.List[that.index].room);
}
});
}
disconnect() {
NavList.List[this.index].connected = false;
// this.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
ws.emit('logout', NavList.List[this.index].room);
}
active() {
this.term.focus();
}
}
...@@ -28,7 +28,7 @@ export let Config: { ...@@ -28,7 +28,7 @@ export let Config: {
}; };
@Component({ @Component({
selector: 'app-element-table', selector: 'elements-table',
templateUrl: './table.component.html', templateUrl: './table.component.html',
styleUrls: ['./table.component.scss'] styleUrls: ['./table.component.scss']
}) })
......
...@@ -2,19 +2,33 @@ ...@@ -2,19 +2,33 @@
div { div {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 15px; /*padding: 15px 0 15px 15px;*/
} }
.terminal { .terminal .xterm-rows {
border: #000 solid 5px; border: #000 solid 1px;
color: #f0f0f0; color: #f0f0f0;
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px; box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
white-space: nowrap; white-space: nowrap;
display: inline-block; display: inline-block;
height: 100%; height: 100%;
font-size: 11px;
*padding: 15px 0 15px 15px;;
} }
div.terminal div span { div.terminal div span {
min-width: 12px; min-width: 12px;
} }
.terminal div {
user-select: text;
}
/*.terminal, .terminal .xterm-viewport {*/
/*background-color: inherit;*/
/*}*/
.terminal .xterm-rows {
background-color: #1f1b1b;
}
<div #term></div> <div style="padding: 15px">
<div #term (mouseenter)="active()"></div>
</div>
import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core'; import {AfterViewInit, Component, Input, Output, OnInit, ViewChild, EventEmitter} from '@angular/core';
import {ElementRef} from '@angular/core'; import {ElementRef} from '@angular/core';
import {term, Terminal, TermWS} from '../../globals'; import * as Terminal from 'xterm/dist/xterm';
import {NavList} from '../../ControlPage/control/control.component'; // import { Terminal } from 'xterm';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as $ from 'jquery/dist/jquery.min.js';
import {UUIDService} from '../../app.service'; import {Observable} from 'rxjs/Rx';
import {CookieService} from 'ngx-cookie-service'; import 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
@Component({ @Component({
selector: 'app-element-term', selector: 'elements-term',
templateUrl: './term.component.html', templateUrl: './term.component.html',
styleUrls: ['./term.component.css'] styleUrls: ['./term.component.css']
}) })
export class ElementTermComponent implements OnInit, AfterViewInit { export class ElementTermComponent implements OnInit, AfterViewInit {
@Input() host: any;
@Input() userid: any;
@Input() index: number;
@Input() token: string;
@Input() monitor: string;
@ViewChild('term') el: ElementRef; @ViewChild('term') el: ElementRef;
secret: string; @Input() term: Terminal;
term: any; @Output() winSizeChangeTrigger = new EventEmitter<Array<number>>();
col = 80;
row = 24;
winSizeChange$: Observable<any>;
constructor(private _uuid: UUIDService, constructor() {
private _cookie: CookieService) {
} }
ngOnInit() { ngOnInit() {
this.secret = this._uuid.gen(); this.winSizeChange$ = Observable.fromEvent(window, 'resize')
this.term = Terminal({ .debounceTime(500)
cols: 80, .distinctUntilChanged();
rows: 24,
useStyle: true, this.winSizeChange$
screenKeys: true, .subscribe(() => this.resizeTerm());
});
// NavList.List[this.index].room = this.room;
} }
ngAfterViewInit() { ngAfterViewInit() {
if (this.host || this.token) {
if (this._cookie.get('cols')) {
term.col = parseInt(this._cookie.get('cols'), 10);
}
if (this._cookie.get('rows')) {
term.row = parseInt(this._cookie.get('rows'), 10);
}
} else {
term.col = Math.floor(jQuery(this.el.nativeElement).width() / jQuery('#liuzheng').width() * 8) - 3;
term.row = Math.floor(jQuery(this.el.nativeElement).height() / jQuery('#liuzheng').height()) - 3;
term.term = this.term;
}
this.term.open(this.el.nativeElement, true); this.term.open(this.el.nativeElement, true);
const that = this; this.resizeTerm();
window.onresize = function () {
term.col = Math.floor(jQuery(that.el.nativeElement).width() / jQuery('#liuzheng').width() * 8) - 3;
term.row = Math.floor(jQuery(that.el.nativeElement).height() / jQuery('#liuzheng').height()) - 3;
if (term.col < 80) {
term.col = 80;
}
if (term.row < 24) {
term.row = 24;
} }
that.term.resize(term.col, term.row);
if (that.host) {
that._cookie.set('cols', term.col.toString(), 99, '/', document.domain);
that._cookie.set('rows', term.row.toString(), 99, '/', document.domain);
TermWS.emit('resize', {'cols': term.col, 'rows': term.row});
}
};
jQuery(window).resize();
NavList.List[this.index].Term = this.term; resizeTerm() {
if (this.host) { let contentElement = $('.window.active');
TermWS.emit('host', {'uuid': this.host.id, 'userid': this.userid, 'secret': this.secret}); if (contentElement.length === 0) {
} contentElement = $('body');
if (this.token) {
TermWS.emit('token', {'token': this.token, 'secret': this.secret});
}
if (this.monitor) {
TermWS.emit('monitor', {'token': this.monitor, 'secret': this.secret});
} else {
this.term.on('data', function (data) {
TermWS.emit('data', {'data': data, 'room': NavList.List[that.index].room});
});
}
TermWS.on('data', function (data) {
if (data['room'] === NavList.List[that.index].room) {
that.term.write(data['data']);
}
});
TermWS.on('disconnect', function () {
that.TerminalDisconnect();
});
TermWS.on('logout', function (data) {
if (data['room'] === NavList.List[that.index].room) {
NavList.List[this.index].connected = false;
// this.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
});
TermWS.on('room', function (data) {
if (data['secret'] === that.secret) {
NavList.List[that.index].room = data['room'];
} }
}); const markerElement = $('#marker');
const col = Math.floor((contentElement.width() - 30) / markerElement.width() * 6) - 1;
const row = Math.floor((contentElement.height() - 30) / markerElement.height());
this.col = col > 80 ? col : 80;
this.row = row > 24 ? row : 24;
console.log('Box size: ', contentElement.width(), '*', contentElement.height());
console.log('Mark size: ', markerElement.width(), '*', markerElement.height());
console.log('Resize term size: ', this.col, this.row);
this.term.resize(this.col, this.row);
this.winSizeChangeTrigger.emit([this.col, this.row]);
} }
TerminalDisconnect() { active() {
NavList.List[this.index].connected = false; this.term.focus();
// this.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
TermWS.emit('logout', NavList.List[this.index].room);
} }
} }
<div *ngFor="let node of TreeData">
<div *ngIf="node.leafs?.length>0">
{{node.id}}
<elements-tree [TreeData]="node.leafs"></elements-tree>
</div>
<div *ngIf="node.leafs?.length==0">
{{node.id}}
</div>
</div>
<!--<ul class="filetree ">-->
<!--<li *ngFor="let hostGroup of HostGroups | SearchFilter: q; let i = index ">-->
<!--<input type="checkbox" id="hostgroup-{{i}}">-->
<!--<label for="hostgroup-{{i}}" matTooltip="{{hostGroup.name}}" [matTooltipPosition]="TooltipPosition">{{hostGroup.name}}</label>-->
<!--</li>-->
<!--</ul>-->
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LinuxComponent } from './linux.component'; import { ElementTreeComponent } from './tree.component';
describe('LinuxComponent', () => { describe('R ', () => {
let component: LinuxComponent; let component: ElementTreeComponent;
let fixture: ComponentFixture<LinuxComponent>; let fixture: ComponentFixture<ElementTreeComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ LinuxComponent ] declarations: [ ElementTreeComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(LinuxComponent); fixture = TestBed.createComponent(ElementTreeComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
import {Component, Input, OnInit} from '@angular/core';
import {HttpService} from '../../app.service';
export interface Assets {
name: string;
id: string;
type: string;
}
export interface Groups {
id: string;
key: string;
name: string;
value: string;
parent: string;
assets_granted: Array<Assets>;
}
export class TreeStruct {
id: string;
leafs: Array<TreeStruct>;
static create(id, parent: string) {
const tmp = new TreeStruct();
tmp.id = id;
tmp.leafs = [];
return tmp;
}
}
@Component({
selector: 'elements-tree',
templateUrl: './tree.component.html',
styleUrls: ['./tree.component.scss']
})
export class ElementTreeComponent implements OnInit {
@Input() TreeData: Array<TreeStruct>;
constructor(private _http: HttpService) {
}
ngOnInit() {
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {ElementTreeComponent} from './tree.component';
@NgModule({
imports: [
CommonModule
],
declarations: [
ElementTreeComponent
]
})
export class TreeModule {
}
...@@ -17,6 +17,7 @@ export let term: { ...@@ -17,6 +17,7 @@ export let term: {
rows: 24, rows: 24,
useStyle: true, useStyle: true,
screenKeys: true, screenKeys: true,
scrollback: 10
}), }),
col: 80, col: 80,
row: 24, row: 24,
...@@ -26,13 +27,17 @@ export let Video: { ...@@ -26,13 +27,17 @@ export let Video: {
id: string, id: string,
src: string, src: string,
type: string, type: string,
height: number,
width: number,
json: object; json: object;
timelist: Array<number>; timelist: Array<number>;
totalTime: number; totalTime: number;
} = { } = {
id: 'sss', id: '',
src: 'sss', src: '',
type: 'json', type: '',
width: 0,
height: 0,
json: {}, json: {},
timelist: [], timelist: [],
totalTime: 0, totalTime: 0,
......
<app-element-term
[index]="0"
[monitor]="token"
></app-element-term>
<app-monitor-linux></app-monitor-linux>
app-element-nav { elements-nav {
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
font-size: 13px; font-size: 13px;
font-weight: 300; font-weight: 300;
...@@ -22,7 +22,7 @@ nav { ...@@ -22,7 +22,7 @@ nav {
width: 100%; width: 100%;
} }
app-element-interactive { elements-interactive {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
bottom: 120px; bottom: 120px;
......
<ng-progress></ng-progress> <ng-progress></ng-progress>
<app-element-nav *ngIf="DataStore.NavShow"></app-element-nav> <elements-nav *ngIf="DataStore.NavShow"></elements-nav>
<router-outlet></router-outlet> <router-outlet></router-outlet>
<!--<app-element-interactive></app-element-interactive>--> <!--<elements-interactive></elements-interactive>-->
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* @author liuzheng <liuzheng712@gmail.com> * @author liuzheng <liuzheng712@gmail.com>
*/ */
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {DataStore} from './globals'; import {DataStore} from '../globals';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
......
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BlankPageComponent } from './blank-page.component'; import { PagesBlankComponent } from './blank.component';
describe('BlankPageComponent', () => { describe('PagesBlankComponent', () => {
let component: BlankPageComponent; let component: PagesBlankComponent;
let fixture: ComponentFixture<BlankPageComponent>; let fixture: ComponentFixture<PagesBlankComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ BlankPageComponent ] declarations: [ PagesBlankComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(BlankPageComponent); fixture = TestBed.createComponent(PagesBlankComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {DataStore} from '../globals'; import {DataStore} from '../../globals';
@Component({ @Component({
selector: 'app-blank-page', selector: 'pages-blank',
templateUrl: './blank-page.component.html', templateUrl: './blank.component.html',
styleUrls: ['./blank-page.component.scss'] styleUrls: ['./blank.component.scss']
}) })
export class BlankPageComponent implements OnInit { export class PagesBlankComponent implements OnInit {
constructor() { constructor() {
DataStore.NavShow = false; DataStore.NavShow = false;
......
<app-element-term <elements-ssh-term
[token]="token" [token]="token"
[index]="0" [index]="0"
*ngIf="system =='linux'"> *ngIf="system =='linux'">
</app-element-term> </elements-ssh-term>
<app-element-guacamole <elements-guacamole
[target]="target" [target]="target"
[index]="0" [index]="0"
*ngIf="system=='windows' && target"> *ngIf="system=='windows' && target">
</app-element-guacamole> </elements-guacamole>
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ConnectPageComponent } from './connect-page.component'; import { PagesConnectComponent } from './connect.component';
describe('ConnectPageComponent', () => { describe('ConnectPageComponent', () => {
let component: ConnectPageComponent; let component: PagesConnectComponent;
let fixture: ComponentFixture<ConnectPageComponent>; let fixture: ComponentFixture<PagesConnectComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ ConnectPageComponent ] declarations: [ PagesConnectComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ConnectPageComponent); fixture = TestBed.createComponent(PagesConnectComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {AppService, HttpService, LocalStorageService} from '../app.service'; import {AppService, HttpService, LocalStorageService} from '../../app.service';
import {DataStore} from '../globals'; import {DataStore} from '../../globals';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({ @Component({
selector: 'app-connect-page', selector: 'pages-connect',
templateUrl: './connect-page.component.html', templateUrl: './connect.component.html',
styleUrls: ['./connect-page.component.scss'] styleUrls: ['./connect.component.scss']
}) })
export class ConnectPageComponent implements OnInit { export class PagesConnectComponent implements OnInit {
token: string; token: string;
system: string; system: string;
authToken: string; authToken: string;
......
<div class="sidebar" fxLayout="column" ngxSplit="column">
<div fxflex="0 0 30px" class="search">
<input id="keyword" class="left-search" placeholder=" {{'Search'| trans }} ..." maxlength="2048" name="q"
autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" autofocus [(ngModel)]="q" (keyup.enter)="Search(q)">
</div>
<div class="overflow ngx-scroll-overlay" fxflex="1 1 90%">
<elements-asset-tree [Data]="zNodes" [query]="q"></elements-asset-tree>
</div>
<div class="footer-version" fxflex="0 0 26px">
<p> Version <strong>{{version}}</strong></p>
</div>
</div>
<!--<elements-server-menu></elements-server-menu>-->
.sidebar {
height: 100%;
width: 100%;
overflow: auto;
}
:root { :root {
font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif; font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
} }
...@@ -8,6 +14,7 @@ label { ...@@ -8,6 +14,7 @@ label {
.filetree { .filetree {
padding-left: 20px; padding-left: 20px;
height: inherit;
} }
.filetree input[type="checkbox"] { .filetree input[type="checkbox"] {
...@@ -52,36 +59,84 @@ label { ...@@ -52,36 +59,84 @@ label {
display: inline-block; display: inline-block;
} }
.search { .fa.fa-undefined:before {
border-left-width: 0; content: "\f26c";
border-bottom: #19aa8d 2px inset;
} }
.left-search { .left-search {
padding-left: 14px; padding-left: 14px;
width: 100%; width: 100%;
border: none; border: none;
height: 28px;
background: #2f2a2a; background: #2f2a2a;
color: #ffffff; color: #ffffff;
} }
.footer { .search {
border-left-width: 0;
border-bottom: #19aa8d 2px inset;
//padding-top: 30px;
width: 100%;
//position: fixed;
//height: 28px;
}
.search > input {
height: 30px;
}
.overflow {
height: 100%;
width: 100%;
float: left;
position: inherit;
background: #2f2a2a;
color: #d6cbcb;
}
.footer-version {
background: #2f2a2a; background: #2f2a2a;
font-size: 9pt; font-size: 9pt;
border-top-width: 1px;
left: 0; left: 0;
width: 100%;
padding: 1px 20px 0 20px; padding: 1px 20px 0 20px;
position: absolute; border-top: 1px solid #e7eaec;
bottom: 0;
//height: 30px;
//position: fixed;
} }
.fa.fa-undefined:before { .footer-version > p {
content: "\f26c"; height: 8px;
padding-top: 2px;
padding-bottom: 2px;
} }
.overflow { //@import "~@swimlane/ngx-ui/release/styles/components/scrollbars";
height: 100%;
overflow-y: scroll; .ngx-scroll-overlay {
position: absolute; overflow: auto; // for FF
width: 100%; //-ms-overflow-style: -ms-autohiding-scrollbar;
//
//&::-webkit-scrollbar {
// display: none;
//}
//
//&:hover::-webkit-scrollbar {
// display: initial;
//}
} }
//.sidebar::-webkit-scrollbar-track {
// -webkit-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.3);
// background-color: #676a6c;
//}
//
//.sidebar::-webkit-scrollbar {
// width: 8px;
//}
//
//.sidebar::-webkit-scrollbar-thumb {
// background-color: #F5F5F5;
// border-radius: 6px;
// border: 2px solid transparent;
//}
...@@ -8,16 +8,15 @@ ...@@ -8,16 +8,15 @@
*/ */
import {Component, Inject, OnInit} from '@angular/core'; import {Component, Inject, OnInit} from '@angular/core';
import {AppService, HttpService, LogService} from '../../app.service'; import {AppService, HttpService, LogService} from '../../../app.service';
import {SearchComponent} from '../search/search.component'; import {SearchComponent} from '../search/search.component';
import {DataStore} from '../../globals'; import {DataStore} from '../../../globals';
import {version} from '../../../environments/environment'; import {version} from '../../../../environments/environment';
import {ElementServerMenuComponent} from '../../elements/server-menu/server-menu.component';
import {NavList, View} from '../control/control.component'; import {NavList, View} from '../control/control.component';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material'; import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms'; import {FormControl, Validators} from '@angular/forms';
import {DialogService} from '../../elements/dialog/dialog.service'; import {ElementServerMenuComponent} from '../../../elements/server-menu/server-menu.component';
import {DialogService} from '../../../elements/dialog/dialog.service';
export interface HostGroup { export interface HostGroup {
name: string; name: string;
...@@ -32,14 +31,15 @@ export class Host { ...@@ -32,14 +31,15 @@ export class Host {
} }
@Component({ @Component({
selector: 'app-cleftbar', selector: 'pages-control-cleftbar',
templateUrl: './cleftbar.component.html', templateUrl: './cleftbar.component.html',
styleUrls: ['./cleftbar.component.css'], styleUrls: ['./cleftbar.component.scss'],
providers: [SearchComponent, ElementServerMenuComponent] providers: [SearchComponent, ElementServerMenuComponent]
}) })
export class CleftbarComponent implements OnInit { export class CleftbarComponent implements OnInit {
DataStore = DataStore; DataStore = DataStore;
HostGroups: Array<HostGroup>; HostGroups: Array<HostGroup>;
zNodes: any;
version = version; version = version;
q: string; q: string;
event: MouseEvent; event: MouseEvent;
...@@ -94,110 +94,14 @@ export class CleftbarComponent implements OnInit { ...@@ -94,110 +94,14 @@ export class CleftbarComponent implements OnInit {
ngOnInit() { ngOnInit() {
this._http.get_my_asset_groups_assets() this._http.get_my_asset_groups_assets()
.subscribe(response => { .subscribe(response => {
this.HostGroups = response; this.zNodes = response;
if (!DataStore.autologin) { // this.HostGroups = response;
this.autologin(); // if (!DataStore.autologin) {
} // this.autologin();
// }
}); });
} }
autologin() {
const asset_id = this._appService.getQueryString('asset_id');
const user_id = this._appService.getQueryString('user_id');
let tag = false;
if (asset_id) {
for (let g of this.HostGroups) {
if (g['assets_granted']) {
for (let host of g['assets_granted']) {
if (host.id.toString() === asset_id) {
if (user_id) {
host['system_users_granted'].forEach((user, kk) => {
if (user.id.toString() === user_id.toString()) {
this.login(host, user);
tag = true;
return;
}
});
} else {
this.Connect(host);
tag = true;
return;
}
}
}
}
}
if (!tag) {
this._layer.alert('Maybe you do not have permission on that host');
}
}
DataStore.autologin = true;
}
Connect(host) {
// console.log(host);
let user: any;
if (host.system_users_granted.length > 1) {
user = this.checkPriority(host.system_users_granted);
if (user) {
this.login(host, user);
} else {
const dialogRef = this._dialog.open(CleftbarDialogComponent, {
height: '200px',
width: '300px',
data: {users: host.system_users_granted}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (let i of host.system_users_granted) {
if (i.id.toString() === result.toString()) {
user = i;
break;
}
}
this.login(host, user);
}
});
}
} else if (host.system_users_granted.length === 1) {
user = host.system_users_granted[0];
this.login(host, user);
}
}
login(host, user) {
const id = NavList.List.length - 1;
if (user) {
NavList.List[id].nick = host.hostname;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].host = host;
NavList.List[id].user = user;
if (user.protocol === 'ssh') {
NavList.List[id].type = 'ssh';
} else if (user.protocol === 'rdp') {
NavList.List[id].type = 'rdp';
}
NavList.List.push(new View());
NavList.Active = id;
}
}
checkPriority(sysUsers) {
let priority = -1;
let user: any;
for (const u of sysUsers) {
if (u.priority > priority) {
user = u;
priority = u.priority;
} else if (u.priority === priority) {
return null;
}
}
return user;
}
Search(q) { Search(q) {
this._search.Search(q); this._search.Search(q);
...@@ -210,34 +114,3 @@ export class CleftbarComponent implements OnInit { ...@@ -210,34 +114,3 @@ export class CleftbarComponent implements OnInit {
// this._menu.contextmenu(this.clientY, this.clientX); // this._menu.contextmenu(this.clientY, this.clientX);
} }
} }
@Component({
selector: 'app-cleftbar-dialog',
templateUrl: 'dialog.html',
})
export class CleftbarDialogComponent implements OnInit {
UserSelectControl = new FormControl('', [Validators.required]);
selected: any;
constructor(public dialogRef: MatDialogRef<CleftbarDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private _logger: LogService) {
}
ngOnInit() {
this.selected = this.data.users[0].id;
this.UserSelectControl.setValue(this.selected);
// this._logger.debug(this.UserSelectControl);
}
onNoClick(): void {
this.dialogRef.close();
}
compareFn: ((f1: any, f2: any) => boolean) | null = this.compareByValue;
compareByValue(f1: any, f2: any) {
return f1 && f2 && f1.value === f2.value;
}
}
div, term-leftbar, term-body { div {
height: 100%; height: 100%;
width: 100%; width: 100%;
padding: 0; padding: 0;
} background-color: #1f1b1b;
div {
background-color: black;
margin: 0; margin: 0;
padding-top: 30px;
position: initial; position: initial;
} }
app-cleftbar { pages-control-cleftbar, pages-control-control {
padding: 0;
background: #2f2a2a; background: #2f2a2a;
color: #d6cbcb; color: #d6cbcb;
} }
app-control { .container-fluid {
padding: 0; padding-top: 30px;
} }
<div class="container-fluid row" fxLayout="row" ngxSplit="row">
<div fxFlex="1 1 20%" minBasis="100px" maxBasis="800px" fxFlexFill ngxSplitArea *ngIf="DataStore.leftbarshow">
<pages-control-cleftbar></pages-control-cleftbar>
</div>
<div fxFlex="0" ngxSplitHandle></div>
<div fxFlex="1 1 80%" ngxSplitArea class="content">
<pages-control-control></pages-control-control>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlPageComponent } from './controlpage.component'; import { PagesControlComponent } from './control.component';
describe('ControlPageComponent', () => { describe('ControlPageComponent', () => {
let component: ControlPageComponent; let component: PagesControlComponent;
let fixture: ComponentFixture<ControlPageComponent>; let fixture: ComponentFixture<PagesControlComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ ControlPageComponent ] declarations: [ PagesControlComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ControlPageComponent); fixture = TestBed.createComponent(PagesControlComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
...@@ -6,15 +6,15 @@ ...@@ -6,15 +6,15 @@
* @author liuzheng <liuzheng712@gmail.com> * @author liuzheng <liuzheng712@gmail.com>
*/ */
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {AppService} from '../app.service'; import {AppService} from '../../app.service';
import {DataStore, User} from '../globals'; import {DataStore, User} from '../../globals';
@Component({ @Component({
selector: 'app-controllpage', selector: 'pages-control',
templateUrl: './controlpage.component.html', templateUrl: './control.component.html',
styleUrls: ['./controlpage.component.css'], styleUrls: ['./control.component.css'],
}) })
export class ControlPageComponent implements OnInit { export class PagesControlComponent implements OnInit {
DataStore = DataStore; DataStore = DataStore;
User = User; User = User;
......
div, elements-term, elements-guacamole, elements-settings {
height: 100%;
}
elements-term, elements-guacamole, elements-settings {
/*padding-bottom: 30px;*/
}
.window {
display: none;
/*padding: 15px;*/
}
.active {
display: block;
}
.right-side {
height: 100%;
width: 100%;
background-color: gray;
}
<div fxLayout="column" ngxSplit="column" style="width: 100%;height: 100%;">
<div fxFlex="0 0 30px" class="search">
<pages-control-nav></pages-control-nav>
</div>
<div fxFlex="0 0 calc(100%-35px)">
<div class="window" *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'active':i==NavList.Active}" style="height: 100%">
<elements-ssh-term [index]="i"
[host]="m.host"
[userid]="m.user.id"
*ngIf="m.type=='ssh'">
</elements-ssh-term>
<elements-guacamole [index]="i"
[host]="m.host"
[userid]="m.user.id"
*ngIf="m.type=='rdp'">
</elements-guacamole>
<!--<elements-settings [index]="i"-->
<!--*ngIf="m.type=='settings'">-->
<!--</elements-settings>-->
</div>
</div>
</div>
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
*/ */
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {TermWS} from '../../globals'; import {TermWS} from '../../../globals';
// export class Term { // export class Term {
// machine: string; // machine: string;
...@@ -41,7 +41,7 @@ export let NavList: { ...@@ -41,7 +41,7 @@ export let NavList: {
}; };
@Component({ @Component({
selector: 'app-control', selector: 'pages-control-control',
templateUrl: './control.component.html', templateUrl: './control.component.html',
styleUrls: ['./control.component.css'] styleUrls: ['./control.component.css']
}) })
......
.tabs { .tabs {
height: 30px; height: 30px;
overflow-y: hidden; overflow-y: hidden;
overflow-x: auto; overflow-x: hidden;
position: relative;
} }
.tabs ul li.disconnected { .tabs ul li.disconnected {
...@@ -32,7 +33,7 @@ ...@@ -32,7 +33,7 @@
.tabs ul li.active { .tabs ul li.active {
box-sizing: border-box; box-sizing: border-box;
border-bottom: 5px solid #19aa8d !important; border-bottom: 3px solid #19aa8d !important;
} }
.tabs ul li span { .tabs ul li span {
...@@ -43,7 +44,8 @@ ...@@ -43,7 +44,8 @@
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
font-size: 13px; font-size: 13px;
text-decoration: none; text-decoration: none;
padding: 8px 20px 6px 15px; padding-left: 24px;
line-height: 26px;
cursor: default; cursor: default;
width: 115px; width: 115px;
height: 21px; height: 21px;
...@@ -66,7 +68,8 @@ ...@@ -66,7 +68,8 @@
} }
.tabs ul li.active span { .tabs ul li.active span {
padding: 5px 20px 4px 15px; padding-left: 24px;
line-height: 26px;
color: white; color: white;
height: 18px; height: 18px;
} }
......
<div class="scroll-botton"> <div class="scroll-botton" style="padding: 0 5px">
&nbsp; <a class="left" (click)="scrollleft()"><i class="fa fa-caret-left"></i></a> <a class="left" (click)="scrollleft()"><i class="fa fa-caret-left"></i></a>
&nbsp;
<a class="right" (click)="scrollright()"><i class="fa fa-caret-right"></i></a> <a class="right" (click)="scrollright()"><i class="fa fa-caret-right"></i></a>
&nbsp;
</div> </div>
<div class="tabs"> <div class="tabs">
<ul [ngStyle]="{'width':150*NavList.List.length+'px'}"> <ul [ngStyle]="{'width':150*NavList.List.length+'px'}">
......
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesControlNavComponent } from './nav.component';
describe('ControlPagesControlNavComponent', () => {
let component: PagesControlNavComponent;
let fixture: ComponentFixture<PagesControlNavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PagesControlNavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PagesControlNavComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
...@@ -12,12 +12,12 @@ import {ControlComponent, NavList} from '../control.component'; ...@@ -12,12 +12,12 @@ import {ControlComponent, NavList} from '../control.component';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({ @Component({
selector: 'app-controlnav', selector: 'pages-control-nav',
templateUrl: './controlnav.component.html', templateUrl: './nav.component.html',
styleUrls: ['./controlnav.component.css'], styleUrls: ['./nav.component.css'],
}) })
export class ControlnavComponent implements OnInit { export class PagesControlNavComponent implements OnInit {
setActive = ControlnavComponent.setActive; setActive = PagesControlNavComponent.setActive;
NavList = NavList; NavList = NavList;
static checkActive(index) { static checkActive(index) {
...@@ -31,7 +31,7 @@ export class ControlnavComponent implements OnInit { ...@@ -31,7 +31,7 @@ export class ControlnavComponent implements OnInit {
} else { } else {
NavList.Active = index; NavList.Active = index;
} }
ControlnavComponent.setActive(NavList.Active); PagesControlNavComponent.setActive(NavList.Active);
} }
static setActive(index) { static setActive(index) {
...@@ -44,7 +44,7 @@ export class ControlnavComponent implements OnInit { ...@@ -44,7 +44,7 @@ export class ControlnavComponent implements OnInit {
if (NavList.List[index].type === 'ssh') { if (NavList.List[index].type === 'ssh') {
NavList.List[index].Term.focus(); NavList.List[index].Term.focus();
} else if (NavList.List[index].type === 'rdp') { } else if (NavList.List[index].type === 'rdp') {
NavList.List[index].Rdp.focus(); // NavList.List[index].Rdp.focus();
} }
} else { } else {
...@@ -65,7 +65,7 @@ export class ControlnavComponent implements OnInit { ...@@ -65,7 +65,7 @@ export class ControlnavComponent implements OnInit {
ControlComponent.TerminalDisconnect(index); ControlComponent.TerminalDisconnect(index);
} }
NavList.List.splice(index, 1); NavList.List.splice(index, 1);
ControlnavComponent.checkActive(index); PagesControlNavComponent.checkActive(index);
} }
scrollleft() { scrollleft() {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
*/ */
import {Component, OnChanges, Input, Pipe, PipeTransform} from '@angular/core'; import {Component, OnChanges, Input, Pipe, PipeTransform} from '@angular/core';
import {AppService, HttpService, LogService} from '../../app.service'; import {AppService, HttpService, LogService} from '../../../app.service';
export let Q = ''; export let Q = '';
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
* @author liuzheng <liuzheng712@gmail.com> * @author liuzheng <liuzheng712@gmail.com>
*/ */
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {AppService, LogService} from '../../app.service'; import {AppService, LogService} from '../../../app.service';
@Component({ @Component({
selector: 'app-ileftbar', selector: 'pages-ileftbar',
templateUrl: './ileftbar.component.html', templateUrl: './ileftbar.component.html',
styleUrls: ['./ileftbar.component.css'] styleUrls: ['./ileftbar.component.css']
}) })
......
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {IleftbarComponent} from './ileftbar.component';
@NgModule({
imports: [
CommonModule
],
declarations: [
IleftbarComponent
]
})
export class IleftbarModule {
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component'; import { PagesIndexComponent } from './index.component';
describe('LoginComponent', () => { describe('PagesIndexComponent', () => {
let component: LoginComponent; let component: PagesIndexComponent;
let fixture: ComponentFixture<LoginComponent>; let fixture: ComponentFixture<PagesIndexComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ LoginComponent ] declarations: [ PagesIndexComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent); fixture = TestBed.createComponent(PagesIndexComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
...@@ -6,15 +6,15 @@ ...@@ -6,15 +6,15 @@
* @author liuzheng <liuzheng712@gmail.com> * @author liuzheng <liuzheng712@gmail.com>
*/ */
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {AppService} from '../app.service'; import {AppService} from '../../app.service';
import {User} from '../globals'; import {User} from '../../globals';
@Component({ @Component({
selector: 'app-index-page', selector: 'pages-index',
templateUrl: './index-page.component.html', templateUrl: './index.component.html',
styleUrls: ['./index-page.component.css'], styleUrls: ['./index.component.css'],
}) })
export class IndexPageComponent implements OnInit { export class PagesIndexComponent implements OnInit {
User = User; User = User;
constructor() { constructor() {
......
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NotFoundComponent } from './not-found.component'; import { PagesLoginComponent } from './login.component';
describe('NotFoundComponent', () => { describe('PagesLoginComponent', () => {
let component: NotFoundComponent; let component: PagesLoginComponent;
let fixture: ComponentFixture<NotFoundComponent>; let fixture: ComponentFixture<PagesLoginComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ NotFoundComponent ] declarations: [ PagesLoginComponent ]
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(NotFoundComponent); fixture = TestBed.createComponent(PagesLoginComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
......
...@@ -13,11 +13,11 @@ import {DataStore, User} from '../../globals'; ...@@ -13,11 +13,11 @@ import {DataStore, User} from '../../globals';
import * as jQuery from 'jquery/dist/jquery.min.js'; import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({ @Component({
selector: 'app-login', selector: 'pages-login',
templateUrl: './login.component.html', templateUrl: './login.component.html',
styleUrls: ['./login.component.css'], styleUrls: ['./login.component.css'],
}) })
export class LoginComponent implements OnInit { export class PagesLoginComponent implements OnInit {
DataStore = DataStore; DataStore = DataStore;
User = User; User = User;
loginBotton = 'login to your account'; loginBotton = 'login to your account';
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment