Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Sources/Likes.php (3 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This file contains liking posts and displaying the list of who liked a post.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Class Likes
21
 */
22
class Likes
23
{
24
	/**
25
	 *@var boolean Know if a request comes from an ajax call or not, depends on $_GET['js'] been set.
26
	 */
27
	protected $_js = false;
28
29
	/**
30
	 *@var string If filled, its value will contain a string matching a key on a language var $txt[$this->_error]
31
	 */
32
	protected $_error = false;
33
34
	/**
35
	 *@var string The unique type to like, needs to be unique and it needs to be no longer than 6 characters, only numbers and letters are allowed.
36
	 */
37
	protected $_type = '';
38
39
	/**
40
	 *@var string A generic string used if you need to pass any extra info. It gets set via $_GET['extra'].
41
	 */
42
	protected $_extra = false;
43
44
	/**
45
	 *@var integer a valid ID to identify your like content.
46
	 */
47
	protected $_content = 0;
48
49
	/**
50
	 *@var integer The number of times your content has been liked.
51
	 */
52
	protected $_numLikes = 0;
53
54
	/**
55
	 *@var boolean If the current user has already liked this content.
56
	 */
57
	protected $_alreadyLiked = false;
58
59
	/**
60
	 * @var array $_validLikes mostly used for external integration, needs to be filled as an array with the following keys:
61
	 * => 'can_see' boolean|string whether or not the current user can see the like.
62
	 * => 'can_like' boolean|string whether or not the current user can actually like your content.
63
	 * for both can_like and can_see: Return a boolean true if the user can, otherwise return a string, the string will be used as key in a regular $txt language error var. The code assumes you already loaded your language file. If no value is returned or the $txt var isn't set, the code will use a generic error message.
64
	 * => 'redirect' string To add support for non JS users, It is highly encouraged to set a valid URL to redirect the user to, if you don't provide any, the code will redirect the user to the main page. The code only performs a light check to see if the redirect is valid so be extra careful while building it.
65
	 * => 'type' string 6 letters or numbers. The unique identifier for your content, the code doesn't check for duplicate entries, if there are 2 or more exact hook calls, the code will take the first registered one so make sure you provide a unique identifier. Must match with what you sent in $_GET['ltype'].
66
	 * => 'flush_cache' boolean this is optional, it tells the code to reset your like content's cache entry after a new entry has been inserted.
67
	 * => 'callback' callable optional, useful if you don't want to issue a separate hook for updating your data, it is called immediately after the data was inserted or deleted and before the actual hook. Uses call_helper(); so the same format for your function/method can be applied here.
68
	 * => 'json' boolean optional defaults to false, if true the Like class will return a json object as response instead of HTML.
69
	 */
70
	protected $_validLikes = array(
71
		'can_see' => false,
72
		'can_like' => false,
73
		'redirect' => '',
74
		'type' => '',
75
		'flush_cache' => '',
76
		'callback' => false,
77
		'json' => false,
78
	);
79
80
	/**
81
	 * @var array The current user info ($user_info).
82
	 */
83
	protected $_user;
84
85
	/**
86
	 * @var integer The topic ID, used for liking messages.
87
	 */
88
	protected $_idTopic = 0;
89
90
	/**
91
	 * @var boolean to know if response(); will be executed as normal. If this is set to false it indicates the method already solved its own way to send back a response.
92
	 */
93
	protected $_setResponse = true;
94
95
	/**
96
	 * Likes::__construct()
97
	 *
98
	 * Sets the basic data needed for the rest of the process.
99
	 */
100
	public function __construct()
101
	{
102
		global $db_show_debug;
103
104
		$this->_type = isset($_GET['ltype']) ? $_GET['ltype'] : '';
105
		$this->_content = isset($_GET['like']) ? (int) $_GET['like'] : 0;
106
		$this->_js = isset($_GET['js']) ? true : false;
107
		$this->_sa = isset($_GET['sa']) ? $_GET['sa'] : 'like';
0 ignored issues
show
The property _sa does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
108
		$this->_extra = isset($_GET['extra']) ? $_GET['extra'] : false;
109
110
		// We do not want to output debug information here.
111
		if ($this->_js)
112
			$db_show_debug = false;
113
	}
114
115
	/**
116
	 * Likes::call()
117
	 *
118
	 * The main handler. Verifies permissions (whether the user can see the content in question), dispatch different method for different sub-actions.
119
	 * Accessed from index.php?action=likes
120
	 */
121
	public function call()
122
	{
123
		global $context;
124
125
		$this->_user = $context['user'];
126
127
		// Make sure the user can see and like your content.
128
		$this->check();
129
130
		$subActions = array(
131
			'like',
132
			'view',
133
			'delete',
134
			'insert',
135
			'_count',
136
		);
137
138
		// So at this point, whatever type of like the user supplied and the item of content in question,
139
		// we know it exists, now we need to figure out what we're doing with that.
140
		if (in_array($this->_sa, $subActions) && !is_string($this->_error))
141
		{
142
			// To avoid ambiguity, turn the property to a normal var.
143
			$call = $this->_sa;
144
145
			// Guest can only view likes.
146
			if ($call != 'view')
147
				is_not_guest();
148
149
			checkSession('get');
150
151
			// Call the appropriate method.
152
			$this->$call();
153
		}
154
155
		// else An error message.
156
		$this->response();
157
	}
158
159
	/**
160
	 * Likes::get()
161
	 *
162
	 * A simple getter for all protected properties.
163
	 * Accessed from index.php?action=likes
164
	 * @param string $property The name of the property to get.
165
	 * @return mixed Either return the property or false if there isn't a property with that name.
166
	 */
167
	public function get($property = '')
168
	{
169
		// All properties inside Likes are protected, thus, an underscore is used.
170
		$property = '_' . $property;
171
		return property_exists($this, $property) ? $this->$property : false;
172
	}
173
174
	/**
175
	 * Likes::check()
176
	 *
177
	 * Performs basic checks on the data provided, checks for a valid msg like.
178
	 * Calls integrate_valid_likes hook for retrieving all the data needed and apply checks based on the data provided.
179
	 */
180
	protected function check()
181
	{
182
		global $smcFunc, $modSettings;
183
184
		// This feature is currently disable.
185
		if (empty($modSettings['enable_likes']))
186
			return $this->_error = 'like_disable';
187
188
		// Zerothly, they did indicate some kind of content to like, right?
189
		preg_match('~^([a-z0-9\-\_]{1,6})~i', $this->_type, $matches);
190
		$this->_type = isset($matches[1]) ? $matches[1] : '';
191
192
		if ($this->_type == '' || $this->_content <= 0)
193
			return $this->_error = 'cannot_';
194
195
		// First we need to verify if the user can see the type of content or not. This is set up to be extensible,
196
		// so we'll check for the one type we do know about, and if it's not that, we'll defer to any hooks.
197
		if ($this->_type == 'msg')
198
		{
199
			// So we're doing something off a like. We need to verify that it exists, and that the current user can see it.
200
			// Fortunately for messages, this is quite easy to do - and we'll get the topic id while we're at it, because
201
			// we need this later for other things.
202
			$request = $smcFunc['db_query']('', '
203
				SELECT m.id_topic, m.id_member
204
				FROM {db_prefix}messages AS m
205
					INNER JOIN {db_prefix}boards AS b ON (m.id_board = b.id_board)
206
				WHERE {query_see_board}
207
					AND m.id_msg = {int:msg}',
208
				array(
209
					'msg' => $this->_content,
210
				)
211
			);
212
			if ($smcFunc['db_num_rows']($request) == 1)
213
				list ($this->_idTopic, $topicOwner) = $smcFunc['db_fetch_row']($request);
214
215
			$smcFunc['db_free_result']($request);
216
			if (empty($this->_idTopic))
217
				return $this->_error = 'cannot_';
218
219
			// So we know what topic it's in and more importantly we know the user can see it.
220
			// If we're not viewing, we need some info set up.
221
			$this->_validLikes['type'] = 'msg';
222
			$this->_validLikes['flush_cache'] = 'likes_topic_' . $this->_idTopic . '_' . $this->_user['id'];
223
			$this->_validLikes['redirect'] = 'topic=' . $this->_idTopic . '.msg' . $this->_content . '#msg' . $this->_content;
224
			$this->_validLikes['can_see'] = allowedTo('likes_view') ? true : 'cannot_view_likes';
225
226
			$this->_validLikes['can_like'] = ($this->_user['id'] == $topicOwner ? 'cannot_like_content' : (allowedTo('likes_like') ? true : 'cannot_like_content'));
0 ignored issues
show
The variable $topicOwner does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
227
		}
228
229
		else
230
		{
231
			// Modders: This will give you whatever the user offers up in terms of liking, e.g. $this->_type=msg, $this->_content=1
232
			// When you hook this, check $this->_type first. If it is not something your mod worries about, return false.
233
			// Otherwise, fill an array according to the docs for $this->_validLikes. Determine (however you need to) that the user can see and can_like the relevant liked content (and it exists) Remember that users can't like their own content.
234
			// If the user cannot see it, return the appropriate key (can_see) as false. If the user can see it and can like it, you MUST return your type in the 'type' key back.
235
			// See also issueLike() for further notes.
236
			$can_like = call_integration_hook('integrate_valid_likes', array($this->_type, $this->_content, $this->_sa, $this->_js, $this->_extra));
237
238
			$found = false;
239
			if (!empty($can_like))
240
			{
241
				$can_like = (array) $can_like;
242
				foreach ($can_like as $result)
243
				{
244
					if ($result !== false)
245
					{
246
						// Match the type with what we already have.
247
						if (!isset($result['type']) || $result['type'] != $this->_type)
248
							return $this->_error = 'not_valid_like_type';
249
250
						// Fill out the rest.
251
						$this->_type = $result['type'];
252
						$this->_validLikes = array_merge($this->_validLikes, $result);
253
						$found = true;
254
						break;
255
					}
256
				}
257
			}
258
259
			if (!$found)
260
				return $this->_error = 'cannot_';
261
		}
262
263
		// Does the user can see this?
264 View Code Duplication
		if (isset($this->_validLikes['can_see']) && is_string($this->_validLikes['can_see']))
265
			return $this->_error = $this->_validLikes['can_see'];
266
267
		// Does the user can like this? Viewing a list of likes doesn't require this permission.
268 View Code Duplication
			if ($this->_sa != 'view' && isset($this->_validLikes['can_like']) && is_string($this->_validLikes['can_like']))
269
				return $this->_error = $this->_validLikes['can_like'];
270
	}
271
272
	/**
273
	 * Likes::delete()
274
	 *
275
	 * Deletes an entry from user_likes table, needs 3 properties: $_content, $_type and $_user['id'].
276
	 */
277
	protected function delete()
278
	{
279
		global $smcFunc;
280
281
		$smcFunc['db_query']('', '
282
			DELETE FROM {db_prefix}user_likes
283
			WHERE content_id = {int:like_content}
284
				AND content_type = {string:like_type}
285
				AND id_member = {int:id_member}',
286
			array(
287
				'like_content' => $this->_content,
288
				'like_type' => $this->_type,
289
				'id_member' => $this->_user['id'],
290
			)
291
		);
292
293
		// Are we calling this directly? if so, set a proper data for the response. Do note that __METHOD__ returns both the class name and the function name.
294
		if ($this->_sa == __FUNCTION__)
295
			$this->_data = __FUNCTION__;
0 ignored issues
show
The property _data does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
296
	}
297
298
	/**
299
	 * Likes::insert()
300
	 *
301
	 * Inserts a new entry on user_likes table. Creates a background task for the inserted entry.
302
	 */
303
	protected function insert()
304
	{
305
		global $smcFunc;
306
307
		// Any last minute changes? Temporarily turn the passed properties to normal vars to prevent unexpected behaviour with other methods using these properties.
308
		$type = $this->_type;
309
		$content = $this->_content;
310
		$user = $this->_user;
311
		$time = time();
312
313
		call_integration_hook('integrate_issue_like_before', array(&$type, &$content, &$user, &$time));
314
315
		// Insert the like.
316
		$smcFunc['db_insert']('insert',
317
			'{db_prefix}user_likes',
318
			array('content_id' => 'int', 'content_type' => 'string-6', 'id_member' => 'int', 'like_time' => 'int'),
319
			array($content, $type, $user['id'], $time),
320
			array('content_id', 'content_type', 'id_member')
321
		);
322
323
		// Add a background task to process sending alerts.
324
		// Mod author, you can add your own background task for your own custom like event using the "integrate_issue_like" hook or your callback, both are immediately called after this.
325 View Code Duplication
		if ($this->_type == 'msg')
326
			$smcFunc['db_insert']('insert',
327
				'{db_prefix}background_tasks',
328
				array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
329
				array('$sourcedir/tasks/Likes-Notify.php', 'Likes_Notify_Background', $smcFunc['json_encode'](array(
330
					'content_id' => $content,
331
					'content_type' => $type,
332
					'sender_id' => $user['id'],
333
					'sender_name' => $user['name'],
334
					'time' => $time,
335
				)), 0),
336
				array('id_task')
337
			);
338
339
		// Are we calling this directly? if so, set a proper data for the response. Do note that __METHOD__ returns both the class name and the function name.
340
		if ($this->_sa == __FUNCTION__)
341
			$this->_data = __FUNCTION__;
342
	}
343
344
	/**
345
	 * Likes::_count()
346
	 *
347
	 * Sets $_numLikes with the actual number of likes your content has, needs two properties: $_content and $_view. When called directly it will return the number of likes as response.
348
	 */
349
	protected function _count()
350
	{
351
		global $smcFunc;
352
353
		$request = $smcFunc['db_query']('', '
354
			SELECT COUNT(id_member)
355
			FROM {db_prefix}user_likes
356
			WHERE content_id = {int:like_content}
357
				AND content_type = {string:like_type}',
358
			array(
359
				'like_content' => $this->_content,
360
				'like_type' => $this->_type,
361
			)
362
		);
363
		list ($this->_numLikes) = $smcFunc['db_fetch_row']($request);
364
		$smcFunc['db_free_result']($request);
365
366
		// If you want to call this directly, fill out _data property too.
367
		if ($this->_sa == __FUNCTION__)
368
			$this->_data = $this->_numLikes;
369
	}
370
371
	/**
372
	 * Likes::like()
373
	 *
374
	 * Performs a like action, either like or unlike. Counts the total of likes and calls a hook after the event.
375
	 */
376
	protected function like()
377
	{
378
		global $smcFunc;
379
380
		// Safety first!
381
		if (empty($this->_type) || empty($this->_content))
382
			return $this->_error = 'cannot_';
383
384
		// Do we already like this?
385
		$request = $smcFunc['db_query']('', '
386
			SELECT content_id, content_type, id_member
387
			FROM {db_prefix}user_likes
388
			WHERE content_id = {int:like_content}
389
				AND content_type = {string:like_type}
390
				AND id_member = {int:id_member}',
391
			array(
392
				'like_content' => $this->_content,
393
				'like_type' => $this->_type,
394
				'id_member' => $this->_user['id'],
395
			)
396
		);
397
		$this->_alreadyLiked = (bool) $smcFunc['db_num_rows']($request) != 0;
398
		$smcFunc['db_free_result']($request);
399
400
		if ($this->_alreadyLiked)
401
			$this->delete();
402
403
		else
404
			$this->insert();
405
406
		// Now, how many people like this content now? We *could* just +1 / -1 the relevant container but that has proven to become unstable.
407
		$this->_count();
408
409
		// Update the likes count for messages.
410
		if ($this->_type == 'msg')
411
			$this->msgIssueLike();
412
413
		// Any callbacks?
414
		elseif (!empty($this->_validLikes['callback']))
415
		{
416
			$call = call_helper($this->_validLikes['callback'], true);
417
418
			if (!empty($call))
419
				call_user_func_array($call, array($this));
420
		}
421
422
		// Sometimes there might be other things that need updating after we do this like.
423
		call_integration_hook('integrate_issue_like', array($this));
424
425
		// Now some clean up. This is provided here for any like handlers that want to do any cache flushing.
426
		// This way a like handler doesn't need to explicitly declare anything in integrate_issue_like, but do so
427
		// in integrate_valid_likes where it absolutely has to exist.
428
		if (!empty($this->_validLikes['flush_cache']))
429
			cache_put_data($this->_validLikes['flush_cache'], null);
430
431
		// All done, start building the data to pass as response.
432
		$this->_data = array(
433
			'id_topic' => !empty($this->_idTopic) ? $this->_idTopic : 0,
434
			'id_content' => $this->_content,
435
			'count' => $this->_numLikes,
436
			'can_like' => $this->_validLikes['can_like'],
437
			'can_see' => $this->_validLikes['can_see'],
438
			'already_liked' => empty($this->_alreadyLiked),
439
			'type' => $this->_type,
440
		);
441
	}
442
443
	/**
444
	 * Likes::msgIssueLike()
445
	 *
446
	 * Partly it indicates how it's supposed to work and partly it deals with updating the count of likes
447
	 * attached to this message now.
448
	 */
449
	function msgIssueLike()
450
	{
451
		global $smcFunc;
452
453
		if ($this->_type !== 'msg')
454
			return;
455
456
		$smcFunc['db_query']('', '
457
			UPDATE {db_prefix}messages
458
			SET likes = {int:num_likes}
459
			WHERE id_msg = {int:id_msg}',
460
			array(
461
				'id_msg' => $this->_content,
462
				'num_likes' => $this->_numLikes,
463
			)
464
		);
465
466
		// Note that we could just as easily have cleared the cache here, or set up the redirection address
467
		// but if your liked content doesn't need to do anything other than have the record in smf_user_likes,
468
		// there's no point in creating another function unnecessarily.
469
	}
470
471
	/**
472
	 * Likes::view()
473
	 *
474
	 * This is for viewing the people who liked a thing.
475
	 * Accessed from index.php?action=likes;view and should generally load in a popup.
476
	 * We use a template for this in case themers want to style it.
477
	 */
478
	function view()
479
	{
480
		global $smcFunc, $txt, $context, $memberContext;
481
482
		// Firstly, load what we need. We already know we can see this, so that's something.
483
		$context['likers'] = array();
484
		$request = $smcFunc['db_query']('', '
485
			SELECT id_member, like_time
486
			FROM {db_prefix}user_likes
487
			WHERE content_id = {int:like_content}
488
				AND content_type = {string:like_type}
489
			ORDER BY like_time DESC',
490
			array(
491
				'like_content' => $this->_content,
492
				'like_type' => $this->_type,
493
			)
494
		);
495
		while ($row = $smcFunc['db_fetch_assoc']($request))
496
			$context['likers'][$row['id_member']] = array('timestamp' => $row['like_time']);
497
498
		// Now to get member data, including avatars and so on.
499
		$members = array_keys($context['likers']);
500
		$loaded = loadMemberData($members);
501
		if (count($loaded) != count($members))
502
		{
503
			$members = array_diff($members, $loaded);
504
			foreach ($members as $not_loaded)
505
				unset ($context['likers'][$not_loaded]);
506
		}
507
508
		foreach ($context['likers'] as $liker => $dummy)
509
		{
510
			$loaded = loadMemberContext($liker);
511
			if (!$loaded)
512
			{
513
				unset ($context['likers'][$liker]);
514
				continue;
515
			}
516
517
			$context['likers'][$liker]['profile'] = &$memberContext[$liker];
518
			$context['likers'][$liker]['time'] = !empty($dummy['timestamp']) ? timeformat($dummy['timestamp']) : '';
519
		}
520
521
		$count = count($context['likers']);
522
		$title_base = isset($txt['likes_' . $count]) ? 'likes_' . $count : 'likes_n';
523
		$context['page_title'] = strip_tags(sprintf($txt[$title_base], '', comma_format($count)));
524
525
		// Lastly, setting up for display.
526
		loadTemplate('Likes');
527
		loadLanguage('Help'); // For the close window button.
528
		$context['template_layers'] = array();
529
		$context['sub_template'] = 'popup';
530
531
		// We already took care of our response so there is no need to bother with respond();
532
		$this->_setResponse = false;
533
	}
534
535
	/**
536
	 * Likes::response()
537
	 *
538
	 * Checks if the user can use JavaScript and acts accordingly.
539
	 * Calls the appropriate sub-template for each method
540
	 * Handles error messages.
541
	 */
542
	protected function response()
543
	{
544
		global $context, $txt;
545
546
		// Don't do anything if someone else has already take care of the response.
547
		if (!$this->_setResponse)
548
			return;
549
550
		// Want a json response huh?
551
		if ($this->_validLikes['json'])
552
			return $this->jsonResponse();
553
554
		// Set everything up for display.
555
		loadTemplate('Likes');
556
		$context['template_layers'] = array();
557
558
		// If there are any errors, process them first.
559
		if ($this->_error)
560
		{
561
			// If this is a generic error, set it up good.
562
			if ($this->_error == 'cannot_')
563
				$this->_error = $this->_sa == 'view' ? 'cannot_view_likes' : 'cannot_like_content';
564
565
			// Is this request coming from an ajax call?
566
			if ($this->_js)
567
			{
568
				$context['sub_template'] = 'generic';
569
				$context['data'] = isset($txt[$this->_error]) ? $txt[$this->_error] : $txt['like_error'];
570
			}
571
572
			// Nope?  then just do a redirect to whatever URL was provided.
573 View Code Duplication
			else
574
				redirectexit(!empty($this->_validLikes['redirect']) ? $this->_validLikes['redirect'] . ';error=' . $this->_error : '');
575
576
			return;
577
		}
578
579
		// A like operation.
580
		else
581
		{
582
			// Not an ajax request so send the user back to the previous location or the main page.
583 View Code Duplication
			if (!$this->_js)
584
				redirectexit(!empty($this->_validLikes['redirect']) ? $this->_validLikes['redirect'] : '');
585
586
			// These fine gentlemen all share the same template.
587
			$generic = array('delete', 'insert', '_count');
588
			if (in_array($this->_sa, $generic))
589
			{
590
				$context['sub_template'] = 'generic';
591
				$context['data'] = isset($txt['like_' . $this->_data]) ? $txt['like_' . $this->_data] : $this->_data;
592
			}
593
594
			// Directly pass the current called sub-action and the data generated by its associated Method.
595
			else
596
			{
597
				$context['sub_template'] = $this->_sa;
598
				$context['data'] = $this->_data;
599
			}
600
		}
601
	}
602
603
	/**
604
	 * Outputs a JSON-encoded response
605
	 */
606
	protected function jsonResponse()
607
	{
608
		global $smcFunc;
609
610
		$print = array(
611
			'data' => $this->_data,
612
		);
613
614
		// If there is an error, send it.
615
		if ($this->_error)
616
		{
617
			if ($this->_error == 'cannot_')
618
				$this->_error = $this->_sa == 'view' ? 'cannot_view_likes' : 'cannot_like_content';
619
620
			$print['error'] = $this->_error;
621
		}
622
623
		// Do you want to add something at the very last minute?
624
		call_integration_hook('integrate_likes_json_response', array(&$print));
625
626
		// Print the data.
627
		smf_serverResponse($smcFunc['json_encode']($print));
628
		die;
629
	}
630
}
631
632
/**
633
 * What's this?  I dunno, what are you talking about?  Never seen this before, nope.  No sir.
634
 */
635
function BookOfUnknown()
636
{
637
	global $context, $scripturl;
638
639
	echo '<!DOCTYPE html>
640
<html', $context['right_to_left'] ? ' dir="rtl"' : '', '>
641
	<head>
642
		<title>The Book of Unknown, ', @$_GET['verse'] == '2:18' ? '2:18' : '4:16', '</title>
643
		<style>
644
			em
645
			{
646
				font-size: 1.3em;
647
				line-height: 0;
648
			}
649
		</style>
650
	</head>
651
	<body style="background-color: #444455; color: white; font-style: italic; font-family: serif;">
652
		<div style="margin-top: 12%; font-size: 1.1em; line-height: 1.4; text-align: center;">';
653
654
	if (!isset($_GET['verse']) || ($_GET['verse'] != '2:18' && $_GET['verse'] != '22:1-2'))
655
		$_GET['verse'] = '4:16';
656
657 View Code Duplication
	if ($_GET['verse'] == '2:18')
658
		echo '
659
			Woe, it was that his name wasn\'t <em>known</em>, that he came in mystery, and was recognized by none.&nbsp;And it became to be in those days <em>something</em>.&nbsp; Something not yet <em id="unknown" name="[Unknown]">unknown</em> to mankind.&nbsp; And thus what was to be known the <em>secret project</em> began into its existence.&nbsp; Henceforth the opposition was only <em>weary</em> and <em>fearful</em>, for now their match was at arms against them.';
660
	elseif ($_GET['verse'] == '4:16')
661
		echo '
662
			And it came to pass that the <em>unbelievers</em> dwindled in number and saw rise of many <em>proselytizers</em>, and the opposition found fear in the face of the <em>x</em> and the <em>j</em> while those who stood with the <em>something</em> grew stronger and came together.&nbsp; Still, this was only the <em>beginning</em>, and what lay in the future was <em id="unknown" name="[Unknown]">unknown</em> to all, even those on the right side.';
663
	elseif ($_GET['verse'] == '22:1-2')
664
		echo '
665
			<p>Now <em>behold</em>, that which was once the secret project was <em id="unknown" name="[Unknown]">unknown</em> no longer.&nbsp; Alas, it needed more than <em>only one</em>, but yet even thought otherwise.&nbsp; It became that the opposition <em>rumored</em> and lied, but still to no avail.&nbsp; Their match, though not <em>perfect</em>, had them outdone.</p>
666
			<p style="margin: 2ex 1ex 0 1ex; font-size: 1.05em; line-height: 1.5; text-align: center;">Let it continue.&nbsp; <em>The end</em>.</p>';
667
668
	echo '
669
		</div>
670
		<div style="margin-top: 2ex; font-size: 2em; text-align: right;">';
671
672 View Code Duplication
	if ($_GET['verse'] == '2:18')
673
		echo '
674
			from <span style="font-family: Georgia, serif;"><strong><a href="', $scripturl, '?action=about:unknown;verse=4:16" style="color: white; text-decoration: none; cursor: text;">The Book of Unknown</a></strong>, 2:18</span>';
675
	elseif ($_GET['verse'] == '4:16')
676
		echo '
677
			from <span style="font-family: Georgia, serif;"><strong><a href="', $scripturl, '?action=about:unknown;verse=22:1-2" style="color: white; text-decoration: none; cursor: text;">The Book of Unknown</a></strong>, 4:16</span>';
678
	elseif ($_GET['verse'] == '22:1-2')
679
		echo '
680
			from <span style="font-family: Georgia, serif;"><strong>The Book of Unknown</strong>, 22:1-2</span>';
681
682
	echo '
683
		</div>
684
	</body>
685
</html>';
686
687
	obExit(false);
688
}
689
690
?>