Commit 740f8027 authored by Ayush's avatar Ayush

File management improved

parent 6a9866f1
......@@ -10,13 +10,13 @@ This is the default script to connect with the MySQL server in the backend. The
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Content-Type: application/json; charset=UTF-8");
$db_host = 'localhost'; // Uncomment and assign values according to your system
$db_host = 'localhost';
$db_username = 'root';
$db_password = '';
$db_name = 'ssl_database'; // add name of your own databse from mysql phpmyadmin
$db_password = '';
$db_name = 'ssl_database';
$mysqli = new mysqli($db_host, $db_username, $db_password,$db_name);
if ($mysqli->connect_error) {
die('Error : ('. $mysqli->connect_errno .') '. $mysqli->connect_error);
}
?>
\ No newline at end of file
?>
......@@ -4,6 +4,12 @@
This script deletes a given code file, or a directory in the user's workspace, given its path.
*/
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Credentials: true');
header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Content-Type: application/json; charset=UTF-8");
include_once("database.php");
$postData = file_get_contents("php://input");
......@@ -21,7 +27,7 @@ if (isset($postData) && !empty($postData)) {
if(strcmp($lang, '.java') == 0)
{
$exec_path = $exec_path . $lang;
$exec_path = $exec_path . '.class';
}
if (unlink($path)) {
......@@ -41,7 +47,7 @@ if (isset($postData) && !empty($postData)) {
$path = '../users/' . $username . '/' . trim($request['file']['path']) . '/' . $dirname;
$exec_path = '../user_execs/' . $username . '/' . trim($request['file']['path']) . '/' . $dirname;
$ret_stat = 0;
system('rm -r ' . $path, $ret_stat);
system('rm -r ' . $exec_path);
......
......@@ -4,6 +4,12 @@
This script saves a given code file for a user, given it's path.
*/
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Credentials: true');
header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Content-Type: application/json; charset=UTF-8");
include_once("database.php");
$postData = file_get_contents("php://input");
......
<?php
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Credentials: true');
header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Content-Type: application/json; charset=UTF-8");
$postData = file_get_contents("php://input");
if(isset($postData) && !empty($postData)) {
$request = json_decode($postData, true);
$username = $request['username'];
$dirname = $request['dir_name'];
$path = "../users/" . $username . "/" . $request['curr_dir'] . "/" . $dirname;
$dirname = $request['dirname'];
$path = "../users/" . $username . "/" . $request['path'] . "/" . $dirname;
if(file_exists($path)) {
$msg = "directory already exists";
......@@ -19,4 +26,4 @@ if(isset($postData) && !empty($postData)) {
}
}
?>
\ No newline at end of file
?>
......@@ -5,6 +5,12 @@ This script registers a user using a username, password, name, and email. This s
user data.
*/
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Credentials: true');
header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Content-Type: application/json; charset=UTF-8");
include_once("database.php");
$postData = file_get_contents("php://input");
......@@ -35,4 +41,4 @@ if (isset($postData) && !empty($postData)) {
echo "mysqli_error($mysqli)";
}
}
?>
\ No newline at end of file
?>
......@@ -12,8 +12,7 @@ import {FileComponent} from './file/file.component';
const routes: Routes = [
{path: 'home', component: HomeComponent, canActivate: [AuthGuard]},
{path: 'arena/problem/:id', component: ArenaComponent, canActivate: [AuthGuard]},
{path: 'arena/file/:id', component: IdeComponent, canActivate: [AuthGuard]},
{path: 'arena/file/new', component: IdeComponent, canActivate: [AuthGuard]},
{path: 'arena/file/:filepath', component: IdeComponent, canActivate: [AuthGuard]},
{path: 'user', component: UserComponent, canActivate: [AuthGuard]},
{path: 'files', component: FileComponent, canActivate: [AuthGuard]},
{path: 'login', component: LoginComponent},
......
<div class="card" [style.marginLeft]="level * 20 + 'px'" [style.background]="isFile ? '#002' : '#022'">
<div class="title" *ngIf="isFile">{{file.filename}}{{file.language}}</div>
<div class="title" *ngIf="!isFile">{{directory.name}}</div>
<div class="dir-desc" *ngIf="!isFile">{{directory.dirs.length}} directories, {{directory.files.length}} files</div>
<div *ngIf="isFile"></div>
<div class="file-buttons">
<button (click)="onDelete()" [disabled]="deleting" [class.disabled]="deleting">Delete</button>
<button *ngIf="!isFile" (click)="isExpanded = !isExpanded">{{isExpanded ? 'Collapse' : 'Expand'}}</button>
<button *ngIf="isFile">Edit</button>
<div [style.background]="isFile ? '#002' : '#022'" [style.marginLeft]="level * 20 + 'px'" class="card">
<div *ngIf="!isNew && isFile" class="title">{{file.filename}}{{file.language}}</div>
<div *ngIf="!isNew && !isFile" class="title">{{directory.name}}</div>
<div *ngIf="!isNew && !isFile" class="dir-desc">{{directory.dirs.length}} directories, {{directory.files.length}}
files
</div>
<div *ngIf="isNew" class="title">
<input (input)="isErrorCreating = false" [(ngModel)]="newName" [disabled]="creating" id="newFilenameInput"
[placeholder]="'Name of new ' + (isFile ? 'file' : 'directory')" type="text">
<label for="newFilenameInput" *ngIf="isFile" (click)="lang = (lang + 1) % 3">{{extensions[lang]}}</label>
</div>
<div *ngIf="isNew || isFile"></div>
<div *ngIf="!isNew" class="file-buttons">
<button (click)="onDelete()" [class.disabled]="deleting || isErrorDeleting"
[disabled]="deleting || isErrorDeleting">{{isErrorDeleting ? 'Error' : (deleting ? 'Deleting...' : 'Delete')}}</button>
<button (click)="isExpanded = !isExpanded" *ngIf="!isFile">{{isExpanded ? 'Collapse' : 'Expand'}}</button>
<button (click)="isExpanded = true; creatingNewSub = 1" *ngIf="!isFile">New Directory</button>
<button (click)="isExpanded = true; creatingNewSub = 2" *ngIf="!isFile">New File</button>
<button *ngIf="isFile" [routerLink]="getRouterLink()">Edit</button>
<button *ngIf="isFile">Run</button>
</div>
<div *ngIf="isNew" class="file-buttons">
<button (click)="cancelled.emit(true)" [class.disabled]="creating" [disabled]="creating">Cancel</button>
<button (click)="isFile ? onCreateFile() : onCreateDir()"
[class.disabled]="newName==='' || creating || isErrorCreating"
[disabled]="newName==='' || creating || isErrorCreating">{{isErrorCreating ? 'Error' : (creating ? 'Creating...' : 'Create')}}</button>
</div>
</div>
<div class="subs" *ngIf="!isFile" [style.display]="isExpanded ? 'block' : 'none'">
<app-file-dir-card *ngFor="let dir of directory.dirs" [isFile]="false" [directory]="dir" [level]="level + 1" [trace]="addToTraceDir(dir)" (deleteFile)="deleteFileExec($event)" (deleteDir)="deleteDirExec($event)"></app-file-dir-card>
<app-file-dir-card *ngFor="let file of directory.files" [isFile]="true" [file]="file" [level]="level + 1" [trace]="addToTraceFile(file)" (deleteFile)="deleteFileExec($event)" (deleteDir)="deleteDirExec($event)"></app-file-dir-card>
<div *ngIf="!isNew && !isFile" [style.display]="isExpanded ? 'block' : 'none'" class="subs">
<app-file-dir-card (cancelled)="creatingNewSub = 0" (createDir)="createDirExec($event)" (createFile)="createFileExec($event)"
*ngIf="creatingNewSub !== 0" [isFile]="creatingNewSub === 2" [level]="level + 1"
[isNew]="true" [parentPath]="directory.path + directory.name + '/'"></app-file-dir-card>
<app-file-dir-card (deleteDir)="deleteDirExec($event)" (deleteFile)="deleteFileExec($event)"
*ngFor="let dir of directory.dirs" [directory]="dir"
[isFile]="false" [isNew]="false" [level]="level + 1"
[trace]="addToTraceDir(dir)"></app-file-dir-card>
<app-file-dir-card (deleteDir)="deleteDirExec($event)" (deleteFile)="deleteFileExec($event)"
*ngFor="let file of directory.files" [file]="file"
[isFile]="true" [isNew]="false" [level]="level + 1"
[trace]="addToTraceFile(file)"></app-file-dir-card>
</div>
.card {
display: grid;
grid-template-columns: 33.33% 33.33% 33.33%;
grid-template-columns: 30% 20% 50%;
height: 3em;
border-bottom: 1px solid #fff;
......@@ -11,6 +11,28 @@
.title {
padding: 1em 20px;
input {
background: none;
border: none;
border-bottom: 1px solid #ddf;
font: inherit;
color: inherit;
width: 60%;
&::placeholder {
font-size: 0.8em;
}
}
label {
font-size: 0.9em;
padding: 5px;
background: #202;
border-radius: 5px;
cursor: pointer;
user-select: none;
}
}
.dir-desc {
......
import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core';
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {File} from '../file';
import {Directory} from '../directory';
import {FileService} from '../file.service';
......@@ -16,13 +16,27 @@ export class FileDirCardComponent implements OnInit {
@Input() file: File;
@Input() directory: Directory;
@Input() trace: number[];
@Input() isNew: boolean;
@Input() parentPath: string;
deleting: boolean;
isExpanded = false;
newName = '';
isErrorDeleting = false;
isErrorCreating = false;
creating = false;
creatingNewSub = 0;
extensions = ['.cpp', '.py', '.java'];
lang = 0;
@Output() deleteFile: EventEmitter<any> = new EventEmitter();
@Output() deleteDir: EventEmitter<any> = new EventEmitter();
@Output() createDir: EventEmitter<any> = new EventEmitter();
@Output() createFile: EventEmitter<File> = new EventEmitter();
@Output() cancelled: EventEmitter<boolean> = new EventEmitter();
constructor(private fileService: FileService, private apiService: ApiService) { }
constructor(private fileService: FileService, private apiService: ApiService) {
}
ngOnInit(): void {
}
......@@ -70,4 +84,53 @@ export class FileDirCardComponent implements OnInit {
this.directory.dirs.splice(index, 1);
}
}
onCreateDir(): void {
this.creating = true;
this.fileService.createDirectory(JSON.parse(this.apiService.getToken()).username, this.newName, this.parentPath)
.subscribe(data => {
if (data === '0') {
this.creating = false;
this.isErrorCreating = true;
} else {
this.createDir.emit(this.newName);
}
});
}
getRouterLink(): string {
return '/arena/file/' + encodeURIComponent(this.file.path + '/' + this.file.filename + this.file.language);
}
onCreateFile(): void {
this.creating = true;
const file: File = {
username: JSON.parse(this.apiService.getToken()).username,
filename: this.newName,
language: this.extensions[this.lang],
text: '',
path: this.parentPath
};
this.fileService.upload(file).subscribe(data => {
this.createFile.emit(file);
}, () => {
this.creating = false;
this.isErrorCreating = true;
});
}
createFileExec(file: File): void {
this.directory.files.push(file);
this.creatingNewSub = 0;
}
createDirExec(name: string): void {
this.directory.dirs.push({
name,
dirs: [],
files: [],
path: this.directory.path + this.directory.name + '/',
});
this.creatingNewSub = 0;
}
}
......@@ -14,6 +14,8 @@ export class FileService {
uploadUrl = 'http://localhost/sfcode/backend/fileupload.php';
fileListUrl = 'http://localhost/sfcode/backend/dir_tree.php';
deleteUrl = 'http://localhost/sfcode/backend/filedelete.php';
fileContentUrl = 'http://localhost/sfcode/backend/get_file.php';
createDirUrl = 'http://localhost/sfcode/backend/newdir.php';
constructor(private http: HttpClient) {
}
......@@ -54,4 +56,13 @@ export class FileService {
return this.http.post(this.fileListUrl, {username});
}
getFileContent(username: string, filepath: string): Observable<any> {
return this.http.post(this.fileContentUrl, {username, file_path: filepath});
}
createDirectory(username: string, dirname: string, path: string): Observable<any> {
console.log(username, dirname, path);
return this.http.post(this.createDirUrl, {username, dirname, path});
}
}
import {Directory} from './directory';
export interface File {
username: string;
filename: string;
......
<div id="tray">
<button routerLink="/arena/file/new" routerLinkActive="active" style="margin-right: 10px;">Create A File</button>
<button (click)="uploadPopupActive = true" id="myBtn" style="margin-left: 10px;">Upload A File</button>
<button (click)="creatingNew = 2" style="margin-right: 10px;">Create A New File</button>
<button (click)="creatingNew = 1" id="myBtn" style="margin-left: 10px;">Create New Directory</button>
</div>
<div class="container">
{{mainDir.dirs.length}} directories, {{mainDir.files.length}} files:
</div>
<app-file-dir-card *ngFor="let dir of mainDir.dirs" [isFile]="false" [directory]="dir" [level]="0" [trace]="[mainDir.dirs.indexOf(dir)]" (deleteDir)="deleteDirExec($event)" (deleteFile)="deleteFileExec($event)"></app-file-dir-card>
<app-file-dir-card *ngFor="let file of mainDir.files" [isFile]="true" [file]="file" [level]="0" [trace]="[mainDir.files.indexOf(file)]" (deleteDir)="deleteDirExec($event)" (deleteFile)="deleteFileExec($event)"></app-file-dir-card>
<app-file-dir-card *ngIf="creatingNew !== 0" [parentPath]="''" [isNew]="true" [isFile]="creatingNew === 2" (createFile)="createFileExec($event)" (createDir)="createDirExec($event)" (cancelled)="creatingNew = 0"></app-file-dir-card>
<app-file-dir-card *ngFor="let dir of mainDir.dirs" [isFile]="false" [directory]="dir" [level]="0" [trace]="[mainDir.dirs.indexOf(dir)]" (deleteDir)="deleteDirExec($event)" (deleteFile)="deleteFileExec($event)" [isNew]="false"></app-file-dir-card>
<app-file-dir-card *ngFor="let file of mainDir.files" [isFile]="true" [file]="file" [level]="0" [trace]="[mainDir.files.indexOf(file)]" (deleteDir)="deleteDirExec($event)" (deleteFile)="deleteFileExec($event)" [isNew]="false"></app-file-dir-card>
<div [class.open]="uploadPopupActive" id="file-upload-cover"></div>
<!--<div [class.open]="uploadPopupActive" id="file-upload-cover"></div>-->
<div [class.open]="uploadPopupActive" id="file-upload-popup">
<p>Upload a file:</p>
<input (change)="onChange($event)" id="file-upload-input" type="file">
<div *ngIf="shortLink !== ''" style="margin-top: 10px">
<a href="{{shortLink}}" target="_blank">Link to last upload</a>
</div>
<p *ngIf="!loading">
<button (click)="uploadPopupActive = false;" id="upload-cancel-btn">Close</button>
<button (click)="onUpload()" id="file-upload-btn" [class.disabled]="fileToUpload == null || fileToUpload == undefined">Upload</button>
</p>
<p *ngIf="loading" style="margin-top: 20px; text-align: center">
Uploading...
</p>
</div>
<!--<div [class.open]="uploadPopupActive" id="file-upload-popup">-->
<!-- <p>Upload a file:</p>-->
<!-- <input (change)="onChange($event)" id="file-upload-input" type="file">-->
<!-- <div *ngIf="shortLink !== ''" style="margin-top: 10px">-->
<!-- <a href="{{shortLink}}" target="_blank">Link to last upload</a>-->
<!-- </div>-->
<!-- <p *ngIf="!loading">-->
<!-- <button (click)="uploadPopupActive = false;" id="upload-cancel-btn">Close</button>-->
<!-- <button (click)="onUpload()" id="file-upload-btn" [class.disabled]="fileToUpload == null || fileToUpload == undefined">Upload</button>-->
<!-- </p>-->
<!-- <p *ngIf="loading" style="margin-top: 20px; text-align: center">-->
<!-- Uploading...-->
<!-- </p>-->
<!--</div>-->
......@@ -23,7 +23,7 @@ export class FileComponent implements OnInit {
loading = false; // Flag variable
deleting = false; // Flag variable
fileToUpload: File = null;
uploadPopupActive = false;
creatingNew = 0;
constructor(private fileService: FileService,
private dataService: ApiService) {
......@@ -110,4 +110,19 @@ export class FileComponent implements OnInit {
this.mainDir.dirs.splice(index, 1);
}
}
createFileExec(file: File): void {
this.mainDir.files.push(file);
this.creatingNew = 0;
}
createDirExec(name: string): void {
this.mainDir.dirs.push({
name,
dirs: [],
files: [],
path: '/'
});
this.creatingNew = 0;
}
}
<div id="ide-cover" *ngIf="loadingFile">
<div id="ide-cover-text">
Loading file...
</div>
</div>
<div class="container">
<div id="toggle-lang">C++</div>
<div id="toggle-lang" [class.disabled]="isSaved">C++</div>
<div id="attempt">
<span *ngIf="!isUpToDate"></span>
<span id="ideFileName">{{file.filename + file.language}}</span>
<span id="ideFileName">{{optimizeURI(file.path)}}{{file.filename + file.language}}</span>
<button (click)="inputField.setState(true);" id="inputBtn">Input</button>
<button (click)="runFile()" id="runBtn" [class.disabled]="isCompiling || isError">{{isError ? 'Error' : (isCompiling ? 'Compiling...' : 'Run File')}}</button>
<button (click)="!isSaved ? this.saveField.setState(true) : updateFile();"
......
......@@ -12,6 +12,10 @@
z-index: 100;
cursor: pointer;
user-select: none;
&.disabled {
cursor: not-allowed;
}
}
label {
......@@ -53,6 +57,25 @@
margin-right: 10px;
}
#ide-cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #002e;
color: #ddf;
font-size: 1.5em;
z-index: 10000;
#ide-cover-text {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%);
}
}
@media screen and (max-width: 800px) {
.container {
padding: 20px 0;
......
import {Component, OnInit, ViewChild} from '@angular/core';
import {Router} from '@angular/router';
import {ActivatedRoute, Router} from '@angular/router';
import {InputComponent} from '../input/input.component';
import {IdeCompileComponent} from '../ide-compile/ide-compile.component';
import {RunCodeService} from '../run-code.service';
......@@ -19,6 +19,7 @@ export class IdeComponent implements OnInit {
inp = '';
extensions = ['.cpp', '.py', '.java'];
loadingFile = false;
isSaved = false;
isUploading = false;
isCompiling = false;
......@@ -35,11 +36,12 @@ export class IdeComponent implements OnInit {
@ViewChild(IdeCompileComponent) runField;
@ViewChild(SaveFileComponent) saveField;
constructor(public router: Router, public runCodeService: RunCodeService, private fileService: FileService,
private apiService: ApiService) {
constructor(public route: ActivatedRoute, public runCodeService: RunCodeService, private fileService: FileService,
private apiService: ApiService, private router: Router) {
}
ngOnInit(): void {
const filepath = decodeURIComponent(this.route.snapshot.params.filepath);
const editorArea = document.getElementById('editor');
const editor = CodeMirror.fromTextArea(editorArea as HTMLTextAreaElement, {
lineNumbers: true,
......@@ -74,6 +76,9 @@ int main() {
this.file.text = code[activeLang];
const tl = document.getElementById('toggle-lang') as HTMLDivElement;
tl.onclick = () => {
if (this.isSaved) {
return;
}
activeLang = (activeLang + 1) % 3;
this.file.language = this.extensions[activeLang];
tl.innerHTML = langs[activeLang];
......@@ -90,6 +95,32 @@ int main() {
this.file.text = editor.getValue();
});
if (filepath !== 'new') {
const arr = filepath.split('.');
if (!(['cpp', 'py', 'java']).includes(arr[arr.length - 1])) {
this.router.navigate(['arena/file/new']).then(() => window.location.reload());
}
const arrf = filepath.split('/');
const arrg = arrf.pop().split('.');
arrg.pop();
this.file.filename = arrg.join('.');
this.file.path = arrf.join('/');
this.file.language = '.' + arr[arr.length - 1];
tl.innerHTML = langs[this.extensions.indexOf(this.file.language)];
this.fileService.getFileContent(this.file.username, filepath)
.subscribe(data => {
editor.setValue(data);
this.isSaved = true;
this.isUpToDate = true;
this.loadingFile = false;
console.log(this.file);
}, error => {
this.router.navigate(['arena/file/new']).then(() => window.location.reload());
});
} else {
this.loadingFile = false;
}
}
updateInput(val: string): void {
......@@ -118,7 +149,9 @@ int main() {
runFile(): void {
const btn = document.getElementById('runBtn');
if (btn.classList.contains('disabled')) {return; }
if (btn.classList.contains('disabled')) {
return;
}
this.isCompiling = true;
this.runCodeService.compileFile(this.file)
.subscribe(data => {
......@@ -133,7 +166,9 @@ int main() {
this.isError = true;
this.runField.status = '<div class="error">Runtime Error!</div>';
this.runField.setState(true);
setTimeout(() => {this.isError = false; }, 3000);
setTimeout(() => {
this.isError = false;
}, 3000);
});
}, error => {
console.log(error);
......@@ -141,7 +176,22 @@ int main() {
this.isError = true;
this.runField.status = '<div class="error">Compilation Error!</div>';
this.runField.setState(true);
setTimeout(() => {this.isError = false; }, 3000);
setTimeout(() => {
this.isError = false;
}, 3000);
});
}
optimizeURI(uri: string): string {
const ret: string[] = [];
for (const c of uri.split('/')) {
if (c === '' || c === '.') {
} else if (c === '..') {
ret.pop();
} else {
ret.push(c);
}
}
return ret.join('') === '' ? '/' : '/' + ret.join('/') + '/';
}
}
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