Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560 [2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85 [3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50 [4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13 [5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
| Server IP : 94.177.8.99 / Your IP : 216.73.217.165 Web Server : Apache/2.4.58 (Ubuntu) System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 User : www-data ( 33) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/hopeinstoughton_www/wp-content/plugins/xapian-search/ |
Upload File : |
<?php
/*
Plugin Name: Media Search
Plugin URI: http://hostz.org/
Description: Audio / Video Search
Version: 1.0.0
Author: Phil Young
Author URI: http://hostz.org/
License: Whatever
*/
require_once __DIR__ . '/lib/class-xap.php';
// https://github.com/DaveChild/Text-Statistics/tree/master/src/DaveChild/TextStatistics
// This uses the sshfs link to hostz.org to generate thumbnails
class media_search_class {
const PAGESIZE = 15;
const SPECIAL_PAGE = 2178; // this is our Archive Page
const EXHORT = 364;
private $header;
private $slug;
private $pagenum;
private $active_hash;
public function __construct() {
$this->DATABASE = __DIR__ . '/database';
$this->BASE = realpath(__DIR__ . '/../../..');
$this->header = FALSE;
$this->pagenum = FALSE;
$this->ActiveHash(NULL);
add_shortcode('xapian-search', [$this, '_shortcode_searchbar' ]);
add_shortcode('xapian-results', [$this, '_shortcode_searchresults' ]);
add_action( 'wp_enqueue_scripts', [$this, 'add_css'] );
add_action( 'admin_enqueue_scripts', [$this, 'add_css'] );
add_action( 'admin_menu', [$this, 'main_menu'], 1 ); // broken - accessing wrong tables
add_action( 'wp', [$this, '__wp_page'], 51 );
add_filter( 'wp_audio_shortcode', [$this, '_audiowidget'], 1, 5 );
add_filter( 'wp_audio_extensions', [$this, '_audioparam'], 1 ); // add our extra shortcode parameter names here
add_action( 'wp_loaded', [$this, 'custom_urls'], 51 );
add_filter( 'catalyst_site_layout', [$this, '__setlayout'], 1 );
add_filter( '_get_page_link', [$this, '__canonical'], 80, 2 );
add_filter( 'catalyst_byline_meta', [$this, '__author'], 90 );
add_filter( 'catalyst_post_categories_shortcode', [$this, '__categories'], 90 );
add_filter( 'wp_title', [$this, '__build_site_title'], 11, 3 ); // restore title of page (ignore catalyst changes)
add_filter( 'mm_generate_link', [$this, '__generate_link'], 1, 4 );
add_filter( 'mm_get_contents', [$this, '__get_contents'], 1, 4 ); // get the contents of a file
add_filter( 'edit_post_link', [$this, '__edit_post_link'], 80, 3 );
}
public function ActiveHash() {
if (func_num_args()) {
$this->active_hash = func_get_arg(0);
return $this;
}
return $this->active_hash;
}
public function custom_urls() {
$cache = $_SERVER['REQUEST_URI'];
if (($p = strpos($cache, '?')) !== FALSE) {
$cache = substr($cache, 0, $p);
}
if (preg_match('#/~/media-search-vtt/(\w+)/(\w+)/([0-9a-f]{10})$#', $cache, $matches)) { // this is deprecated - replaced by media-manager.
header('Content-type: text/plain');
$type = $matches[1];
$docid = $matches[2];
$auth = $matches[3];
$expected_auth = $this->generateAuth($type, $docid);
if ($auth != $expected_auth) {
echo "\nPermission Denied\n";
} else {
echo $this->renderTranscript($type, $docid, FALSE);
}
exit;
} elseif (preg_match('#/~/testpage/(.*?)$#', $cache, $matches)) {
list($basic, $pagenum) = explode('/', $matches[1] . '//', 3);
if (empty($pagenum)) $pagenum = 0;
if (is_numeric($pagenum)) {
$ok = $this->_checkIfvalid($basic, $pagenum, TRUE);
} else {
$ok = FALSE;
}
if ($ok) {
$this->slug = $basic;
$this->pagenum = $pagenum;
$_GET['type'] = $this->header->mi_type;
$_GET['item'] = $this->header->mi_id;
$_GET['auth'] = $this->generateAuth($_GET['type'], $_GET['item']);
$this->_loadThePage($pagenum);
} else {
$this->header = FALSE;
}
} elseif (preg_match('#/~/media-search-reindex/(.*?)$#', $cache, $matches)) { // reindex a vtt file
$id = $matches[1];
$this->command_reindex($id);
wp_redirect('/wp-admin/admin.php?page=talks-single&action=class&cid=' . $id);
exit;
} elseif (preg_match('#/~/media-search-remove/(.*?)$#', $cache, $matches)) { // reindex a vtt file
$id = $matches[1];
// $this->remove_from_xapian($id);
die("Document $id removed.");
// This must be last
} elseif (preg_match('#/(.*?)$#', $cache, $matches)) {
list($basic, $pagenum) = explode('/', $matches[1] . '//', 3);
if (empty($pagenum)) $pagenum = 0;
if (is_numeric($pagenum)) {
$ok = $this->_checkIfvalid($basic, $pagenum, FALSE);
} else {
$ok = FALSE;
}
if ($ok) {
$this->slug = $basic;
$this->pagenum = $pagenum;
$_GET['type'] = $this->header->mi_type;
$_GET['item'] = $this->header->mi_id;
$_GET['auth'] = $this->generateAuth($_GET['type'], $_GET['item']);
$this->_loadThePage($pagenum);
} else {
$this->header = FALSE;
}
}
}
// change the canonical link to something more suitable
public function __canonical($link, $post_id) {
if ($this->header && $post_id == static::SPECIAL_PAGE) {
$elements = parse_url($link);
$pagenum = $this->pagenum > 1 ? '/' . $this->pagenum : '';
// return $elements['scheme'] . '://' . $elements['host'] . '/~/testpage/' . $this->slug . $pagenum;
return $elements['scheme'] . '://' . $elements['host'] . '/' . $this->slug . $pagenum;
}
return $link;
}
// this is overwritten by media-manager
public function __generate_link($link, $id, $download_name, $source) {
if (strpos($download_name, '.vtt') !== FALSE) {
$type = 'mt';
$subdir = substr($id, 0, 1);
$cc = "/var/www/hopeinstoughton_cdn1/{$subdir}/{$download_name}";
if (file_exists($cc)) {
$auth = $this->generateAuth($type, $id);
$link = "/~/media-search-vtt/{$type}/{$id}/{$auth}";
} else {
$link = '';
}
} elseif ($download_name == '') {
$link = "https://audio.hopeinstoughton.org/play/$id";
} else {
$dfilename = rawurlencode($download_name);
$link = "https://audio.hopeinstoughton.org/download/$id/$dfilename";
}
return $link;
}
// get the contents of a file - this too is overwritten
public function __get_contents($data, $id, $extension, $source) {
if ($extension == 'vtt') {
//$filename = "{$this->BASE}/av/moorestown-vtt/{$id}.vtt";
$subdir = substr($id, 0, 1);
$filename = "/var/www/hopeinstoughton_cdn1/{$subdir}/{$id}.vtt";
if (file_exists($filename)) {
$data = file_get_contents($filename);
} else {
$data = '';
}
}
return $data;
}
// Tools -> Reindex Audio and Video VTT files
public function main_menu() {
// add_options_page('Closed Captions', 'Closed Captions', 10, 'msc_reindex', [$this, 'reindex_all']);
}
public function add_css() {
// add .js file
wp_enqueue_script('jquery');
wp_enqueue_script('formwizard_js', '//code.jquery.com/ui/1.10.3/jquery-ui.js', [], '' );
wp_enqueue_script('media_search_js', plugins_url('media-search.js', __FILE__), [], '0.3' );
wp_enqueue_style('media_search_ui', '//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css');
wp_enqueue_style('media_search', plugins_url('media-search.css', __FILE__), [], '1.1');
}
public function _reindex_mm($vtt, $type = 'mt') {
global $wpdb;
set_time_limit(0);
$vtt->DocumentType($type);
$rows = $wpdb->get_results('select mi_id,mi_type,present_vtt from mm_item where mi_type="' . $type . '" and present_vtt <> 0');
foreach($rows as $row) {
$item = $row->mi_id;
echo "Reindexing {$item}...<br>\n";
$text = apply_filters('mm_get_contents', '', $item, 'vtt', $row->present_vtt);
$vtt->DocumentId($item)
->Content($text)
->Reindex();
}
}
function _reindex_folder($vtt, $doctype, $folder, $generateId) {
$vtt->DocumentType($doctype);
echo "Reindexing files in $folder...<br>";
$files = glob("$folder/*.vtt");
foreach($files as $filename) {
if (filesize($filename) > 20) {
//$mt = filemtime($filename);
//if ($mt < time() - 7200) continue;
$id = $generateId($filename);
if ($id === NULL) {
echo "Skipping $filename...<br>\n";
} else {
echo "Reindexing $id...<br>\n";
$vtt->DocumentId($id)
->FilenameDocument($filename)
->Reindex();
}
}
}
}
public function _shortcode_searchbar($atts) {
global $wpdb;
$mediasearch = isset($_GET['mediasearch']) ? trim(stripslashes_deep($_GET['mediasearch'])) : '';
$width = isset($atts['width']) ? $atts['width'] : '';
$action = isset($atts['action']) ? $atts['action'] : '';
if ($action != '') $action = " action=\"$action\"";
if ($width == '') $width = '100%';
$safe_q = htmlspecialchars($mediasearch);
return <<<BLOCK
<form class="search-wrapper"$action>
<div class="search-inner-wrapper" style="width:{$width};">
<input name="mediasearch" maxlength="256" type="text" aria-controls="searchSuggestionTable" aria-expanded="false" placeholder="Search Audio and Video" title="Search Audio and Video" value="{$safe_q}" aria-label="Search Audio and Video">
<button id="searchSubmit" class="search-buttonx action-searching fa fa-arrow-right fa-2x" title="Search" aria-label="Search"></button>
</div>
</form>
BLOCK;
}
private function _remove(&$after, $d) { // number of items to delete
$x = intval((count($after) - $d) / 2);
array_splice($after, $x, $d, ['...']);
}
private function getAuthors(&$res) {
global $wpdb;
$docs = [];
$result = [];
$wherein = '';
foreach($res as $index => $item) {
$docs[$item['docid']] = $index;
if ($wherein != '') $wherein .= ',';
$wherein .= '"' . esc_sql($item['docid']) . '"';
}
$sql = <<<BLOCK
select mi_id,mi_speaker,ms_firstname,ms_lastname,ms_suffix
from mm_item
left join mm_speaker on mi_speaker=ms_id
where mi_id in ($wherein)
BLOCK;
$rows = $wpdb->get_results($sql);
foreach($rows as $row) {
$entry = &$res[$docs[$row->mi_id]];
$entry['speaker'] = $row->mi_speaker;
if (!isset($result[$entry['speaker']])) {
$result[$entry['speaker']] = [
'firstname' => $row->ms_firstname,
'lastname' => $row->ms_lastname,
'suffix' => $row->ms_suffix,
'count' => 1
];
} else {
$result[$entry['speaker']]['count']++;
}
}
uasort($result, function($a, $b) {
$x = strcasecmp($a['lastname'], $b['lastname']);
if ($x) return $x;
return strcasecmp($a['firstname'], $b['firstname']);
});
$output = '';
$a = explode('.', $_GET['a']);
$a = array_flip($a);
foreach($result as $id => $item) {
$name = $item['lastname'] . ', ' . $item['firstname'];
if (!empty($item['suffix'])) $name .= ', ' . $item['suffix'];
$select = isset($a[$id]) ? 'checked' : '';
$output .= "<div><label><input type=\"checkbox\" name=\"author[]\" value=\"$id\" $select> $name ({$item['count']})</label></div>\n";
}
return $output;
}
private function _assemble_navigation($emediasearch, $start, $count) {
/* $before = $after = [];
$current = ''; */
$pagination = '';
$begin = $start - 6 * self::PAGESIZE;
if ($begin < 0) $begin = 0;
$end = $begin + 13 * self::PAGESIZE;
if ($end > $count) $end = $count;
for($i = $begin; $i < $end; $i += self::PAGESIZE) {
$page = intval($i / self::PAGESIZE) + 1;
if ($i == $start) {
$link = " <span class=\"xs-button current\">$page</span>";
} else {
$link = " <a class=\"xs-button\" href=\"?mediasearch=$emediasearch&start=$i\">$page</a>";
}
/* if ($i < $start) {
$before[] = $link;
} elseif ($i > $start) {
$after[] = $link;
} else {
$current = $link;
} */
$pagination .= $link;
}
/* if (count($before) < 5) { // remove only from after
$this->_remove($after, 14 - count($before));
} elseif (count($after) < 5) {
$this->_remove($before, 14 - count($after));
}
$pagination = join('', $before) . $current . join('', $after); */
return $pagination;
}
private function filterList(&$res) {
$a = explode('.', $_GET['a']);
$a = array_flip($a);
foreach($res as $index => $item) {
$speaker = $item['speaker'];
if (!isset($a[$speaker])) {
unset($res[$index]);
}
}
$res = array_values($res);
}
// mediasearch
public function _shortcode_searchresults($atts) {
global $wpdb;
$mediasearch = isset($_GET['mediasearch']) ? trim(stripslashes_deep($_GET['mediasearch'])) : '';
if ($mediasearch == '') return;
$emediasearch = rawurlencode($mediasearch);
$start = isset($_GET['start']) ? (int) $_GET['start'] : 0;
$vtt = new class_xap;
$vtt->FilenameDatabase($this->DATABASE)
->Open(); // open for reading
$res = $vtt->Search($mediasearch);
$authors = $this->getAuthors($res); // add the speakers to the results
if ($_GET['a'] != '') {
$this->filterList($res); // restrict to just selected authors
}
$count = count($res);
if ($start >= $count && $_GET['a'] != '') { // filter
$start = 0;
}
// take a slice, based on page number
$page_res = array_slice($res, $start, static::PAGESIZE);
// fill in details about each search result using info from the database
$this->loadDetails($page_res);
// render the search results
$output = '';
foreach($page_res as $result) {
$output .= $this->_asSearchResult($result);
}
// generate pagination
$pagination = '';
if ($start > 0) {
$prev = $start - self::PAGESIZE;
$pagination = "<a class=\"xs-button\" href=\"?mediasearch=$emediasearch&start=$prev\"><</a>";
} else {
$pagination = "<span class=\"xs-button disabled\"><</span>";
}
$pagination .= $this->_assemble_navigation($emediasearch, $start, $count);
if ($start + self::PAGESIZE < $count) {
$prev = $start + self::PAGESIZE;
$pagination .= " <a class=\"xs-button\" href=\"?mediasearch=$emediasearch&start=$prev\">></a>";
} else {
$pagination .= " <span class=\"xs-button disabled\">></span>";
}
$vtt->Close();
$summary = $count == 1 ? '1 result.' : "$count results.";
$filter2 = <<<BLOCK
<div class="xs-filteritems" style="display:none">
<div class="xs-buttons"><a class="action-xs" data-action="apply" href="#">Apply</a> | <a class="action-xs" data-action="clear" href="#">Clear</a> | <a class="action-xs" data-action="close" href="#">Close</a></div>
<div class="xs-body"><h3>Speakers</h3>$authors</div>
</div>
BLOCK;
$filter = <<<BLOCK
<a href="#" class="xs-filter action-xs" data-action="open">Filter...</a>
BLOCK;
return <<<BLOCK
{$filter2}<div class="xp-summary">{$summary}{$filter}</div>
<ul class="xp-results">
$output
</ul>
<div class="xp-pagination">$pagination</div>
BLOCK;
//return '<pre>' . htmlspecialchars(var_export($page_res, 1)) . '</pre>';
}
protected function _asSearchResult($result) {
$title = isset($result['title']) ? $result['title'] : 'title goes here';
$link = isset($result['link']) ? htmlspecialchars($result['link']) : '';
$foots = isset($result['foot']) ? htmlspecialchars($result['foot']) : '';
if (isset($result['offset'])) {
list($hms, $ms) = explode('.', $result['offset']);
$link .= '/' . $hms;
if (isset($result['test'])) {
$result['test'] .= '/' . $hms;
}
} elseif ($link == '') {
$link = '#';
}
if (isset($result['test'])) {
$foots .= ' <a href="' . $result['test'] . '">.</a>';
}
$dtitle = htmlspecialchars($title);
if (!empty($result['thumb'])) {
$thumb = <<<BLOCK
<a href="$link" class="xp-thumb">
{$result['thumb']}
</a>
BLOCK;
} else {
$thumb = '';
}
return <<<BLOCK
<li class="xp-result">
$thumb<h2><a href="$link" target="_blank">$dtitle</a></h2>
<p>{$result['snip']}</p>
<cite>$foots</cite>
</li>
BLOCK;
}
private function _asDuration($x) {
$duration = $x;
$hours = floor($duration / 3600);
$duration %= 3600;
$duration = sprintf('%d:%02d:%02d', $hours, floor($duration / 60), $duration % 60);
return $duration;
}
private function _loadThumbnail($filename_source) {
if (file_exists($filename_source)) {
$image_info = getimagesize($filename_source);
$wd = $image_info[0];
$ht = $image_info[1];
$new_height = 110;
$new_width = intval($wd * $new_height / $ht);
$original = ImageCreateFromJPEG($filename_source);
$image = imagecreatetruecolor($new_width, $new_height);
imagecopyresized($image, $original, 0, 0, 0, 0, $new_width, $new_height, $wd, $ht);
imagedestroy($original);
// create the image tag, with size, class, and data
ob_start();
imagejpeg($image, NULL, 90);
$rawImageBytes = base64_encode(ob_get_clean());
imagedestroy($image);
return "<img width=\"$new_width\" height=\"$new_height\" src=\"data:image/jpeg;base64,$rawImageBytes\">";
}
return '';
}
protected function loadDetails(&$res) {
global $wpdb;
$ids = [];
foreach($res as $item) {
$dhash = '"' . esc_sql($item['docid']) . '"';
$ids[] = $dhash;
}
unset($item);
if (count($ids)) {
$temp = join(',', $ids);
$rows = media_manager_class::Load(["mi_id in ($temp)"], ['group' => 'mixe_item', 'first' => FALSE, 'thumb' => TRUE]);
$resultindex = [];
foreach($rows as $index => $row) {
$resultindex[$row->mi_id] = $index;
}
foreach($res as &$item) {
if (isset($resultindex[$item['docid']])) {
$cliprow = $rows[$resultindex[$item['docid']]];
$type = empty($cliprow->me_aslisting) ? $cliprow->mi_type : 'in';
$item['link'] = $this->generateAuth($type, $cliprow->mi_id, TRUE) . '#' . $type .'_' . $cliprow->mi_id;
$speaker = $cliprow->_speaker;
if ($cliprow->mi_type == 'mt' || $cliprow->mi_type == 'cbt') {
// breadcrumb: location (year) -> topic -> subject (1 of 9)
$item['title'] = $cliprow->mi_title;
$item['foot'] = $cliprow->me_year . ' – ' . $speaker . ' – ' . $this->_asDuration($cliprow->duration) . ' – ' .
$cliprow->me_location . ' – ' . $cliprow->me_topic;
} else {
if (!empty($cliprow->mi_date)) {
$dt = strtotime($cliprow->mi_date . ' 12:00Z');
$item['foot'] = gmdate('l, F j, Y', $dt);
} else {
$item['foot'] = '';
}
if ($item['foot'] != '') $item['foot'] .= ' – ';
$item['foot'] .= $speaker;
if ($item['foot'] != '') $item['foot'] .= ' – ';
$item['foot'] .= $this->_asDuration($cliprow->duration);
if ($cliprow->mi_type == 'ex') {
$item['title'] = "Exhortation — $speaker";
} elseif (empty($cliprow->mi_title)) {
$item['title'] = $cliprow->me_topic;
} else {
$item['title'] = $cliprow->mi_title;
if ($item['foot'] != '') $item['foot'] .= ' – ';
$item['foot'] .= $cliprow->me_topic;
}
$item['thumb'] = $cliprow->mi_thumbnail;
if (!empty($cliprow->me_slug) && empty($cliprow->me_aslisting)) {
$item['link'] = $result = '/' . $cliprow->me_slug;
$seq = (int) $cliprow->mixe_sequence;
if ($seq > 1) $item['link'] .= '/' . $seq;
$item['link'] .= '#';
}
}
} else {
$item['foot'] = $item['doctype'];
}
}
}
}
public function generateAuth($doctype, $docid, $asLink = FALSE) {
// doctype = ['mt', 'ex' ...];
$auth = substr(md5('a^media#item:' . $doctype .':' . $docid), 7, 10);
if ($asLink) {
$safe_key = rawurlencode($docid);
$auth = "/archive/?type={$doctype}&item={$safe_key}&auth={$auth}";
}
return $auth;
}
// generate a list of all audio for the given topic/year/location.
// mark current one as active
// if it's an exhort, don't get related exhorts!
private function getRelatedContent($item, $event, $thumbs = FALSE, $specific = FALSE) {
global $wpdb;
$EX = static::EXHORT;
$ditem = '"' . esc_sql($item) . '"';
if (!$event) {
$row = $wpdb->get_row("select mixe_event from `mm_item_x_event` where mixe_item={$ditem}");
$event = is_null($row) ? 0 : (int) $row->mixe_event;
}
$devent = '"' . esc_sql($event) . '"';
if ($event == $EX || $specific) {
$rows = media_manager_class::Load($item, [ 'first' => FALSE ] ); // exhort - no related
} else {
$param = [
'first' => FALSE,
'order' => 'mixe_sequence',
];
if ($thumbs) $param['thumb'] = TRUE;
$rows = media_manager_class::Load(["mixe_event=$devent"], $param );
}
$max = 1;
foreach($rows as $row) {
if ($row->mixe_sequence > $max) $max = $row->mixe_sequence;
}
$group = [];
foreach($rows as $row) {
$node = [
'type' => $row->mi_type,
'item' => $row->mi_id,
'active' => $item == $row->mi_id ? 1 : 0,
'caption' => $row->mixe_sequence . ' of ' . $max,
'hint' => $row->mi_title,
'mp4' => (int) $row->present_mp4,
'mp3' => (int) $row->present_mp3,
'vtt' => (int) $row->present_vtt,
'date' => $row->mi_date,
];
if ($thumbs) $node['thumb'] = $row->mi_thumbnail;
if ($node['mp3'] || $node['mp4']) { // only if we have loaded some content
$group[] = $node;
}
}
return $group;
}
protected function _getAttachments($row) {
$attach_source = 0;
$attach_html = 0;
if (is_array($row->_media)) {
foreach($row->_media as $entry) {
if ($entry->mm_type == 'zip') {
$attach_source = (int) $entry->mm_source;
break;
} elseif ($entry->mm_type == 'html') {
$attach_html = (int) $entry->mm_source;
}
}
}
if (!$attach_source && !$attach_html) return ''; // no attachments
$output = '';
if ($attach_html) {
$contents = apply_filters('mm_get_contents', '', $row->mi_id, 'html', 1 << $attach_html);
$output .= $contents;
}
if ($attach_source) {
$link_zip = apply_filters('mm_generate_link', '', $row->mi_id, $item. '.zip', 1 << $attach_source);
// get source file
$contents = apply_filters('mm_get_contents', '', $row->mi_id, 'zip', 1 << $attach_source);
$filename_temp = tempnam(sys_get_temp_dir(), 'zip');
$filename_zip = $filename_temp . '.zip';
file_put_contents($filename_zip, $contents);
$lines = '';
$zip = new \ZipArchive;
$zip->open($filename_zip);
$result_stats = [];
for ($i = 0; $i < $zip->numFiles; $i++) {
$stat = $zip->statIndex($i);
$filename = htmlspecialchars($stat['name']);
$sz = MediaSource::formatBytes($stat['size'], 1);
$lines .= "<tr><td><a href=\"$link_zip/{$stat['index']}/$filename\" target=\"_blank\">$filename</a> ($sz)</td></tr>\n";
}
$zip->close();
@unlink($filename_zip);
@unlink($filename_temp);
$output = <<<BLOCK
<h3>Attachments</h3>
<table>
$lines
</table>
BLOCK;
}
return $output;
}
private function _renderMediaPlayer($item, $event, $transcript = FALSE, $specific = FALSE, $single = FALSE) {
global $wpdb, $post;
$group = $this->getRelatedContent($item, $event, FALSE, $single);
if ($item == '' && count($group)) $item = $group[0]['item'];
$this->ActiveHash($item);
$j = '';
$activetab = FALSE;
foreach($group as $tabindex => $groupitem) {
$type = $groupitem['type'];
$item = $groupitem['item'];
$link_vtt = apply_filters('mm_generate_link', '', $item, $item. '.vtt', $groupitem['vtt']);
if ($link_vtt != '') {
if (strpos($link_vtt, '?') === FALSE) {
$link_vtt .= '?short=1';
} else {
$link_vtt .= '&short=1';
}
$link_vtt = " cc=\"{$link_vtt}\"";
}
$row = media_manager_class::Load($item, ['first' => TRUE, 'media' => TRUE]);
$other = $row->_other;
$youtube = empty($row->_other->youtube) ? '' : $row->_other->youtube;
$safelink = htmlspecialchars($youtube);
if ($row->present_mp4) {
$link_download = apply_filters('mm_generate_link', '', $item, $other->original, $row->present_mp4);
if ($type == 'ex') {
$post->post_title = "Exhortation — {$row->_speaker}";
} else {
$post->post_title = $row->me_topic;
}
$date = '';
$dt = strtotime(str_replace('.', '-', $row->mi_date));
if (!empty($dt)) {
$date = date('l, F j, Y', $dt);
}
if ($row->me_showlink && !empty($youtube)) {
if ($date == '') $date = ' ';
$date = <<<BLOCK
<span style="float:right">
<a href="{$safelink}" target="_blank" style="font-weight:bold">Original URL<i class="fas fa-external-link-alt" title="Open external link in a new tab"></i></a>
</span>$date
BLOCK;
}
if ($date != '') $date = '<p style="max-width:870px">' . $date . '</p>' ;
if (!empty($row->mi_title)) {
$safetitle = htmlspecialchars($row->mi_title);
$link_vtt .= " title=\"$safetitle\"";
}
$jpg = (int) $row->present_jpg;
if ($jpg) {
$link_thumb = apply_filters('mm_generate_link', '', $item, '.jpg', $jpg);
$safeposter = htmlspecialchars($link_thumb);
if ($link_thumb != '') $link_vtt .= " poster=\"$safeposter\"";
}
$link_vtt .= " width=\"870\"";
if ($item == '3f85ed78' || $item == 'f6589c42' || $item == '725cf0f6' || $item == '02fa2e3e') {
$link_download = str_replace(['cdn1', 'cdn3' ], 'media', $link_download);
}
$block = "[videoplayer src=\"$link_download\"{$link_vtt}]{$date}";
} else { // no video, so must be audio-only
$thumb = $this->_getThumbnail_moorsetown($item);
$date = '';
// add duration & download size?
$dlocation = htmlspecialchars($row->me_location);
$dtopic = htmlspecialchars($row->me_topic);
$dtitle = htmlspecialchars($row->mi_title);
$dspeaker = htmlspecialchars($row->_speaker);
$dfilename = rawurlencode($other->original);
if (!empty($row->present_mp3)) {
$link_download = apply_filters('mm_generate_link', '', $item, $other->original, $groupitem['mp3']);
$download = "<a href=\"$link_download\">{$other->original}</a>";
$download = "<div><span class=\"xp-audio-field\">Download:</span><span class=\"xp-audio-value ellipsis\">$download</span></div>";
} else {
$link_download = $download = '';
}
if ($row->me_showlink && !empty($youtube)) {
$date = <<<BLOCK
<span style="float:right;padding-bottom:10px">
<a href="{$safelink}" target="_blank" style="font-weight:bold">Original URL<i class="fas fa-external-link-alt" title="Open external link in a new tab."></i></a> |
</span>
BLOCK;
} elseif ($type == 'cbt') {
$date = <<<BLOCK
<span style="float:right;padding-bottom:10px">
<a href="http://christadelphianbibletalks.com/#study/{$row->mi_moorestown}" target="_blank" style="font-weight:bold">Original URL<i class="fas fa-external-link-alt" title="open external link in a new tab"></i></a> |
</span>
BLOCK;
} elseif ($type == 'mt') {
/* $date = <<<BLOCK
<span style="float:right;padding-bottom:10px">
Source: <a href="http://archive.moorestownchristadelphians.org/">archive.moorestownchristadelphians.org</a> |
</span>
BLOCK;
*/
$date = ''; // archive site is gone
}
$year = ($row->me_year == '0000' || $row->me_year == '') ? '' : " ({$row->me_year})";
$post->post_title = 'Audio Archive';
$block = <<<BLOCK
<div class="xp-audio-container">
<div class="xp-audio-thumbnail">
$thumb
</div>
<div class="xp-audio-captions">
<div><span class="xp-audio-field">Location:</span><span class="xp-audio-value">$dlocation <span style="font-weight: normal">$year</span></span></div>
<div><span class="xp-audio-field">Topic:</span><span class="xp-audio-value">$dtopic</span></div>
<div><span class="xp-audio-field">Title:</span><span class="xp-audio-value">$dtitle</span></div>
<div><span class="xp-audio-field">Speaker:</span><span class="xp-audio-value">$dspeaker</span></div>
$download
</div>
</div>
BLOCK;
if ($link_download != '') {
$block .= "[audio src=\"$link_download\" info=\"{$item}\"{$link_vtt}]{$date}";
} else {
$block .= '<p>We do not have the audio for this item... are you able to help us?</p>';
}
}
if ($this->pagenum && ($row->mixe_sequence == $this->pagenum)) {
$activetab = $groupitem['type'] . '_' . $groupitem['item'];
} elseif ($specific !== FALSE && $groupitem['item'] == $specific) {
$activetab = $groupitem['type'] . '_' . $groupitem['item'];
}
$block .= $this->_getAttachments($row);
if (!empty($transcript)) {
$block .= $this->renderTranscript($type, $item, TRUE);
}
$j .= "\n<DIV class=\"media-search-tab\" id=\"{$groupitem['type']}_{$groupitem['item']}\">$block</DIV>\n";
}
if (count($group) > 1) { // create tabs
$tabline = '';
foreach($group as $groupitem) {
$type = $groupitem['type'];
$item = $groupitem['item'];
$safehint = htmlspecialchars($groupitem['hint']);
$tabline .= "<li title=\"{$safehint}\"><a href=\"#{$groupitem['type']}_{$groupitem['item']}\">{$groupitem['caption']}</a></li>";
}
$j = <<<BLOCK
<div class="media-search-tabs">
<ul>$tabline</ul>
$j
</div>
BLOCK;
if ($activetab) {
$j .= <<<BLOCK
<script type="text/javascript">
window.mediaSearchTab = '{$activetab}';
</script>
BLOCK;
};
// since we have tabs, we may have a youtube playlist
if (($row->me_playlist ?? 0) && ($row->me_playlistid ?? '') != '') {
$j .= <<<BLOCK
<div style="text-align: center;margin-top:20px"><a href="https://www.youtube.com/playlist?list={$row->me_playlistid}">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 100 100">
<g transform="translate(0,100) scale(0.1,-0.1)" fill="#f00" stroke="none">
<path fill="#999" d="M197 832 c-10 -10 -17 -22 -17 -25 0 -4 153 -7 339 -7 187 0 347 -3
356 -6 39 -15 45 -54 45 -279 0 -128 4 -215 9 -215 6 0 17 10 25 22 13 19 16
61 16 253 0 192 -3 234 -16 253 l-15 22 -363 0 c-339 0 -364 -1 -379 -18z"/>
<path fill="#666" d="M127 762 c-10 -10 -17 -21 -17 -25 0 -3 156 -8 346 -9 l346 -3 19
-24 c17 -21 19 -42 19 -245 0 -123 3 -226 7 -229 3 -4 15 0 25 10 16 15 18 36
18 263 0 226 -2 248 -18 263 -17 15 -57 17 -374 17 -331 0 -356 -1 -371 -18z"/>
<path d="M46 678 c-24 -33 -24 -473 0 -506 l15 -22 363 0 c339 0 364 1 379 18
15 16 17 49 17 260 0 205 -2 243 -16 256 -13 14 -62 16 -379 16 l-364 0 -15
-22z m397 -194 c42 -24 77 -51 77 -58 0 -17 -159 -108 -177 -102 -9 4 -13 32
-13 106 0 120 0 120 113 54z"/>
</g>
</svg><span style="font-size:24pt;padding-left:13px;vertical-align: 10px;">YouTube Playlist</span></a></div>
BLOCK;
}
}
return $j;
}
// Update
public function __wp_page() {
global $post, $wpdb;
// this plug intercepts the text '[videoplayer archive="hash"]' within a post/page
$type = isset($_GET['type']) ? trim($_GET['type']) : '';
$item = isset($_GET['item']) ? trim($_GET['item']) : '';
$auth = isset($_GET['auth']) ? trim($_GET['auth']) : '';
$expected_auth = $this->generateAuth($type, $item);
$hasPlayer = preg_match('~\[videoplayer(.*?)\]~ims', $post->post_content, $match) ? $match[0] : '';
$isListing = empty($this->header->me_aslisting) ? 0 : (int) $this->header->me_aslisting;
if (strpos($hasPlayer, 'archive="hash"') !== FALSE && $auth != '' && $auth == $expected_auth) {
$transcript = preg_match('~transcript="(.*?)"~', $hasPlayer, $match) ? $match[1] : FALSE;
if (is_numeric($type)) {
$event = $type;
} else {
$event = empty($this->header->__event) ? 0 : (int) $this->header->__event;
}
if ($isListing) {
$j = $this->_generateEventListing($event);
} else {
$j = $this->_renderMediaPlayer($item, $event, $transcript, FALSE, $type == 'in');
}
$post->post_content = str_replace($hasPlayer, $j, $post->post_content);
} elseif (preg_match('~archive="([0-9a-f]{8,10})"~', $hasPlayer, $matches )) {
$transcript = preg_match('~transcript="(.*?)"~', $hasPlayer, $match) ? $match[1] : FALSE;
if ($isListing) {
$j = $this->_generateEventListing($event);
} else {
$j = $this->_renderMediaPlayer($matches[1], 0, $transcript, $matches[1], $type == 'in');
}
// maybe name of speaker should appear after date in videoplayer footer.
$post->post_content = str_replace($hasPlayer, $j, $post->post_content);
// add javascript to go to the active tab
}
global $wp_query;
$wp_query->is_404 = FALSE;
}
public function _generateEventListing($me_id) {
global $post;
// heading
$post->post_title = $this->header->me_topic;
$output = '';
// get everything in this event
$group = $this->getRelatedContent(NULL, $me_id, TRUE);
$group = array_reverse($group);
foreach($group as $item) {
$date = ' ';
if (!empty($item['date'])) {
$dt = strtotime("{$item['date']} 12:00Z");
$date = gmdate('l, F j, Y', $dt);
}
$link = $this->generateAuth('in', $item['item'], TRUE);
$safelink = htmlspecialchars($link);
if (empty($item['thumb'])) {
$thumb = $this->_getThumbnail_audio();
} else {
$thumb = $item['thumb'];
}
$output .= <<<BLOCK
<div class="searchlist-item" style="margin: 10px; flex-basis: calc(25% - 20px);">
<div>$date</div>
<div class="thumbnail" style="text-align: center; background-color: #d8d8d8; padding: 3px">
<a href="{$safelink}">{$thumb}</a>
</div>
<div class="caption" style="text-align: center; font-weight: bold">
<a href="{$safelink}">{$item['hint']}</a>
</div>
</div>
BLOCK;
}
$admin = current_user_can('editor') || current_user_can('administrator');
if ($admin) {
$admin = admin_url( 'admin.php' );
$admin = <<<BLOCK
<a href="{$admin}?page=talks-main-page&action=event&eid={$me_id}">(edit)</a>
BLOCK;
} else {
$admin = '';
}
// table
return <<<BLOCK
<div class="searchlist-container" style="display: flex; flex-flow: row wrap">
$output
</div>
$admin
BLOCK;
}
public function _audioparam($list) {
$list[] = 'info';
$list[] = 'cc';
return $list;
}
public function _audiowidget($html, $atts, $audio, $post_id, $library) {
if (!empty($atts['info'])) { // if specified, this is one of our audio tags
// remove the stuff added by WordPress
$html = preg_replace('~<source\s+type="".*?/>~ism', '', $html);
$html = preg_replace('~<source\s+type="text/vtt".*?/>~ism', '', $html);
// then add our stuff: captions
if (!empty($atts['cc'])) {
$p = strpos($html, '<a href=');
$new = '<track id="trk" kind="subtitles" srclang="en" src="' . $atts['cc'] . '?_=1" default />';
$html = substr($html, 0, $p) . $new . substr($html, $p);
}
}
return $html;
}
private function _getThumbnail_moorsetown($item) {
$data = base64_encode(file_get_contents(__DIR__ . '/placeholder.png'));
return "<img width=\"340\" height=\"340\" src=\"data:image/png;base64,$data\">";
}
private function _getThumbnail_audio() {
$data = base64_encode(file_get_contents(__DIR__ . '/placeholder16.png'));
return "<img width=\"340\" height=\"248\" src=\"data:image/png;base64,$data\">";
}
// load the VTT file - put in some basic formatting
public function renderTranscript($type, $item, $asHTML = TRUE) {
global $wpdb;
$ditem = '"' . esc_sql($item) . '"';
$row = $wpdb->get_row("select present_vtt from `mm_item` where mi_id={$ditem}");
$text = apply_filters('mm_get_contents', '', $item, 'vtt', $row->present_vtt);
$vtt = new vtt_class;
$vtt->Content($text);
$vttdata = $vtt->getEntries();
if (!count($vttdata)) return ''; // no transcript available
$output = $vtt->asTranscript($asHTML);
if ($asHTML) {
return <<<BLOCK
<div class="xp-transcript" id="trans_{$type}_{$item}">
<h3>Transcript</h3>
$output
</div>
BLOCK;
} else {
return <<<BLOCK
WEBVTT
$output
BLOCK;
}
}
// set wide theme in Catalyst, but only if we are on our special page
public function __setlayout( $layout ) {
if ($this->slug) {
return 'no-sidebar wide';
}
return $layout;
}
private function _checkIfvalid($basic, &$pagenum, $testmode = FALSE) {
global $wpdb;
if ($basic == '') return FALSE;
$xbasic = '"' . esc_sql($basic) . '"';
// first step - get the event this is part of
$sql = "select me_id from `mm_event` as e where me_slug={$xbasic}";
$event = $wpdb->get_row($sql);
if (is_null($event)) return FALSE;
$event = (int) $event->me_id;
if (empty($pagenum)) {
$sql = "select min(mixe_sequence) as mn from mm_item_x_event where mixe_event=$event";
$data = $wpdb->get_row($sql);
$pagenum = is_null($data) ? 1 : (int) $data->mn;
}
$xseq = '"' . esc_sql($pagenum) . '"';
$where = [
"mixe_event=$event",
"(mixe_sequence = 0 or mixe_sequence = $xseq)"
];
if (!$testmode) $where[] = '((present_mp4 <> 0) or (present_mp3 <> 0))';
$this->header = media_manager_class::Load($where);
$ok = !is_null($this->header);
if ($ok) $this->header->__event = $event;
return $ok;
}
private function _loadThePage($pagenum) {
global $wp_query;
$args = [
'p' => static::SPECIAL_PAGE, // ID of a page, post, or custom type
'post_type' => 'any'
];
$wp_query = new WP_Query($args);
$wp_query->post->post_title = $this->header->csh_title ?? '';
$wp_query->post->post_name = "{$this->slug}/$pagenum";
// the content of the special page is updated in $this->__wp_page().
}
public function __build_site_title( $title, $separator, $separator_location) {
global $post;
// this is only needed when catalyst is active
if (function_exists('catalyst_get_core') && $post->ID == static::SPECIAL_PAGE) {
$separator = catalyst_get_core( 'title_tag_separator' ) ? catalyst_get_core( 'title_tag_separator' ) : '|';
$separator_location = catalyst_get_core( 'title_append_location' ) == 'Right' ? 'Right' : 'Left';
$title = $post->post_title; // revert back to original title - ignore catalyst seo stuff
$title = $separator_location == 'Right'
? $title . " $separator " . get_bloginfo( 'name' )
: get_bloginfo( 'name' ) . " $separator " . $title;
$title = esc_html( trim( $title ) );
}
return $title;
}
// remove Author section at top
public function __author($meta) {
if ($this->header) {
return '';
}
return $meta;
}
// remove Categories: at bottom
public function __categories($value) {
if ($this->header) {
return '';
}
return $value;
}
public function command_reindex($id) {
global $wpdb;
$did = '"' . esc_sql($id) . '"';
$sql = "select mi_id,mi_date,mi_type,mi_title,present_vtt from mm_item where mi_id=$did";
$row = $wpdb->get_row($sql);
$text = apply_filters('mm_get_contents', '', $id, 'vtt', $row->present_vtt);
$vtt = new class_xap;
$vtt->FilenameDatabase($this->DATABASE)
->Open(FALSE); // open for writing
$vtt->DocumentType($row->mi_type)
->DocumentId($id)
->Content($text)
->Reindex();
}
public function remove_from_xapian($id) {
$vtt = new class_xap;
$vtt->FilenameDatabase($this->DATABASE)
->Open(FALSE); // open for writing
$vtt->DocumentId($id)
->Remove()
->Close();
}
function __edit_post_link($link, $post_id, $text) {
$hash = $this->ActiveHash();
if (!empty($hash)) {
$link .= " | <a href=\"/wp-admin/admin.php?page=talks-single&action=class&cid={$hash}\" class=\"bind-editlink\">(details)</a>";
}
return $link;
}
}
// prevent breadcrumbs from being displayed
// wp-content\themes\catalyst\lib\functions\catalyst-breadcrumbs.php
function breadcrumbs() {
return '';
}
include_once dirname(__FILE__) . '/widget.php';
global $MEDIASEARCH;
$MEDIASEARCH = new media_search_class();
/*
SELECT count(cst_id), cst_date FROM `cd_seriest` WHERE 1 group by cst_date
2 2016-05-15
2 2016-09-18
2 2016-09-25
2 2017-03-19
2 2017-09-17
2 2017-09-24
2 2017-04-09
2 2018-03-04
2 2018-03-11
2 2018-03-25
Classes are in a mess; see below for example. 'ac' files need to be uploaded to the classes/year folder; data-entry to make sure slug name is unique
SELECT * FROM `cd_seriest` WHERE cst_date = '2020-06-24'
Last class should automagically appear on adult-class page
* thumbnail sizes for videos on the other tabs
* docid based on record id in detail file?
*/