Completed
Pull Request — master (#598)
by Sean
04:20 queued 42s
created

DNDataTransfer::getCMSFields()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
cc 1
eloc 18
nc 1
nop 0
1
<?php
2
3
/**
4
 * Class representing a single data transfer in a project,
5
 * which can include a database export, an archive of all assets, or both.
6
 *
7
 * It can be one of two directions:
8
 * - Backup: Package up data on an environment and store it in a local file
9
 * - Restore: Transfer data from a local file into an environment, extract assets and/or restore a database
10
 *
11
 * The choice of database and/or assets is represented in the "Mode".
12
 * There's always one file archive involved (stored as the has_one "ArchiveFile") on the local Deploynaut environment.
13
 *
14
 * Each transfer is executed by a Resque job, so the model also contains
15
 * a reference to a Resque token (which might still be in progress).
16
 *
17
 * The "Environment" points to the source or target involved.
18
 *
19
 * @property string $ResqueToken
20
 * @property string $Status
21
 * @property string $Direction
22
 * @property string $Mode
23
 * @property string $Origin
24
 *
25
 * @method DNEnvironment Environment()
26
 * @property int EnvironmentID
27
 * @method Member Author()
28
 * @property int AuthorID
29
 * @method DNDataArchive DataArchive()
30
 * @property int DataArchiveID
31
 * @method DNDataTransfer BackupDataTransfer()
32
 * @property int BackupDataTransferID
33
 */
34
class DNDataTransfer extends DataObject {
35
36
	private static $db = array(
37
		"ResqueToken" => "Varchar(255)",
38
		// Observe that this is not the same as Resque status, since ResqueStatus is not persistent.
39
		"Status" => "Enum('Queued, Started, Finished, Failed, n/a', 'n/a')",
40
		"Direction" => "Enum('get, push', 'get')",
41
		"Mode" => "Enum('all, assets, db', '')",
42
		"Origin" => "Enum('EnvironmentTransfer,ManualUpload', 'EnvironmentTransfer')",
43
	);
44
45
	private static $has_one = array(
46
		"Environment" => "DNEnvironment",
47
		"Author" => "Member",
48
		"DataArchive" => "DNDataArchive",
49
		"BackupDataTransfer" => "DNDataTransfer" // denotes an automated backup done for a push of this data transfer
50
	);
51
52
	private static $singular_name = 'Data Transfer';
53
54
	private static $plural_name = 'Data Transfers';
55
56
	private static $summary_fields = array(
57
		'Created' => 'Created',
58
		'Author.Title' => 'Author',
59
		'Environment.Project.Name' => 'Project',
60
		'Environment.Name' => 'Environment',
61
		'Status' => 'Status',
62
		'Origin' => 'Origin',
63
	);
64
65
	private static $searchable_fields = array(
66
		'Environment.Project.Name' => array(
67
			'title' => 'Project',
68
		),
69
		'Environment.Name' => array(
70
			'title' => 'Environment',
71
		),
72
		'Status' => array(
73
			'title' => 'Status',
74
		),
75
		'Origin' => array(
76
			'title' => 'Origin',
77
		),
78
		'Mode' => array(
79
			'title' => 'Mode',
80
		),
81
		'Direction' => array(
82
			'title' => 'Direction',
83
		),
84
	);
85
86
	/**
87
	 * @param int $int
88
	 * @return string
89
	 */
90 View Code Duplication
	public static function map_resque_status($int) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
91
		$remap = array(
92
			Resque_Job_Status::STATUS_WAITING => "Queued",
93
			Resque_Job_Status::STATUS_RUNNING => "Running",
94
			Resque_Job_Status::STATUS_FAILED => "Failed",
95
			Resque_Job_Status::STATUS_COMPLETE => "Complete",
96
			false => "Invalid",
97
		);
98
		return $remap[$int];
99
	}
100
101
	public function getTitle() {
102
		return $this->dbObject('Created')->Nice() . " (Status: {$this->Status})";
103
	}
104
105
	public function Link() {
106
		return Controller::join_links($this->Environment()->Project()->Link(), 'transfer', $this->ID);
107
	}
108
109
	public function LogLink() {
110
		return Controller::join_links($this->Link(), 'log');
111
	}
112
113
	public function getDefaultSearchContext() {
114
		$context = parent::getDefaultSearchContext();
115
		$context->getFields()->dataFieldByName('Status')->setHasEmptyDefault(true);
116
		$context->getFields()->dataFieldByName('Origin')->setHasEmptyDefault(true);
117
118
		return $context;
119
	}
120
121
	public function getCMSFields() {
122
		$fields = parent::getCMSFields();
123
		$fields->removeByName('EnvironmentID');
124
		$fields->removeByName('ArchiveFile');
125
		$fields->addFieldsToTab(
126
			'Root.Main',
127
			array(
128
				new ReadonlyField('ProjectName', 'Project', $this->Environment()->Project()->Name),
129
				new ReadonlyField('EnvironmentName', 'Environment', $this->Environment()->Name),
130
				new ReadonlyField(
131
					'DataArchive',
132
					'Archive File',
133
					sprintf(
134
						'<a href="%s">%s</a>',
135
						$this->DataArchive()->ArchiveFile()->AbsoluteURL,
136
						$this->DataArchive()->ArchiveFile()->Filename
137
					)
138
				),
139
			)
140
		);
141
		$fields = $fields->makeReadonly();
142
143
		return $fields;
144
	}
145
146
	/**
147
	 * Queue a transfer job
148
	 */
149
	public function start() {
150
		$env = $this->Environment();
151
		$log = $this->log();
152
153
		$args = array(
154
			'dataTransferID' => $this->ID,
155
			'logfile' => $this->logfile()
156
		);
157
158
		if(!$this->AuthorID) {
159
			$this->AuthorID = Member::currentUserID();
160
		}
161
162 View Code Duplication
		if($this->AuthorID) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
163
			$author = $this->Author();
164
			$message = sprintf(
165
				'Data transfer on %s (%s, %s) initiated by %s (%s), with IP address %s',
166
				$env->getFullName(),
167
				$this->Direction,
168
				$this->Mode,
169
				$author->getName(),
170
				$author->Email,
171
				Controller::curr()->getRequest()->getIP()
172
			);
173
			$log->write($message);
174
		}
175
176
		$token = Resque::enqueue('snapshot', 'DataTransferJob', $args, true);
177
		$this->ResqueToken = $token;
178
		$this->write();
179
180
		$message = sprintf('Data transfer queued as job %s', $token);
181
		$log->write($message);
182
	}
