<?php
/*
 * Copyright (c) 2025, Tribal Limited
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Zenario, Tribal Limited nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL TRIBAL LTD BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
if (!defined('NOT_ACCESSED_DIRECTLY')) exit('This file may not be directly accessed');

class zenario_videos_manager__admin_boxes__videos_manager__video extends zenario_videos_manager {
	
	public function fillAdminBox($path, $settingGroup, &$box, &$fields, &$values) {
		if (!ze\priv::check('_PRIV_MANAGE_VIDEOS')) {
			$box['tabs']['details']['edit_mode']['enabled'] = false;
			$box['tabs']['details']['edit_mode']['on'] = false;
		}
		
		$videoCategories = ze\row::getAssocs(ZENARIO_VIDEOS_MANAGER_PREFIX . 'categories', 'name');
		
		$categoriesPanelHref = ze\link::absolute() . 'organizer.php#zenario_videos_manager/panels/categories';
		$linkStart = '<a href="' . htmlspecialchars($categoriesPanelHref) . '" target="_blank">';
		$linkEnd = "</a>";
		
		$fields['details/no_categories']['snippet']['html'] = ze\admin::phrase(
			'No video categories have been created. [[Link_start]]Create categories...[[Link_end]]',
			['Link_start' => $linkStart, 'Link_end' => $linkEnd]
		);

		if (!empty($videoCategories)) {
			$fields['details/categories']['values'] = $videoCategories;
		} else {
			$fields['details/categories']['hidden'] = true;
			$fields['details/no_categories']['hidden'] = false;
		}
		
		//Load languages - only when zenario_document_envelopes_fea module is running
		//(soft depengency)
		$documentEnvelopesModuleIsRunning = ze\module::inc('zenario_document_envelopes_fea');
		if ($documentEnvelopesModuleIsRunning) {
			$fields['details/language_id']['values'] = ze\dataset::centralisedListValues('zenario_document_envelopes_fea::getEnvelopeLanguages');
			$fields['details/language_id']['hidden'] = false;
			
			$href = ze\link::absolute() . 'organizer.php#zenario__document_envelopes/panels/envelope_languages';
			$fields['details/language_id']['note_below'] = ze\admin::phrase('<a target="_blank" href=[[href]]>Click here to manage languages</a>', ['href' => $href]);
			
			if (!ze::setting('video_language_is_mandatory')) {
				unset($fields['details/language_id']['validation']);
			}
		}
		
		if ($box['key']['id']) {
			$video = ze\row::get(ZENARIO_VIDEOS_MANAGER_PREFIX . 'videos', true, $box['key']['id']);
			
			$box['title'] = ze\admin::phrase('Editing the video "[[title]]"', $video);
			$box['identifier']['value'] = $box['key']['id'];
			$box['key']['thumbnail_id_on_load'] = $values['details/image'] = (int) $video['image_id'];
			
			$values['details/url'] = $video['url'];
			$values['details/start_time'] = $video['start_time'];
			$values['details/title'] = $video['title'];
			$values['details/short_description'] = $video['short_description'];
			$values['details/description'] = $video['description'];
			$values['details/date'] = $video['date'];
			$values['details/language_id'] = $video['language_id'];
			
			$categories = [];
			$result = ze\row::query(ZENARIO_VIDEOS_MANAGER_PREFIX . 'category_video_link', ['category_id'], ['video_id' => $box['key']['id']]);
			while ($row = ze\sql::fetchAssoc($result)) {
				$categories[] = $row['category_id'];
			}
			$values['details/categories'] = implode(',', $categories);
			
			$box['last_updated'] = ze\admin::formatLastUpdated($video);
			
			if (ze::setting('vimeo_access_token')) {
				$parsed = parse_url($video['url']);
				if ($parsed) {
					$url = false;
					if (isset($parsed['host'])) {
						if (strpos($parsed['host'], 'vimeo.com') !== false) {
							$vimeoPrivacySettingsFormattedNicely = zenario_videos_manager::getVimeoPrivacySettingsFormattedNicely();
					
							if ($video['vimeo_privacy_setting'] && array_key_exists($video['vimeo_privacy_setting'], $vimeoPrivacySettingsFormattedNicely)) {
								$privacyString = $vimeoPrivacySettingsFormattedNicely[$video['vimeo_privacy_setting']]['note'];
								ze\lang::applyMergeFields($privacyString, ['code' => $video['vimeo_privacy_setting'], 'date_time' => ze\date::formatRelativeDateTime(strtotime($video['vimeo_privacy_last_cached']))]);
							} else {
								$privacyString = $this->phrase('Sorry, cannot fetch privacy setting');
							}
							
							$fields['details/video_privacy']['snippet']['html'] = $privacyString;
						}
					}
				}
			}
		} else {
			if ($box['key']['from_video_upload']) {
				$box['title'] = ze\admin::phrase('Upload successful');
				$fields['details/url']['readonly'] = true;
			}
		}

		//Don't let the admin use the "Fetch" button if the API key is missing.
		if (!ze::setting('vimeo_access_token')) {
			$fields['details/fetch_vimeo_details']['disabled'] = true;

			$href = 'organizer.php#zenario__administration/panels/site_settings//api_keys~.site_settings~tzenario_videos_manager__vimeo~k{"id"%3A"api_keys"}';
			$linkStart = '<a href="' . htmlspecialchars($href) . '" target="_blank">';
			$linkEnd = '</a>';
			
			$fields['details/fetch_vimeo_details']['note_below'] = ze\admin::phrase(
				'The Vimeo API key is missing, so fetching data from Vimeo is disabled. An API key can be added in the [[link_start]]Site Settings[[link_end]].',
				['link_start' => $linkStart, 'link_end' => $linkEnd]
			);
		}
	}
	
	public function formatAdminBox($path, $settingGroup, &$box, &$fields, &$values, $changes) {
		$fields['details/fetch_youtube_details']['hidden'] = $fields['details/fetch_vimeo_details']['hidden'] = true;

		$parsed = [];
		$fields['details/video_privacy']['hidden'] = false;
		if ($values['details/url']) {
			$parsed = parse_url($values['details/url']);

			if (isset($parsed['host'])) {
				if (strpos($parsed['host'], 'youtube.com') !== false || strpos($parsed['host'], 'youtu.be') !== false) {
					$fields['details/fetch_youtube_details']['hidden'] = false;
					
					$noteBelowText = 'Enter a URL on YouTube, e.g. https://youtu.be/12345. You can then press the "Fetch" button to get the video\'s thumbnail and descriptive text.';
					
					$noticeBelowText = "
						You may enter an additional start time argument.
						For example, for YouTube, enter \"t=120\" to start the video 2 minutes in.";
					
					$fields['details/video_privacy']['hidden'] = true;
				} elseif (strpos($parsed['host'], 'vimeo.com') !== false) {
					$fields['details/fetch_vimeo_details']['hidden'] = false;
					
					$noteBelowText = 'Enter a URL on Vimeo, e.g. https://vimeo.com/12345 or https://vimeo.com/12345/98765 if the video is unlisted. You can then press the "Fetch" button to get the video\'s thumbnail and descriptive text.';
					
					$noticeBelowText = "
						You may enter an additional start time argument.
						For example, for Vimeo, enter \"t=2m\" to start the video 2 minutes in.";
				}
			}
		} else {
			$noteBelowText = 'Enter a URL on YouTube, e.g. https://youtu.be/12345 or Vimeo, e.g. https://vimeo.com/12345 or https://vimeo.com/12345/98765 if the video is unlisted. You can then press the "Fetch" button to get the video\'s thumbnail and descriptive text.';
			$noticeBelowText = "
				You may enter an additional start time argument.
				For example, for YouTube, enter \"t=120\" to start the video 2 minutes in.
				For Vimeo, enter \"t=2m\" to start the video 2 minutes in.";
		}
		
		$fields['details/url']['note_below'] = ze\admin::phrase($noteBelowText);
		$fields['details/start_time']['notices_below']['start_time_info']['message'] = ze\admin::phrase($noticeBelowText);
		
		//"Fetch thumbnail and title" feature:
		if (!empty($fields['details/fetch_youtube_details']['pressed'])) {
			//YouTube version
			if ($values['details/url'] && $parsed) {
				if (strpos($parsed['host'], 'youtube.com') !== false || strpos($parsed['host'], 'youtu.be') !== false) {
					$videoId = false;
					if (strpos($parsed['host'], 'youtube.com') !== false) {
						$videoId = substr($parsed['query'], 2);
					} elseif (strpos($parsed['host'], 'youtu.be') !== false) {
						$videoId = substr($parsed['path'], 1);
					}

					$thumbnailUrl = false;
					$apiUrl = 'https://www.youtube.com/oembed?format=json&url=http%3A//youtube.com/watch%3Fv%3D' . htmlspecialchars($videoId);
					$data = file_get_contents($apiUrl);
					if (!empty($data)) {
						$json = json_decode($data, true);
					}
					
					if ($videoId && !empty($json)) {
						if (!$values['details/title'] && !empty($json['title'])) {
							$values['details/title'] = $json['title'];
						}

						if (!empty($json['thumbnail_url'])) {
							//Check if there is a thumbnail available.
							//There may not be a max resolution thumbnail, so try smaller ones if needed.
							$thumbnailUrl = $json['thumbnail_url'];

							$sha = sha1($thumbnailUrl);
							if (!\ze\cache::cleanDirs() || !($dir = \ze\cache::createDir($sha, 'private/uploads', false))) {
								echo ze\admin::phrase('Zenario cannot currently receive uploaded files, because the private/ folder is not writeable.');
							} else {
								$filename = 'video_' . ze\escape::sql($videoId) . '_thumbnail.jpg';
								$safeFileName = ze\file::safeName($filename);

								$failed = false;
								if (!file_exists($path = CMS_ROOT. $dir. $safeFileName) || !filesize($path = CMS_ROOT. $dir. $safeFileName)) {
									touch($path);
									ze\cache::chmod($path, 0666);

									if ($in = fopen($thumbnailUrl, 'r')) {
										$out = fopen($path, 'w');
										while (!feof($in)) {
											fwrite($out, fread($in, 65536));
										}
										fclose($out);
										fclose($in);
										
										clearstatcache();
										$failed = !filesize($path);
									}
								}

								if (!$failed && ($mimeType = ze\file::mimeType($safeFileName)) && (ze\file::isImage($mimeType)) && ($image = @getimagesize($path))) {
									$file = [
										'filename' => $safeFileName,
										'width' => $image[0],
										'height' => $image[1],
									];
									$file['id'] = ze\ring::encodeIdForOrganizer($sha. '/'. $safeFileName. '/'. $file['width']. '/'. $file['height']);
									$file['link'] = 'zenario/preview_uploaded_file.php?uploadCode='. $file['id'];
									$file['label'] = $safeFileName . ' [' . $file['width'] . ' × ' . $file['height'] . ']';

									$fields['details/image']['values'][$file['id']] = $file;
									$values['details/image'] = $file['id'];
								}
							}
						}
					} else {
						$fields['details/fetch_youtube_details']['error'] = ze\admin::phrase("Video details not found. Please make sure the link is valid.");
					}
				}
			} else {
				$fields['details/url']['error'] = ze\admin::phrase("Please enter a valid URL beginning with https://");
			}
		} elseif (!empty($fields['details/fetch_vimeo_details']['pressed'])) {
			//Vimeo version
			$fields['details/video_privacy']['snippet']['html'] = '';
			if ($values['details/url'] && $parsed) {
				if (strpos($parsed['host'], 'vimeo.com') !== false) {

					if (strpos($values['details/url'], 'manage') !== false) {
						$fields['details/url']['error'] = ze\admin::phrase('It looks like you entered a Vimeo video management link. Please check the URL again.');
					} else {
						$videoId = $parsed['path'];
						if (substr($videoId, 0, 1) == '/') {
							$videoId = substr($videoId, 1);
						}
						
						if (($forwardSlashPos = strpos($videoId, '/')) !== false) {
							$videoId = substr($videoId, 0, $forwardSlashPos);
						}
						
						$thumbnailUrl = false;

						$videoData = zenario_videos_manager::getVimeoVideoData($videoId);

						if (!empty($videoData) && is_array($videoData)) {
							$params = ['url' => $videoData['link'], 'autoplay' => true];
							$url = "https://vimeo.com/api/oembed.json?" . http_build_query($params);
							
							if ($url) {
								$result = ze\curl::fetch($url);
								if ($result && ($json = json_decode($result, true))) {
									$box['tabs']['details']['notices']['connection_via_api_key_successful']['show'] = true;
									$fields['details/url']['note_below'] = ze\admin::phrase('Connection via API key is successful.') . ' ' . $fields['details/url']['note_below'];
									
									if (!$values['details/title'] && !empty($json['title'])) {
										$values['details/title'] = $json['title'];
									}

									if (!empty($json['thumbnail_url'])) {
										//Check if there is a thumbnail available.
										//There may not be a max resolution thumbnail, so try smaller ones if needed.
										$thumbnailUrl = $json['thumbnail_url'];
			
										$sha = sha1($thumbnailUrl);
										if (!\ze\cache::cleanDirs() || !($dir = \ze\cache::createDir($sha, 'private/uploads', false))) {
											echo ze\admin::phrase('Zenario cannot currently receive uploaded files, because the private/ folder is not writeable.');
										} else {
											$filename = 'video_' . ze\escape::sql($videoId) . '_thumbnail.jpg';
											$safeFileName = ze\file::safeName($filename);
			
											$failed = false;
											if (!file_exists($path = CMS_ROOT. $dir. $safeFileName) || !filesize($path = CMS_ROOT. $dir. $safeFileName)) {
												touch($path);
												ze\cache::chmod($path, 0666);
			
												if ($in = fopen($thumbnailUrl, 'r')) {
													$out = fopen($path, 'w');
													while (!feof($in)) {
														fwrite($out, fread($in, 65536));
													}
													fclose($out);
													fclose($in);
													
													clearstatcache();
													$failed = !filesize($path);
												}
											}
			
											if (!$failed && ($mimeType = ze\file::mimeType($safeFileName)) && (ze\file::isImage($mimeType)) && ($image = @getimagesize($path))) {
												$file = [
													'filename' => $safeFileName,
													'width' => $image[0],
													'height' => $image[1],
												];
												$file['id'] = ze\ring::encodeIdForOrganizer($sha. '/'. $safeFileName. '/'. $file['width']. '/'. $file['height']);
												$file['link'] = 'zenario/preview_uploaded_file.php?uploadCode='. $file['id'];
												$file['label'] = $safeFileName . ' [' . $file['width'] . ' × ' . $file['height'] . ']';
			
												$fields['details/image']['values'][$file['id']] = $file;
												$values['details/image'] = $file['id'];
											}
											
											$vimeoPrivacySettingsFormattedNicely = zenario_videos_manager::getVimeoPrivacySettingsFormattedNicely($creatingNewVideo = true);
					
											$privacy = $videoData['privacy']['view'] ?? '';
											
											if ($privacy && array_key_exists($privacy, $vimeoPrivacySettingsFormattedNicely)) {
												$privacyString = $vimeoPrivacySettingsFormattedNicely[$privacy]['note'];
											} else {
												$privacyString = $this->phrase('Sorry, cannot fetch privacy setting');
											}
							
											$fields['details/video_privacy']['snippet']['html'] = $privacyString;
										}
									}
								} else {
									$fields['details/fetch_vimeo_details']['error'] = ze\admin::phrase("Video details not found. Please make sure the link is valid.");
								}
							}
						} else {
							$fields['details/fetch_vimeo_details']['error'] = ze\admin::phrase("Video details not found. Please make sure the link is valid.");
						}
					}
				}
			} else {
				$fields['details/url']['error'] = ze\admin::phrase("Please enter a valid URL beginning with https://");
			}
		}

		unset($fields['details/fetch_youtube_details']['pressed']);
		unset($fields['details/fetch_vimeo_details']['pressed']);
	}
	
	public function validateAdminBox($path, $settingGroup, &$box, &$fields, &$values, $changes, $saving) {
		if ($values['details/url']) {
			if (!filter_var($values['details/url'], FILTER_VALIDATE_URL)) {
				$fields['details/url']['error'] = ze\admin::phrase("Please enter a valid URL beginning with https://");
			} else {
				$parsed = parse_url($values['details/url']);
	
				if (!empty($parsed) && !empty($parsed['host']) && strpos($parsed['host'], 'vimeo.com') !== false) {
					if (strpos($values['details/url'], 'manage') !== false) {
						$fields['details/url']['error'] = ze\admin::phrase('It looks like you entered a Vimeo video management link. Please check the URL again.');
					}
				}
				
				//Check if the URL is unique. If there already is some error with the URL,
				//let the admin deal with that first before checking for uniqueness.
				if (empty($fields['details/url']['error'])) {
					$whereStatement = ['url' => $values['details/url']];
					
					if ($box['key']['id']) {
						$whereStatement['id'] = ['!' => $box['key']['id']];
					}
					
					$urlIsNotUnique = ze\row::exists(ZENARIO_VIDEOS_MANAGER_PREFIX . 'videos', $whereStatement);
					
					if ($urlIsNotUnique) {
						$fields['details/url']['error'] = ze\admin::phrase('One or more videos with this URL already exist.');
					}
				}
			}
		}
		
		if ($values['details/start_time']) {
			if (strstr($values['details/start_time'], '=') === false || count(explode('=', $values['details/start_time'])) != 2) {
				$fields['details/start_time']['error'] = ze\admin::phrase('The start time must have exactly one "=" symbol.');
			}
		}
	}
	
	public function saveAdminBox($path, $settingGroup, &$box, &$fields, &$values, $changes) {
		ze\priv::exitIfNot('_PRIV_MANAGE_VIDEOS');
		
		$imageId = $values['details/image'];
		if ($filepath = ze\file::getPathOfUploadInCacheDir($imageId)) {
			$imageId = ze\fileAdm::addToDatabase(
				'zenario_video_image', $filepath, $filename = false, $mustBeAnImage = true,
				$deleteWhenDone = false, $addToDocstoreDirIfPossible = false,
				$imageAltTag = false, $imageTitle = false, $imagePopoutTitle = false, $imageMimeType = false, $imageCredit = '',
				//Video thumbnails must always be public, regardless of the default privacy setting
				$setPrivacy = 'public'
			);
		}
		
		$videoDetails = [
			'url' => mb_substr($values['details/url'], 0, 255, 'UTF-8'),
			'start_time' => mb_substr($values['details/start_time'], 0, 255, 'UTF-8'),
			'image_id' => (int)$imageId,
			'title' => mb_substr($values['details/title'], 0, 255, 'UTF-8'),
			'short_description' => mb_substr($values['details/short_description'], 0, 65535, 'UTF-8'),
			'description' => mb_substr(ze\ring::sanitiseWYSIWYGEditorHTML($values['details/description']), 0, 65535, 'UTF-8'),
			'date' => $values['details/date']
		];
		
		if (!$box['key']['id']) {
			$parsed = parse_url($values['details/url']);
			if ($parsed) {
				$url = false;
				if (isset($parsed['host'])) {
					if (strpos($parsed['host'], 'vimeo.com') !== false) {
						$vimeoVideoId = $parsed['path'];
						if (substr($vimeoVideoId, 0, 1) == '/') {
							$vimeoVideoId = substr($vimeoVideoId, 1);
						}
						
						if (($forwardSlashPos = strpos($vimeoVideoId, '/')) !== false) {
							$vimeoVideoId = substr($vimeoVideoId, 0, $forwardSlashPos);
						}
						
						$videoData = zenario_videos_manager::getVimeoVideoData($vimeoVideoId);
						
						$dateNow = ze\date::now();
						
						$videoDetails['vimeo_privacy_setting'] = ze\escape::sql($videoData['privacy']['view'] ?? '');
						$videoDetails['vimeo_privacy_last_cached'] = ze\escape::sql($dateNow);
					}
				}
			}
		}
		
		$documentEnvelopesModuleIsRunning = ze\module::inc('zenario_document_envelopes_fea');
		if ($documentEnvelopesModuleIsRunning && $values['details/language_id']) {
			$videoDetails['language_id'] = $values['details/language_id'];
		} else {
			$videoDetails['language_id'] = false;
		}
		
		ze\admin::setLastUpdated($videoDetails, !$box['key']['id']);
		
		$box['key']['id'] = ze\row::set(ZENARIO_VIDEOS_MANAGER_PREFIX . 'videos', $videoDetails, $box['key']['id']);
		
		ze\row::delete(ZENARIO_VIDEOS_MANAGER_PREFIX . 'category_video_link', ['video_id' => $box['key']['id']]);
		if ($categories = $values['details/categories']) {
			foreach (ze\ray::explodeAndTrim($categories) as $categoryId) {
				ze\row::insert(ZENARIO_VIDEOS_MANAGER_PREFIX . 'category_video_link', ['video_id' => $box['key']['id'], 'category_id' => $categoryId]);
			}
		}
		
		//If the thumbnail was replaced with a different one, remove the old one if it is no longer used.
		if ($imageId && ($box['key']['thumbnail_id_on_load'] != $imageId)) {
			$sql = "
				SELECT COUNT(*)
				FROM " . DB_PREFIX . ZENARIO_VIDEOS_MANAGER_PREFIX . "videos
				WHERE id != " . (int) $box['key']['id'] . "
				AND image_id = " . (int) $box['key']['thumbnail_id_on_load'];
			$result = ze\sql::select($sql);
			$count = ze\sql::fetchValue($result);
			
			if (!$count) {
				ze\row::delete('files', ['id' => $box['key']['thumbnail_id_on_load'], 'usage' => 'zenario_video_image']);
			}
		}
	}
}