7
7
from django import forms
8
8
from django.forms import ModelForm
9
from django.conf import settings
9
10
from django.core.files.storage import default_storage
10
from django.conf import settings
12
12
from wlmaps.models import Map
17
18
class UploadMapForm(ModelForm):
20
All file operations are done in the temp folder.
19
22
We have to handle here three different kind of files:
20
1. The map which is uploaded
21
2. The files created by 'wl_map_info'
22
a. The json file containing infos of the map
23
b. The image of the minimap (png)
25
The filename of uploaded maps may contain bad characters which must be handled.
27
If a map get deleted in the database, the underlying files (.wmf, .png) still exists. Uploading
28
a map with the same name does not overwrite the existing file(s), instead they get a
29
name in form of 'filename_<random_chars>.wmf(.png)'. This is importand for linking the correct
31
The map file and the minimap (png) have different random characters in such a case, because the map
32
get saved twice: One time for the call to wl_map_info, and when the model is saved.
23
1. The map which is uploaded, stored as e.g '/tmp/tmp_xyz.upload'.
24
Because 'wl_map_info' can't handle files with this extension and
25
produces a minimap with the same name like the initial name, the
26
uploade file will be copied to a file with the name of the
27
uploaded file and extension, e.g. '/tmp/original_filename.wmf'
28
2. 'wl_map_info' produces two files which will be stored at the same
29
location as the uploaded file and named like the map file:
30
a. '/tmp/original_filename.wmf.json': Contains infos of the map
31
b. '/tmp/original_filename.wmf.png': The image of the minimap (png).
33
Because we can't be sure the original filename is a valid filename, we
34
may modify it to be a valid filename.
40
42
cleaned_data = super(UploadMapForm, self).clean()
42
mem_file_obj = cleaned_data.get('file')
44
file_obj = cleaned_data.get('file')
44
46
# no clean file => abort
45
47
return cleaned_data
48
# Try to make a safe filename
49
safe_name = default_storage.get_valid_name(mem_file_obj.name)
50
file_path = settings.MEDIA_ROOT + 'wlmaps/maps/' + safe_name
51
saved_file = default_storage.save(file_path, mem_file_obj)
52
except UnicodeEncodeError:
53
self._errors['file'] = self.error_class(
54
['The filename contains characters which cannot be handled. Please rename and upload again.'])
55
del cleaned_data['file']
49
# Make a save filename and copy the uploaded file
50
saved_file = file_obj.temporary_file_path()
51
safe_name = default_storage.get_valid_name(file_obj.name)
52
tmpdir = tempfile.gettempdir()
53
copied_file = shutil.copyfile(saved_file,
54
os.path.join(tmpdir, safe_name)
59
58
# call map info tool to generate minimap and json info file
59
# change working directory so that the datadir is found
60
60
old_cwd = os.getcwd()
61
61
os.chdir(settings.WIDELANDS_SVN_DIR)
62
check_call(['wl_map_info', saved_file])
64
# Deleting the file because it will be saved again when
66
default_storage.delete(saved_file)
62
check_call(['wl_map_info', copied_file])
68
64
except CalledProcessError:
69
65
self._errors['file'] = self.error_class(
70
66
['The map file could not be processed.'])
71
67
del cleaned_data['file']
68
os.remove(copied_file)
72
69
return cleaned_data
74
mapinfo = json.load(open(saved_file + '.json'))
71
mapinfo = json.load(open(copied_file + '.json'))
76
73
if Map.objects.filter(name=mapinfo['name']):
77
74
self._errors['file'] = self.error_class(
78
75
['A map with the same name already exists.'])
79
76
del cleaned_data['file']
77
# Delete the file copy and the generated files
79
os.remove(copied_file)
80
os.remove(copied_file + '.json')
81
os.remove(copied_file + '.png')
82
except os.FileNotFoundError:
80
85
return cleaned_data
82
87
# Add information to the map
90
95
self.instance.world_name = mapinfo['world_name']
91
96
self.instance.wl_version_after = mapinfo['needs_widelands_version_after']
93
# mapinfo["minimap"] is the absolute path containing the path where it
94
# is saved, extract the name
98
# mapinfo["minimap"] is the absolute path to the image file
99
# We move the file to the correct destination
95
100
minimap_name = mapinfo['minimap'].rpartition('/')[2]
96
101
minimap_upload_to = self.instance._meta.get_field('minimap').upload_to
97
# Set the destination relative to MEDIA_ROOT
98
102
minimap_path = os.path.join(minimap_upload_to, minimap_name)
99
103
self.instance.minimap = minimap_path
100
# Move the minimap file (.png) from wlmaps/maps to wlmaps/minimaps
101
104
shutil.move(mapinfo['minimap'], os.path.join(
102
105
settings.MEDIA_ROOT, minimap_path))
104
# the json file is no longer needed
105
default_storage.delete(saved_file + '.json')
108
os.remove(copied_file + '.json')
109
os.remove(copied_file)
107
111
return cleaned_data