183
184
	/**
185
	 * @param Member|null $member
186
	 * @return bool
187
	 */
188
	public function canView($member = null) {
189
		return $this->Environment()->canView($member);
190
	}
191
192
	/**
193
	 * Return a path to the log file.
194
	 * @return string
195
	 */
196
	protected function logfile() {
197
		return sprintf(
198
			'%s.datatransfer.%s.log',
199
			$this->Environment()->getFullName('.'),
200
			$this->ID
201
		);
202
	}
203
204
	/**
205
	 * @return \DeploynautLogFile
206
	 */
207
	public function log() {
208
		return new DeploynautLogFile($this->logfile());
209
	}
210
211
	/**
212
	 * @return string
213
	 */
214
	public function LogContent() {
215
		return $this->log()->content();
216
	}
217
218
	public function getDescription() {
219
		$envName = $this->Environment()->getFullName();
220
		if($this->Direction == 'get') {
221
			if($this->Origin == 'ManualUpload') {
222
				$description = 'Manual upload of ' . $this->getModeNice() . ' to ' . $envName;
223
			} elseif($this->IsBackupDataTransfer()) {
224
				$description = 'Automated backup of ' . $this->getModeNice() . ' from ' . $envName;
225
			} else {
226
				$description = 'Backup of ' . $this->getModeNice() . ' to ' . $envName;
227
			}
228
		} else {
229
			$description = 'Restore ' . $this->getModeNice() . ' to ' . $envName;
230
		}
231
232
		return $description;
233
	}
234
235
	public function getModeNice() {
236
		if($this->Mode == 'all') {
237
			return 'database and assets';
238
		} else {
239
			return $this->Mode;
240
		}
241
	}
242
243
	/**
244
	 * Is this transfer an automated backup of a push transfer?
245
	 * @return boolean
246
	 */
247
	public function IsBackupDataTransfer() {
248
		return DB::query(sprintf(
249
			'SELECT COUNT("ID") FROM "DNDataTransfer" WHERE "BackupDataTransferID" = %d',
250
			$this->ID
251
		))->value();
252
	}
253
254
	/**
255
	 * Returns the status of the resque job
256
	 *
257
	 * @return string
258
	 */
259 View Code Duplication
	public function ResqueStatus() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
260
		$status = new Resque_Job_Status($this->ResqueToken);
0 ignored issues
show
Security Bug introduced by
It seems like $this->ResqueToken can also be of type false; however, Resque_Job_Status::__construct() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
261
		$statusCode = $status->get();
262
		// The Resque job can no longer be found, fallback to the DNDataTransfer.Status
263
		if($statusCode === false) {
264
			// Translate from the DNDataTransfer.Status to the Resque job status for UI purposes
265
			switch($this->Status) {
266
				case 'Finished':
267
					return 'Complete';
268
				case 'Started':
269
					return 'Running';
270
				default:
271
					return $this->Status;
272
			}
273
		}
274
		return self::map_resque_status($statusCode);
275
	}
276
277
}
278