Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PasswordProtectedFiles #5067

Merged
merged 35 commits into from Sep 23, 2018
Merged

PasswordProtectedFiles #5067

merged 35 commits into from Sep 23, 2018

Conversation

dapocalypse
Copy link
Contributor

This PR adds password protected files. When creating a file an additional prompt will come up. This will ask if you would like to set a password. If left blank a password will not be set and the file will function normally. If a password is set, when viewing, cloning, renaming, or deleting a file, the password prompt will again come up asking for the previously set password. When cloned, the cloned file retains the password.

Copy link
Contributor

@BurgerLUA BurgerLUA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a places where you could be using else statements, and a lot of the checks are wrong in the first place. NOT EQUAL statements are x != y not !x == y.

!x == y checks in the inverse of x is equal to y.

Other than that, this is a fine addition.

HDD.remove_file(file)
if(!currentpass)
HDD.remove_file(file)
if(currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an else statement.

checkpass = sanitize(input(usr, "Please enter the password:"))
if(checkpass == currentpass)
open_file = href_list["PRG_openfile"]
if(!checkpass == currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an else statement.

var/currentpass = file.password
if(!currentpass)
open_file = href_list["PRG_openfile"]
if(currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an else statement.

checkpass = sanitize(input(usr, "Please enter the password:"))
if(checkpass == currentpass)
HDD.remove_file(file)
if(!checkpass == currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an else statement, and I think it's written wrong in the first place.
NOT EQUALS statements are x != y not `!x == y', I think I'm seeing this a lot in this PR so you might want to look at that.

checkpass = sanitize(input(usr, "Please enter the password:"))
if(checkpass == currentpass)
RHDD.remove_file(file)
if(!checkpass == currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an else statement.

checkpass = sanitize(input(usr, "Please enter the password:"))
if(checkpass == currentpass)
HDD.store_file(C)
if(!checkpass == currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Else statement.

Copy link
Contributor

@Karolis2011 Karolis2011 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just horrid. But improving.

@@ -0,0 +1,2 @@
[LocalizedFileNames]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary file

var/currentpass = file.password
if(!currentpass)
open_file = href_list["PRG_openfile"]
if(currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (...)
    onething()
else
    otherthing()

exists

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on what if(currentpass) is, a ternary may be more appropriate.

var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_openfile"])
var/checkpass = ""
var/currentpass = file.password
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsafe access of file.
This whole password check should be put inside ui_interact() around line 225

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I make a proc to check for the password? Also I dont understand what you mean by unsafe acess of file.

Copy link
Contributor

@skull132 skull132 Jul 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should make password protection validation a proc, yes. This method of doing:

		if (!currentpass)
			// do one thing
		else
			// check password and do the other

is bad. Very bad. A file should have a proc proc/can_access_file(mob/user, prompt_for_password = FALSE, input_password = "")

Rough contents would look like this:

/datum/computer_file/proc/can_access_file(mob/user, prompt_for_password = FALSE, input_password = "")
	if (!password)
		return TRUE

	if (prompt_for_password)
		if (!ismob(user))
			return FALSE

		input_password = sanitize(input(user, "Please enter a password to access file '[name]':"))

	return password == input_password

This allows for 100x cleaner functionality:

		if (!file.can_access_file(usr, TRUE))
			return 1

		open_file = href_list["PRG_openfile"]

#################################

# Your name.
author: dapocalypse
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DO NOT EDIT example.yml

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It´s now a different file

var/datum/computer_file/data/F = new/datum/computer_file/data()
F.filename = newname
F.filetype = "TXT"
F.password = newpassword
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should do if (newpassword)

if(!file || file.undeletable)
return 1
HDD.remove_file(file)
if(!currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleting shouldn't require password

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree, bald greytide names steve breaks into the captains office and logs onto his computer, baldie mcsteve finds file names "realy important information i need to remember and cannot be deleted" baldie mcsteve deleted the file and runs to his greytide layer. The captain returns and finds his file has been deleted.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Password protected files IRL ask for a password every time you so much as breathe on it. It requires a password when you move the file or delete the file. It makes sense that password protected files ask for a password every time you modify it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would depend on what the actual underlying mentality of the password protection is. There's two ways to handle password protection:

  • OS level. This is what you're referring to, in that the OS enforces password protection to even breathe on the file.
  • Encrypted file. This just means that the contents of the file are encrypted and require a key to decrypt. You can move/copy/delete the file without issues, tho. Because it's just a random text file with garbled contents.

I don't really have a preference, @Arrow768 might have one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking of revealing encrypted ciphertext during an emag then having the player decrypt the file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding password required for deletion:
Most operating systems do not care weather a file is encrypted or not. The actual encryption / decryption is handled by a application interacting with the file.
The operating system only uses RBAC or something simmilar, but there are no special checks if a file is encrypted or not.

I believe the second option is our way to go in that case:
Viewing / editing requires a key (saved with the file datum)
but moving / copying / deleting does not.
This leaves room to add file owners and permissions later on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding Baldie McSteve.
Physical Security is a important aspect.
If you allow someone else physical access to a computer system, then they can do almost anything with it.
Therefore I would allow other people to tamper with it, if they can get access.
It should be the responsibility of the player to restrict the access to the computer system accordingly or keep backups

if(!F || !istype(F))
return 1
var/datum/computer_file/C = F.clone(1)
HDD.store_file(C)
if(!currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you opened file, you already know password, so closing it shouldn't ask password too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for cloning the file, not closing it.

if(!file || !istype(file))
return 1
var/newname = sanitize(input(usr, "Enter new file name:", "File rename", file.filename))
if(file && newname)
file.filename = newname
if(!currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming shouldn't need password

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Baldie McSteve will strike again.

if(!file || file.undeletable)
return 1
RHDD.remove_file(file)
if(!currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing about deleting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the thing about deleting files in flash drive as you cant find them or make them i dont think.

if(!file || file.undeletable)
return 1
HDD.remove_file(file)
if(!currentpass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would depend on what the actual underlying mentality of the password protection is. There's two ways to handle password protection:

  • OS level. This is what you're referring to, in that the OS enforces password protection to even breathe on the file.
  • Encrypted file. This just means that the contents of the file are encrypted and require a key to decrypt. You can move/copy/delete the file without issues, tho. Because it's just a random text file with garbled contents.

I don't really have a preference, @Arrow768 might have one.

if(href_list["PRG_usbdeletefile"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.portable_drive
var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.hard_drive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems weird. Can you explain the difference between portable_drive and hard_drive? It could lead to odd things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was unintended,

var/datum/computer_file/file = RHDD.find_file_by_name(href_list["PRG_usbdeletefile"])
var/datum/computer_file/file = RHDD.find_file_by_name(href_list["PRG_deletefile"])
var/checkpass = ""
var/currentpass = file.password
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know if the file exists. Move this below line 76.

@@ -60,21 +92,40 @@
if(!HDD)
return 1
var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_clone"])
var/checkpass = ""
var/currentpass = F.password
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're accessing F.password without checking if F actually exists. Line 97 checks if F exists and exits if it doesn't. So this has to be below it.

if(href_list["PRG_rename"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
if(!HDD)
return 1
var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_rename"])
var/checkpass = ""
var/currentpass = file.password
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below line 117.

if(href_list["PRG_copyfromusb"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive
if(!HDD || !RHDD || computer.enrolled != 2)
return 1
var/datum/computer_file/F = RHDD.find_file_by_name(href_list["PRG_copyfromusb"])
if(!F || !istype(F))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Erm. Line 207 already exists. Delete this and line 204.

if(!F || !istype(F))
return 1
var/checkpass = ""
var/currentpass = F.password
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this below line 208.

var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_openfile"])
var/checkpass = ""
var/currentpass = file.password
Copy link
Contributor

@skull132 skull132 Jul 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should make password protection validation a proc, yes. This method of doing:

		if (!currentpass)
			// do one thing
		else
			// check password and do the other

is bad. Very bad. A file should have a proc proc/can_access_file(mob/user, prompt_for_password = FALSE, input_password = "")

Rough contents would look like this:

/datum/computer_file/proc/can_access_file(mob/user, prompt_for_password = FALSE, input_password = "")
	if (!password)
		return TRUE

	if (prompt_for_password)
		if (!ismob(user))
			return FALSE

		input_password = sanitize(input(user, "Please enter a password to access file '[name]':"))

	return password == input_password

This allows for 100x cleaner functionality:

		if (!file.can_access_file(usr, TRUE))
			return 1

		open_file = href_list["PRG_openfile"]

@dapocalypse
Copy link
Contributor Author

The thing stalled, my push is fine, can somone get it to test it again

var/currentpass = file.password
if(!currentpass)
var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_openfile"])
var/iscorrect = F.can_acess_file(passcheck)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be after line 27. We don't know if F exists yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The var iscorrect is unnecessary. Just call the proc in an if: if (F.can_access_file(passcheck))

@@ -19,18 +19,16 @@

if(href_list["PRG_openfile"])
. = 1
var/passcheck
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What and why is this variable.

if(href_list["PRG_usbdeletefile"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.hard_drive
var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.portable_drive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, this change needs to be explained.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was reverted from the previos push

if(.)
SSnanoui.update_uis(NM)

/datum/nano_module/program/computer_filemanager
name = "NTOS File Manager"

/datum/computer_file/proc/can_acess_file(input_password = "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This proc is in the wrong file. Look for a file which has the computer_file definition.

Also, you're missing the argument mob/user. You need it.

if(!password)
return TRUE
else
input_password = sanitize(input(usr, "Please enter a password to access file '[filename]':"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage of usr outside of Topic and a few other procs is forbidden. It is unsafe and makes procs unusable. This is why you need the argument of mob/user.

@dapocalypse
Copy link
Contributor Author

dapocalypse commented Jul 29, 2018

can admins please run check again for travis, byond is fucking up not me

@dapocalypse
Copy link
Contributor Author

alright, papi skull, do you appreove

@@ -150,6 +159,16 @@
/datum/nano_module/program/computer_filemanager
name = "NTOS File Manager"

/datum/computer_file/proc/can_acess_file(var/mob/user, input_password = "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling mistake in the proc.
It should be access

@Arrow768
Copy link
Member

Arrow768 commented Jul 30, 2018

I do not like that you are promoted for a password when you create the file and there is no way to change / decrypt the file.

There should be options to encrypt / decrypt the file manually.
-> This would also allow you to change the encryption key
-> And we can get rid of the password prompt when a file is created.

if(href_list["PRG_encrypt"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_openfile"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldnt that be ?
var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_encrypt"])
-> Same for the decrypt option below

@@ -73,8 +80,7 @@
if(!file || !istype(file))
return 1
var/newname = sanitize(input(usr, "Enter new file name:", "File rename", file.filename))
if(file && newname)
file.filename = newname
file.filename = newname
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the two checks here is bad. First, we don't know if the name is empty; second, the file might get deleted while the proc sleeps. Put them back.

@@ -144,12 +150,39 @@
return 1
var/datum/computer_file/C = F.clone(0)
HDD.store_file(C)
if(href_list["PRG_encrypt"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check if HDD exists after this, and return if not. See line 135 or 146.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skull's comment is now invalid, check is present

F.password = sanitize(input(usr, "Enter an encryption key:", "Encrypt File"))
if(href_list["PRG_decrypt"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check if HDD exists after this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skull's comment is now invalid, check is present

@dapocalypse
Copy link
Contributor Author

admins admins hear my cry please reset travis

Copy link
Contributor

@Karolis2011 Karolis2011 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly way way better than initial implementation. If not that proc, I would have approved it.

@@ -46,7 +53,7 @@
var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.portable_drive
if(!RHDD)
return 1
var/datum/computer_file/file = RHDD.find_file_by_name(href_list["PRG_usbdeletefile"])
var/datum/computer_file/file = RHDD.find_file_by_name(href_list["PRG_deletefile"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line shouldn't have been changed

@@ -144,12 +150,39 @@
return 1
var/datum/computer_file/C = F.clone(0)
HDD.store_file(C)
if(href_list["PRG_encrypt"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skull's comment is now invalid, check is present

F.password = sanitize(input(usr, "Enter an encryption key:", "Encrypt File"))
if(href_list["PRG_decrypt"])
. = 1
var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skull's comment is now invalid, check is present

if(.)
SSnanoui.update_uis(NM)

/datum/nano_module/program/computer_filemanager
name = "NTOS File Manager"

/datum/computer_file/proc/can_access_file(var/mob/user, input_password = "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Proc should have been in code/modules/modular_computers/file_system/computer_file.dm

if(!password)
return TRUE
else
input_password = sanitize(input(user, "Please enter a password to access file '[filename]':"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point of proc param input_password if it's being reset by user's input anyway?
I recommend putting this line inside if(!input_password)

@skull132 skull132 added this to the September Update milestone Aug 5, 2018
return TRUE
else
if (!input_password)
input_password = sanitize(input(user, "Please enter a password to access file '[filename]':"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think file '[filename]' is a bit redundant. What are you going to be accessing that isn't a file?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imho its fine.

"Please enter a password to access"
Is what would be left and thats worse.

Maybe remove just the "file"
So its only:

Please enter a password to file '[filename]':

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're misunderstanding what I'm suggesting. Just removing the word 'file' is fine, so that it's Please enter a password to access 'foo':, or similar.

Copy link
Member

@Arrow768 Arrow768 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine. Please make sure its tested and works ingame

Copy link
Contributor

@Karolis2011 Karolis2011 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do as I suggest and you will:

  1. Reduce great wall of buttons
  2. Fix issue that you can't decrypt files

Also you can encrypt programs, but there is no check while opening program if program is encrypted. Add check or make them unencryptable.

@@ -28,6 +28,8 @@
{{:helper.link('DELETE', null, { "PRG_deletefile" : value.name }, value.undeletable ? 'disabled' : null)}}
{{:helper.link('RENAME', null, { "PRG_rename" : value.name }, value.undeletable ? 'disabled' : null)}}
{{:helper.link('CLONE', null, { "PRG_clone" : value.name }, value.undeletable ? 'disabled' : null)}}
{{:helper.link('ENCRYPT', null, { "PRG_encrypt" : value.name }, (value.encrypted || value.undeletable) ? 'disabled' : null)}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{{if !value.encrypted}}
  {{:helper.link('ENCRYPT', null, { "PRG_encrypt" : value.name }, (value.undeletable) ? 'disabled' : null)}}
{{else}}
  {{:helper.link('DECRYPT', null, { "PRG_decrypt" : value.name }, (value.undeletable) ? 'disabled' : null)}}
{{/if}}

@@ -28,8 +28,10 @@
{{:helper.link('DELETE', null, { "PRG_deletefile" : value.name }, value.undeletable ? 'disabled' : null)}}
{{:helper.link('RENAME', null, { "PRG_rename" : value.name }, value.undeletable ? 'disabled' : null)}}
{{:helper.link('CLONE', null, { "PRG_clone" : value.name }, value.undeletable ? 'disabled' : null)}}
{{if data.usbconnected}}
{{:helper.link('EXPORT', null, { "PRG_copytousb" : value.name }, value.undeletable ? 'disabled' : null)}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove copying to USB drive? Please return this button.

{{if data.usbconnected}}
{{:helper.link('EXPORT', null, { "PRG_copytousb" : value.name }, value.undeletable ? 'disabled' : null)}}
{{/if}}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The {{/if}} tag needs to be here as well.

@skull132 skull132 merged commit c5f5f8c into Aurorastation:development Sep 23, 2018
@dapocalypse
Copy link
Contributor Author

dapocalypse commented Sep 24, 2018

I'd like to thank Armok, you are best, I'd like to thank my boi eddy snowman for keeping me safe, You all are amazing, god bless ancapistan.
PS: Also thanos for gifting me his amazing thanos car.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants