Issues (41)

src/protected/controllers/BackendController.php (1 issue)

1
<?php
2
3
/**
4
 * Handles the backends
5
 * 
6
 * @author Sam Stenvall <[email protected]>
7
 * @copyright Copyright &copy; Sam Stenvall 2013-
8
 * @license https://www.gnu.org/licenses/gpl.html The GNU General Public License v3.0
9
 */
10
class BackendController extends AdminOnlyController
11
{
12
	
13
	/**
14
	 * @return array the filters
15
	 */
16
	public function filters()
17
	{
18
		return array_merge(parent::filters(), array(
19
			'ajaxOnly + delete, ajaxCheckConnectivity',
20
			array('application.filters.CheckBackendConnectivityFilter + '.
21
				'updateLibrary'),
22
		));
23
	}
24
25
	/**
26
	 * Override parent implementation to allow everyone to change backend and 
27
	 * update the library
28
	 * @return array the access control rules
29
	 */
30
	public function accessRules()
31
	{
32
		$actions = array('change', 'updateLibrary', 'waitForConnectivity', 
33
				'ajaxCheckConnectivity', 'waitForLibraryUpdate');
34
35
		return array_merge(array(
36
			array('allow', 'actions'=>$actions)
37
		), parent::accessRules());
38
	}
39
40
	/**
41
	 * Override parent implementation so we don't get stuck in a redirect loop
42
	 * @param CFilterChain $filterChain
43
	 */
44
	public function filterCheckConfiguration($filterChain)
45
	{
46
		if ($this->route === 'backend/create')
47
			$filterChain->run();
48
		else
49
			parent::filterCheckConfiguration($filterChain);
50
	}
51
	
52
	/**
53
	 * Switches to the specified backend
54
	 * @param int $id the backend ID
55
	 */
56
	public function actionChange($id)
57
	{
58
		$model = $this->loadModel($id);
59
		
60
		Yii::app()->backendManager->setCurrent($model);
61
62
		$this->log('"%s" switched backend to "%s"', Yii::app()->user->name, 
63
						$model->name);
64
		Yii::app()->user->setFlash('success', Yii::t('Backend', 'Changed backend to {backendName}', 
65
				array('{backendName}'=>$model->name)));
66
67
		// Redirect to the previous page, unless that page was a details page 
68
		// or the "waiting for connectivity" page, in which case we go to the 
69
		// home URL
70
		$referrer = Yii::app()->request->urlReferrer;
71
		
72
		$invalidReferrers = array(
73
			'tvShow/details',
74
			'movie/details',
75
			'backend/waitForConnectivity');
76
77
		foreach ($invalidReferrers as $invalidReferrer)
78
			if (strpos($referrer, $invalidReferrer) !== false)
79
				$this->redirect(Yii::app()->homeUrl);
80
81
		$this->redirectToPrevious(Yii::app()->homeUrl);
82
	}
83
	
84
	/**
85
	 * Initiates a library update. The update is performed differently 
86
	 * depending on whether WebSocket connectivity to the backend is available
87
	 */
88
	public function actionUpdateLibrary()
89
	{
90
		$backend = $this->getCurrent();
91
		
92
		Yii::app()->user->setFlash('success', Yii::t('Misc', 'Library update has been initiated'));
93
94
		if (!$backend->hasWebSocketConnectivity())
95
			$this->asynchronousLibraryUpdate();
96
		else
97
			$this->synchronousLibraryUpdate();
98
	}
99
100
	/**
101
	 * Triggers an asynchronous library update, meaning there will be no way to 
102
	 * determine whether the update finished.
103
	 */
104
	private function asynchronousLibraryUpdate()
105
	{
106
		// Update the library
107
		Yii::app()->xbmc->sendNotification('VideoLibrary.Scan');
108
109
		// Remind users that they'll have to flush their cache
110
		if (Setting::getBoolean('cacheApiCalls'))
111
			Yii::app()->user->setFlash('info', Yii::t('Misc', "You'll have to flush the API call cache to see any newly scanned content"));
112
113
		$this->redirectToPrevious(Yii::app()->homeUrl);
114
	}
115
116
	/**
117
	 * Renders a waiting page which triggers actionWaitForLibraryUpdate()
118
	 */
119
	private function synchronousLibraryUpdate()
120
	{
121
		// Pass the previous location to the view so we can tell the JavaScript 
122
		// to redirect us back where we came from
123
		$previousLocation = $this->getPreviousLocation(Yii::app()->homeUrl);
124
		
125
		$this->render('waitForLibraryUpdate', array(
126
			'previousLocation'=>$previousLocation,
127
		));
128
	}
129
	
130
	/**
131
	 * Triggers a library update over a WebSocket connection and blocks the 
132
	 * request until the update has finished
133
	 */
134
	public function actionWaitForLibraryUpdate()
135
	{
136
		$backend = $this->getCurrent();
137
138
		// Create a library update listener
139
		$listener = new LibraryUpdateListener($backend);
140
141
		// Log when the scan was started and finished
142
		$listener->onScanStarted = function() use($backend)
143
		{
144
			$this->log('Library update started on %s', $backend->name);
145
		};
146
147
		$listener->onScanFinished = function() use($backend)
148
		{
149
			$this->log('Library update finished on %s', $backend->name);
150
		};
151
152
		// Wait for the library update to finish
153
		$listener->blockUntil(LibraryUpdateListener::METHOD_ON_SCAN_FINISHED);
154
		Yii::app()->user->setFlash('success', Yii::t('Misc', 'Library update completed'));
155
		
156
		// Flush the cache so potential new content will be available
157
		if (Setting::getBoolean('cacheApiCalls'))
158
			Yii::app()->apiCallCache->flush();
159
	}
160
161
	/**
162
	 * Displays the "waiting for connection" page where the user is asked to 
163
	 * wait until the backend has been woken using WOL
164
	 */
165
	public function actionWaitForConnectivity()
166
	{
167
		$backend = $this->getCurrent();
168
169
		// Determine the IP address of the backend
170
		if (filter_var($backend->hostname, FILTER_VALIDATE_IP))
171
			$ipAddress = $backend->hostname;
172
		else
173
			$ipAddress = gethostbyname($backend->hostname);
174
175
		// Send the WOL packet
176
		$wol = new \Phpwol\Factory();
177
		$magicPacket = $wol->magicPacket();
178
179
		if (!$ipAddress || !$magicPacket->send($backend->macAddress, $ipAddress, $backend->subnetMask))
180
			throw new CHttpException(500, Yii::t('Backend', 'Unable to send WOL packet'));
181
182
		// Render the "waiting" page
183
		Yii::app()->user->setFlash('error', Yii::t('Backend', 'The current backend is not connectable at the moment'));
184
185
		$this->render('waitForConnectivity');
186
	}
187
188
	/**
189
	 * AJAX polling URL which returns whether the current backend is 
190
	 * connectable or not
191
	 */
192
	public function actionAjaxCheckConnectivity()
193
	{
194
		$this->layout = false;
195
		echo json_encode(array('status'=>$this->getCurrent()->isConnectable(null, false)));
196
		Yii::app()->end();
197
	}
198
199
	/**
200
	 * Creates a new backend, then return to the admin action
201
	 */
202
	public function actionCreate()
203
	{
204
		$model = new Backend();
205
206
		if (isset($_POST['Backend']))
207
		{
208
			$model->attributes = $_POST['Backend'];
209
210
			// Check whether this is the first backend ever created, if so we 
211
			// redirect to the settings page
212
			$firstRun = $this->getCurrent() === null;
213
			
214
			if ($model->save())
215
			{
216
				$this->log('"%s" created backend "%s"', Yii::app()->user->name, 
217
						$model->name);
218
				
219
				Yii::app()->user->setFlash('success', Yii::t('Backend', 'Backend created successfully'));
220
				$this->checkWebSocketConnectivity($model);
221
				
222
				if ($firstRun)
223
				{
224
					Yii::app()->user->setFlash('info', Yii::t('Settings', 'Before you get started, please have a look at the application settings'));
225
					$this->redirect(array('setting/admin'));
226
				}
227
				else
228
					$this->redirect(array('admin'));
229
			}
230
		}
231
		else
232
		{
233
			$model->setDefaultValues();
234
235
			// Check "default" if there are no other backends
236
			$backends = Backend::model()->findAll();
237
238
			if (count($backends) === 0)
239
				$model->default = true;
0 ignored issues
show
Documentation Bug introduced by
The property $default was declared of type integer, but true is of type true. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
240
		}
241
242
		$this->render('create', array(
243
			'model'=>$model,
244
		));
245
	}
246
	
247
	/**
248
	 * Updates the specified backend, then returns to the admin action
249
	 * @param int $id the backend ID
250
	 */
251
	public function actionUpdate($id)
252
	{
253
		$model = $this->loadModel($id);
254
255
		if ($this->saveFromPost($model))
256
		{
257
			$this->log('"%s" updated backend "%s"', Yii::app()->user->name, 
258
					$model->name);
259
260
			Yii::app()->user->setFlash('success', Yii::t('Backend', 'Backend updated successfully'));
261
			$this->checkWebSocketConnectivity($model);
262
			
263
			$this->redirect(array('admin'));
264
		}
265
266
		$this->render('update', array(
267
			'model'=>$model,
268
		));
269
	}
270
	
271
	/**
272
	 * Deletes a backend
273
	 * @param int $id the backend ID
274
	 */
275
	public function actionDelete($id)
276
	{
277
		$model = $this->loadModel($id);
278
		
279
		if ($model == $this->getCurrent())
280
			throw new CHttpException(403, Yii::t('Backend', "You can't delete the current backend. Please switch to another one if you want to delete this."));
281
282
		$model->delete();
283
284
		$this->log('"%s" deleted backend "%s"', Yii::app()->user->name, 
285
						$model->name);
286
		
287
		$this->redirectOnDelete();
288
	}
289
	
290
	/**
291
	 * @return Backend the current backend
292
	 */
293
	private function getCurrent()
294
	{
295
		return Yii::app()->backendManager->getCurrent();
296
	}
297
	
298
	/**
299
	 * Checks whether the specified backend can be contacted on the 
300
	 * configured TCP port and raises a warning if it can't.
301
	 * @param Backend $backend
302
	 */
303
	private function checkWebSocketConnectivity($backend)
304
	{
305
		if (!$backend->hasWebSocketConnectivity())
306
		{
307
			Yii::app()->user->setFlash('info', 
308
					Yii::t('Backend', 'WebSocket connection not available, library updates will be performed synchronously'));
309
		}
310
	}
311
312
}