Template/Update (Part 39)

* implement video suggestion & management
This commit is contained in:
Sarjuuk 2025-08-14 01:12:42 +02:00
parent a369244908
commit cb523353fd
36 changed files with 2465 additions and 16 deletions

View file

@ -0,0 +1,68 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected string $template = 'admin/videos';
protected string $pageName = 'videos';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 1, 17]; // Staff > Content > Videos
protected array $scripts = array(
[SC_JS_FILE, 'js/video.js'],
[SC_CSS_STRING, '.layout {margin: 0px 25px; max-width: inherit; min-width: 1200px; }'],
[SC_CSS_STRING, '#highlightedRow { background-color: #322C1C; }']
);
protected array $expectedGET = array(
'action' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']],
'type' => ['filter' => FILTER_VALIDATE_INT ],
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode' ]
);
public ?bool $getAll = null;
public array $viPages = [];
public array $viData = [];
public int $viNFound = 0;
public array $pageTypes = [];
protected function generate() : void
{
$this->h1 = 'Video Manager';
// types that can have videos
foreach (Type::getClassesFor(0, 'contribute', CONTRIBUTE_SS) as $type => $obj)
$this->pageTypes[$type] = Util::ucWords(Lang::game(Type::getFileString($type)));
$viGetAll = $this->_get['all'];
$viPages = [];
$viData = [];
$nMatches = 0;
if ($this->_get['type'] && $this->_get['typeid'])
$viData = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid'], nFound: $nMatches);
else if ($this->_get['user'])
{
if (mb_strlen($this->_get['user']) >= 3)
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
$viData = VideoMgr::getVideos(userId: $uId, nFound: $nMatches);
}
else
$viPages = VideoMgr::getPages($viGetAll, $nMatches);
$this->getAll = $viGetAll;
$this->viPages = $viPages;
$this->viData = $viData;
$this->viNFound = $nMatches; // ssm_numPagesFound
parent::generate();
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Aowow;
use GdImage;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionApproveResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminVideosActionApproveResponse - videoId empty', E_USER_ERROR);
return;
}
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_APPROVED, $this->_get['id']);
foreach ($viEntries as $id => $viData)
{
// set as approved in DB
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
// flag DB entry as having videos
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
unset($viEntries[$id]);
}
if (!$viEntries)
trigger_error('AdminVideosActionApproveResponse - video(s) # '.implode(', ', array_keys($viEntries)).' not in db or already approved', E_USER_WARNING);
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionDeleteResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
);
// 2 steps: 1) remove from sight, 2) remove from disk
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminVideosActionDeleteResponse - videoId empty', E_USER_ERROR);
return;
}
// irrevocably purge files already flagged as deleted (should only exist as pending)
if (User::isInGroup(U_GROUP_ADMIN))
DB::Aowow()->selectCell('SELECT 1 FROM ?_videos WHERE `status` & ?d AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
// flag as deleted if not aready
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ?_videos WHERE `id` IN (?a) GROUP BY `type`', $this->_get['id']);
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdDelete` = ?d WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, User::$id, CC_FLAG_DELETED, $this->_get['id']);
// deflag db entry as having videos
foreach ($oldEntries as $type => $typeIds)
{
$typeIds = explode(',', $typeIds);
$toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE ?# SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', $tbl, CUSTOM_HAS_VIDEO, array_keys($toUnflag));
}
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionEdittitleResponse extends TextResponse
{
use TrCommunityHelper;
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected array $expectedPOST = array(
'title' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
return;
$caption = $this->handleCaption($this->_post['title']);
DB::Aowow()->query('UPDATE ?_videos SET `caption` = ? WHERE `id` = ?d', $caption, $this->_get['id'][0]);
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionListResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
);
protected function generate() : void
{
$pages = VideoMgr::getPages($this->_get['all'], $nPages);
$this->result = 'vim_videoPages = '.Util::toJSON($pages).";\n";
$this->result .= 'vim_numPagesFound = '.$nPages.';';
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionManageResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'type' => ['filter' => FILTER_VALIDATE_INT ],
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode']
);
protected function generate() : void
{
$res = [];
if ($this->_get['type'] && $this->_get['typeid'])
$res = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid']);
else if ($this->_get['user'])
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
$res = VideoMgr::getVideos(userId: $uId);
$this->result = 'vim_videoData = '.Util::toJSON($res);
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionOrderResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned'] ],
'move' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => -1, 'max_range' => 1]] // -1 = up, 1 = down
);
protected function generate() : void
{
if (!$this->assertGET('id', 'move') || $this->_get['move'] === 0)
{
trigger_error('AdminVideosActionOrderResponse - id or move empty', E_USER_ERROR);
return;
}
$id = $this->_get['id'][0];
$videos = DB::Aowow()->selectCol('SELECT a.`id` AS ARRAY_KEY, a.`pos` FROM ?_videos a, ?_videos b WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND (a.`status` & ?d) = 0 AND b.`id` = ?d ORDER BY a.`pos` ASC', CC_FLAG_DELETED, $id);
if (!$videos || count($videos) == 1)
{
trigger_error('AdminVideosActionOrderResponse - not enough videos to sort', E_USER_WARNING);
return;
}
$dir = $this->_get['move'];
$curPos = $videos[$id];
if ($dir == -1 && $curPos == 0)
{
trigger_error('AdminVideosActionOrderResponse - video #'.$id.' already in top position', E_USER_WARNING);
return;
}
if ($dir == 1 && $curPos + 1 == count($videos))
{
trigger_error('AdminVideosActionOrderResponse - video #'.$id.' already in bottom position', E_USER_WARNING);
return;
}
$oldKey = array_search($curPos + $dir, $videos);
$videos[$oldKey] -= $dir;
$videos[$id] += $dir;
foreach ($videos as $id => $pos)
DB::Aowow()->query('UPDATE ?_videos SET `pos` = ?d WHERE `id` = ?d', $pos, $id);
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionRelocateResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']],
'typeid' => ['filter' => FILTER_VALIDATE_INT ]
// (but not type..?)
);
protected function generate() : void
{
if (!$this->assertGET('id', 'typeid'))
{
trigger_error('AdminVideosActionRelocateResponse - videoId or typeId empty', E_USER_ERROR);
return;
}
$id = $this->_get['id'][0];
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ?_videos WHERE `id` = ?d', $id));
$typeId = $this->_get['typeid'];
if (Type::validateIds($type, $typeId))
{
$tbl = Type::getClassAttrib($type, 'dataTable');
// move video
DB::Aowow()->query('UPDATE ?_videos SET `typeId` = ?d WHERE `id` = ?d', $typeId, $id);
// flag target as having video
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $typeId);
// deflag source for having had videos (maybe)
$viInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `status`& ?d AND `type` = ?d AND `typeId` = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if ($viInfo || !$viInfo['hasMore'])
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $oldTypeId);
}
else
trigger_error('AdminVideosActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionStickyResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminVideosActionStickyResponse - videoId empty', E_USER_ERROR);
return;
}
// this one is a bit strange: as far as i've seen, the only thing a 'sticky' video does is show up in the infobox
// this also means, that only one video per page should be sticky
// so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
foreach ($viEntries as $id => $viData)
{
// approve yet unapproved videos
if (!($viData['status'] & CC_FLAG_APPROVED))
{
// set as approved in DB
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
// flag DB entry as having videos
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
}
// reset all others
DB::Aowow()->query('UPDATE ?_videos a, ?_videos b SET a.`status` = a.`status` & ~?d WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = ?d', CC_FLAG_STICKY, $id);
// toggle sticky status
DB::Aowow()->query('UPDATE ?_videos SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE `id` = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
unset($viEntries[$id]);
}
if ($viEntries)
trigger_error('AdminVideosActionStickyResponse - video(s) # '.implode(', ', array_keys($viEntries)).' not in db or flagged as deleted', E_USER_WARNING);
}
}

124
endpoints/video/add.php Normal file
View file

@ -0,0 +1,124 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
-> 1. =add: receives user upload
1.1. checks and processing on the upload
1.2. forward to =confirm or blank response
2. =confirm: user edites upload
3. =complete: store edited video file and data
4. =thankyou
*/
class VideoAddResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'videourl' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
);
private string $videoHash = '';
private int $destType = 0;
private int $destTypeId = 0;
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
// get video destination
// target delivered as video=<command>&<type>.<typeId>.<hash:16> (hash is optional)
if (!preg_match('/^video=\w+&(-?\d+)\.(-?\d+)(\.(\w{16}))?$/i', $_SERVER['QUERY_STRING'] ?? '', $m, PREG_UNMATCHED_AS_NULL))
$this->generate404();
[, $this->destType, $this->destTypeId, , $videoHash] = $m;
// no such type or this type cannot receive videos
if (!Type::checkClassAttrib($this->destType, 'contribute', CONTRIBUTE_VI))
$this->generate404();
// no such typeId
if (!Type::validateIds($this->destType, $this->destTypeId))
$this->generate404();
// only accept/expect hash for confirm & complete
if ($videoHash)
$this->generate404();
}
protected function generate() : void
{
if ($this->handleAdd())
$this->redirectTo = '?video=confirm&'.$this->destType.'.'.$this->destTypeId.'.'.$this->videoHash;
else if ($this->destType && $this->destTypeId)
$this->redirectTo = '?'.Type::getFileString($this->destType).'='.$this->destTypeId.'#suggest-a-video';
else
$this->generate404();
}
private function handleAdd() : bool
{
if (!User::canSuggestVideo())
{
$_SESSION['error']['vi'] = Lang::video('error', 'notAllowed');
return false;
}
if (!$this->assertPOST('videourl'))
{
$_SESSION['error']['vi'] = Lang::video('error', 'selectVI');
return false;
}
$videoId = '';
if (preg_match('/^https?:\/\/(www\.)?youtu(\.be|be\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/', $this->_post['videourl'], $m))
$videoId = $m[3];
else
{
$_SESSION['error']['vi'] = Lang::video('error', 'selectVI');
return false;
}
$curl = curl_init('https://youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v='.$videoId);
if (!$curl)
{
trigger_error('VideoAddResponse - curl_init fail', E_USER_ERROR);
$_SESSION['error']['vi'] = Lang::main('intError');
return false;
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$ytOembed = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
if ($status == 401)
{
$_SESSION['error']['vi'] = Lang::video('error', 'isPrivate');
return false;
}
else if ($status != 200) // 404, 500 seen .. does it matter why its inaccessible?
{
$_SESSION['error']['vi'] = Lang::video('error', 'noExist');
return false;
}
$videoInfo = json_decode($ytOembed);
$videoInfo->id = $videoId;
if (!VideoMgr::saveSuggestion($videoInfo, $this->destType, $this->destTypeId, $this->videoHash))
{
$_SESSION['error']['ss'] = Lang::main('intError');
return false;
}
return true;
}
}
?>

View file

@ -0,0 +1,92 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
1. =add: receives user upload
2. =crop: user edites upload
-> 3. =complete: store edited video file and data
4. =thankyou
*/
class VideoCompleteResponse extends TextResponse
{
use TrCommunityHelper;
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'caption' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
);
private string $videoHash = '';
private int $destType = 0;
private int $destTypeId = 0;
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
// get video destination
// target delivered as video=<command>&<type>.<typeId>.<hash:16> (hash is optional)
if (!preg_match('/^video=\w+&(-?\d+)\.(-?\d+)(\.(\w{16}))?$/i', $_SERVER['QUERY_STRING'] ?? '', $m, PREG_UNMATCHED_AS_NULL))
$this->generate404();
[, $this->destType, $this->destTypeId, , $this->videoHash] = $m;
// no such type or this type cannot receive videos
if (!Type::checkClassAttrib($this->destType, 'contribute', CONTRIBUTE_VI))
$this->generate404();
// no such typeId
if (!Type::validateIds($this->destType, $this->destTypeId))
$this->generate404();
}
protected function generate() : void
{
if ($this->handleComplete())
$this->forward('?video=thankyou&'.$this->destType.'.'.$this->destTypeId);
else
$this->generate404();
}
private function handleComplete() : bool
{
if (!VideoMgr::loadSuggestion($videoInfo, $this->destType, $this->destTypeId, $this->videoHash))
$this->generate404();
$pos = DB::Aowow()->selectCell('SELECT MAX(`pos`) FROM ?_videos WHERE `type` = ?d AND `typeId` = ?d AND (`status` & ?d) = 0', $this->destType, $this->destTypeId, CC_FLAG_DELETED);
if (!is_int($pos))
$pos = -1;
// write to db
$newId = DB::Aowow()->query(
'INSERT INTO ?_videos (`type`, `typeId`, `userIdOwner`, `date`, `videoId`, `pos`, `url`, `width`, `height`, `name`, `caption`, `status`) VALUES (?d, ?d, ?d, UNIX_TIMESTAMP(), ?, ?d, ?, ?d, ?d, ?, ?, 0)',
$this->destType, $this->destTypeId, User::$id,
$videoInfo->id,
$pos + 1,
$videoInfo->thumbnail_url,
$videoInfo->thumbnail_width,
$videoInfo->thumbnail_height,
$videoInfo->title,
$this->handleCaption($this->_post['caption'])
);
if (!is_int($newId)) // 0 is valid, NULL or FALSE is not
{
trigger_error('VideoCompleteResponse - video query failed', E_USER_ERROR);
return false;
}
VideoMgr::dropTempFile();
return true;
}
}
?>

View file

@ -0,0 +1,81 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
1. =add: receives user upload
-> 2. =crop: user edites upload
2.1. just show edit page
2.2. user submits coords and description to =complete
3. =complete: store edited video file and data
4. =thankyou
*/
class VideoConfirmResponse extends TemplateResponse
{
protected bool $requiresLogin = true;
protected string $template = 'video';
protected string $pageName = 'video';
public ?Markup $infobox = null;
public string $videoHash = '';
public int $destType = 0;
public int $destTypeId = 0;
public string $url = '';
public int $width = 0;
public int $height = 0;
public array $video = [];
public string $viTitle = '';
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
// get video destination
// target delivered as video=<command>&<type>.<typeId>.<hash:16> (hash is optional)
if (!preg_match('/^video=\w+&(-?\d+)\.(-?\d+)(\.(\w{16}))?$/i', $_SERVER['QUERY_STRING'] ?? '', $m, PREG_UNMATCHED_AS_NULL))
$this->generateError();
[, $this->destType, $this->destTypeId, , $this->videoHash] = $m;
// no such type or this type cannot receive videos
if (!Type::checkClassAttrib($this->destType, 'contribute', CONTRIBUTE_VI))
$this->generateError();
// no such typeId
if (!Type::validateIds($this->destType, $this->destTypeId))
$this->generateError();
}
protected function generate() : void
{
$this->h1 = Lang::video('submission');
array_unshift($this->title, $this->h1);
if (!VideoMgr::loadSuggestion($videoInfo, $this->destType, $this->destTypeId, $this->videoHash))
$this->generateError();
$this->viTitle = $videoInfo->title;
$this->url = $videoInfo->thumbnail_url;
$this->width = $videoInfo->thumbnail_width;
$this->height = $videoInfo->thumbnail_height;
$this->video = [[
'videoType' => VideoMgr::TYPE_YOUTUBE,
'videoId' => $videoInfo->id,
'caption' => $videoInfo->title
]];
// target
$this->infobox = new Markup(Lang::screenshot('displayOn', [Lang::typeName($this->destType), Type::getFileString($this->destType), $this->destTypeId]), ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
$this->extendGlobalIds($this->destType, $this->destTypeId);
parent::generate();
}
}
?>

View file

@ -0,0 +1,60 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
1. =add: receives user upload
2. =crop: user edites upload
3. =complete: store edited video file and data
-> 4. =thankyou
*/
class VideoThankyouResponse extends TemplateResponse
{
protected bool $requiresLogin = true;
protected string $template = 'text-page-generic';
protected string $pageName = 'video';
private int $destType = 0;
private int $destTypeId = 0;
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
// get video destination
// target delivered as video=<command>&<type>.<typeId>
if (!preg_match('/^video=\w+&(-?\d+)\.(-?\d+)$/i', $_SERVER['QUERY_STRING'] ?? '', $m, PREG_UNMATCHED_AS_NULL))
$this->generateError();
[, $this->destType, $this->destTypeId] = $m;
// no such type or this type cannot receive videos
if (!Type::checkClassAttrib($this->destType, 'contribute', CONTRIBUTE_VI))
$this->generateError();
// no such typeId
if (!Type::validateIds($this->destType, $this->destTypeId))
$this->generateError();
}
protected function generate() : void
{
$this->h1 = Lang::video('submission');
array_unshift($this->title, $this->h1);
$this->extraHTML = Lang::video('thanks', 'contrib').'<br /><br />';
$this->extraHTML .= Lang::video('thanks', 'goBack', [Type::getFileString($this->destType), $this->destTypeId])."<br /><br />\n";
$this->extraHTML .= '<i>'.Lang::video('thanks', 'note').'</i>';
parent::generate();
}
}
?>

View file

@ -0,0 +1,229 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class VideoMgr
{
// as expected by js - this also makes the CC-flags functionally exclusive with each other
private const STATUS_PENDING = 0;
private const STATUS_DELETED = 999;
private const STATUS_APPROVED = 100;
private const STATUS_STICKY = 105;
public const TYPE_YOUTUBE = 1; // for in the grim darkness of the future, there is only youtube
public const PATH_TEMP = 'static/uploads/temp/%s';
private static $tmpFile = '';
public static function saveSuggestion(\stdClass $videoInfo, int $destType, int $destTypeId, ?string &$uid) : bool
{
$uid = Util::createHash(16);
self::$tmpFile = sprintf(self::PATH_TEMP, User::$username.'-'.$destType.'-'.$destTypeId.'-'.$uid);
$tmpFile = fopen(self::$tmpFile, 'w');
if (!$tmpFile)
{
trigger_error('VideoMrg::saveSuggestion - failed to create temp file');
return false;
}
fwrite($tmpFile, $videoInfo->id . PHP_EOL);
fwrite($tmpFile, $videoInfo->title . PHP_EOL);
fwrite($tmpFile, $videoInfo->thumbnail_url . PHP_EOL);
fwrite($tmpFile, $videoInfo->thumbnail_height . PHP_EOL);
fwrite($tmpFile, $videoInfo->thumbnail_width . PHP_EOL);
return fclose($tmpFile);
}
public static function loadSuggestion(?\stdClass &$videoInfo, int $destType, int $destTypeId, ?string $uid) : bool
{
self::$tmpFile = sprintf(self::PATH_TEMP, User::$username.'-'.$destType.'-'.$destTypeId.'-'.$uid);
if (!file_exists(self::$tmpFile))
return false;
if ($info = file(self::$tmpFile, FILE_IGNORE_NEW_LINES))
{
$videoInfo = new \stdClass;
$videoInfo->id = $info[0];
$videoInfo->title = $info[1];
$videoInfo->thumbnail_url = $info[2];
$videoInfo->thumbnail_height = (int)$info[3];
$videoInfo->thumbnail_width = (int)$info[4];
return true;
}
return false;
}
public static function dropTempFile()
{
if (!self::$tmpFile || !file_exists(self::$tmpFile))
return;
unlink(self::$tmpFile);
}
/*************/
/* Admin Mgr */
/*************/
public static function getVideos(int $type = 0, int $typeId = 0, $userId = 0, ?int &$nFound = 0) : array
{
/* VideoData
* caption: caption
* date: isodate
* height: ytPreviewImgHeight?
* width: ytPreviewImgWidth?
* id: id
* next: idx || null
* prev: idx || null
* name: ytTitle?
* pending: bool
* status: statusCode
* type: dbType
* typeId: typeId
* user: userName
* url: ytPreviewImg?
* videoType: always 1
* videoId: videoId
* unique: bool || null
*/
$videos = DB::Aowow()->select(
'SELECT v.`id`, a.`username` AS "user", v.`date`, v.`videoId`, v.`type`, v.`typeId`, v.`caption`, v.`status` AS "flags", v.`url`, v.`name`
FROM ?_videos v
LEFT JOIN ?_account a ON v.`userIdOwner` = a.`id`
WHERE
{ v.`type` = ?d }
{ AND v.`typeId` = ?d }
{ v.`userIdOwner` = ?d }
{ LIMIT ?d }
ORDER BY `type`, `typeId`, `pos` ASC',
$userId ? DBSIMPLE_SKIP : $type,
$userId ? DBSIMPLE_SKIP : $typeId,
$userId ? $userId : DBSIMPLE_SKIP,
$userId || $type ? DBSIMPLE_SKIP : 100
);
$num = [];
foreach ($videos as $v)
{
if (empty($num[$v['type']][$v['typeId']]))
$num[$v['type']][$v['typeId']] = 1;
else
$num[$v['type']][$v['typeId']]++;
}
$nFound = 0;
// format data to meet requirements of the js
foreach ($videos as $i => &$v)
{
$nFound++;
$v['date'] = date(Util::$dateFormatInternal, $v['date']);
$v['videoType'] = self::TYPE_YOUTUBE;
if ($i > 0)
$v['prev'] = $i - 1;
if (($i + 1) < count($videos))
$v['next'] = $i + 1;
// order gives priority for 'status'
if (!($v['flags'] & CC_FLAG_APPROVED))
{
$v['pending'] = 1;
$v['status'] = self::STATUS_PENDING;
}
else
$v['status'] = self::STATUS_APPROVED;
if ($v['flags'] & CC_FLAG_STICKY)
{
$v['sticky'] = 1;
$v['status'] = self::STATUS_STICKY;
}
if ($v['flags'] & CC_FLAG_DELETED)
{
$v['deleted'] = 1;
$v['status'] = self::STATUS_DELETED;
}
// something todo with massSelect .. am i doing this right?
if ($num[$v['type']][$v['typeId']] == 1)
$v['unique'] = 1;
if (!$v['user'])
unset($v['user']);
}
return $videos;
}
public static function getPages(?bool $all, ?int &$nFound) : array
{
// i GUESS .. vi_getALL ? everything : pending
$nFound = 0;
$pages = DB::Aowow()->select(
'SELECT v.`type`, v.`typeId`, COUNT(1) AS "count", MIN(v.`date`) AS "date"
FROM ?_videos v
{ WHERE (v.`status` & ?d) = 0 }
GROUP BY v.`type`, v.`typeId`',
$all ? DBSIMPLE_SKIP : CC_FLAG_APPROVED | CC_FLAG_DELETED
);
if ($pages)
{
// limit to one actually existing type each
foreach (array_unique(array_column($pages, 'type')) as $t)
{
$ids = [];
foreach ($pages as $row)
if ($row['type'] == $t)
$ids[] = $row['typeId'];
if (!$ids)
continue;
$obj = Type::newList($t, [Cfg::get('SQL_LIMIT_NONE'), ['id', $ids]]);
if (!$obj || $obj->error)
continue;
foreach ($pages as &$p)
if ($p['type'] == $t)
if ($obj->getEntry($p['typeId']))
$p['name'] = $obj->getField('name', true);
}
foreach ($pages as &$p)
{
if (empty($p['name']))
{
trigger_error('VideoMgr::getPages - video linked to nonexistent type/typeId combination: '.$p['type'].'/'.$p['typeId'], E_USER_NOTICE);
unset($p);
}
else
{
$nFound += $p['count'];
$p['date'] = date(Util::$dateFormatInternal, $p['date']);
}
}
}
return $pages;
}
}
?>

View file

@ -90,7 +90,7 @@ define('SITEREP_ACTION_DAILYVISIT', 2); // Daily visit
define('SITEREP_ACTION_COMMENT', 3); // Posted comment
define('SITEREP_ACTION_UPVOTED', 4); // Your comment was upvoted
define('SITEREP_ACTION_DOWNVOTED', 5); // Your comment was downvoted
define('SITEREP_ACTION_SUBMIT_SCREENSHOT', 6); // Submitted screenshot (suggested video)
define('SITEREP_ACTION_SUBMIT_SCREENSHOT', 6); // Submitted screenshot
// Cast vote
// Uploaded data
define('SITEREP_ACTION_GOOD_REPORT', 9); // Report accepted
@ -98,7 +98,7 @@ define('SITEREP_ACTION_BAD_REPORT', 10); // Report declined
// Copper Achievement
// Silver Achievement
// Gold Achievement
// Test 1
define('SITEREP_ACTION_SUGGEST_VIDEO', 14); // repurposed, originally: Test 1
// Test 2
define('SITEREP_ACTION_ARTICLE', 16); // Guide approved (article approved)
define('SITEREP_ACTION_USER_WARNED', 17); // Moderator Warning

View file

@ -727,12 +727,12 @@ abstract class Util
$x['amount'] = $action == SITEREP_ACTION_UPVOTED ? Cfg::get('REP_REWARD_UPVOTED') : Cfg::get('REP_REWARD_DOWNVOTED');
break;
case SITEREP_ACTION_SUBMIT_SCREENSHOT:
case SITEREP_ACTION_SUGGEST_VIDEO:
if (empty($miscData['id']) || empty($miscData['what']))
return false;
$x['sourceA'] = $miscData['id']; // screenshotId or videoId
$x['sourceB'] = $miscData['what']; // screenshot:1
$x['amount'] = Cfg::get('REP_REWARD_UPLOAD');
$x['amount'] = $action == SITEREP_ACTION_SUBMIT_SCREENSHOT ? Cfg::get('REP_REWARD_SUBMIT_SCREENSHOT') : Cfg::get('REP_REWARD_SUGGEST_VIDEO');
break;
case SITEREP_ACTION_GOOD_REPORT: // NYI
case SITEREP_ACTION_BAD_REPORT:

View file

@ -16,6 +16,7 @@ class Lang
private static $maps;
private static $profiler;
private static $screenshot;
private static $video;
private static $privileges;
private static $smartAI;
private static $unit;

View file

@ -278,6 +278,20 @@ $lang = array(
'notAllowed' => "Es ist euch nicht erlaubt einen Screenshot hochzuladen!",
)
),
'video' => array(
'submission' => "Video-Einsendung",
'thanks' => array(
'contrib' => "Vielen Dank für Euren Beitrag!",
'goBack' => '<a href="?%s=%d">Klickt hier</a>, um zu der vorherigen Seite zurückzukehren.',
'note' => "Hinweis: Euer Video muss zunächst zugelassen werden, bevor es auf der Seite erscheint. Dies kann bis zu 72 Stunden dauern."
),
'error' => array(
'isPrivate' => "Das vorgeschlagene Video ist privat.",
'noExist' => "An der eingereichten Url existiert kein Video.",
'selectVI' => "Bitte gebt gültige Videoinformationen ein.",
'notAllowed' => "Es ist euch nicht erlaubt Videos vorzuschlagen!"
)
),
'game' => array(
// type strings
'npc' => "NPC", // 1

View file

@ -278,6 +278,20 @@ $lang = array(
'notAllowed' => "You are not allowed to upload screenshots!",
)
),
'video' => array(
'submission' => "Video Suggestion",
'thanks' => array(
'contrib' => "Thanks a lot for your contribution!",
'goBack' => '<a href="?%s=%d">Click here</a> to go back to the page you came from.',
'note' => "Note: Your video will need to be approved before appearing on the site. This can take up to 72 hours."
),
'error' => array(
'isPrivate' => "The suggested video is private.",
'noExist' => "No video found at the provided Url.",
'selectVI' => "Please enter valid video information.", // message_novideo
'notAllowed' => "You are not allowed to suggest videos!",
)
),
'game' => array(
// type strings
'npc' => "NPC",

View file

@ -278,6 +278,20 @@ $lang = array(
'notAllowed' => "¡No estás permitido para subir capturas de pantalla!",
)
),
'video' => array(
'submission' => "Sugerencia de video",
'thanks' => array(
'contrib' => "¡Muchísimas gracias por tu aportación!",
'goBack' => '<a href="?%s=%d">aquí vuelve</a> a la página de la que viniste.',
'note' => "Nota: Tu video tiene que ser aprobado antes de que pueda aparecer en el sitio. Esto puede tomar hasta 72 horas."
),
'error' => array(
'isPrivate' => "El video sugerido es privado.",
'noExist' => "No se encontró ningún video en la URL proporcionada.",
'selectVI' => "Por favor, introduce información válida del vídeo.", // message_novideo
'notAllowed' => "¡No tienes permiso para sugerir videos!",
)
),
'game' => array(
// type strings
'npc' => "PNJ",

View file

@ -278,6 +278,20 @@ $lang = array(
'notAllowed' => "Vous n'êtes pas autorisés à exporter des captures d'écran.",
)
),
'video' => array(
'submission' => "Suggestion de vidéo",
'thanks' => array(
'contrib' => "Merci beaucoup de votre contribution!",
'goBack' => '<a href="?%s=%d">ici</a> pour retourner à la page d\'où vous venez.',
'note' => "Note : Votre vidéo devra être approuvée avant d'apparaître sur le site. Cela peut prendre jusqu'à 72 heures."
),
'error' => array(
'isPrivate' => "La vidéo suggérée est privée.",
'noExist' => "Aucune vidéo trouvée à l'URL fournie.",
'selectVI' => "Veuillez entrer des informations valides pour la vidéo.", // message_novideo
'notAllowed' => "Vous n'êtes pas autorisé à suggérer des vidéos!",
)
),
'game' => array(
// type strings
'npc' => "PNJ",

View file

@ -278,6 +278,20 @@ $lang = array(
'notAllowed' => "[You are not allowed to upload screenshots!]",
)
),
'video' => array(
'submission' => "Предложить видео",
'thanks' => array(
'contrib' => "Спасибо за ваш вклад!",
'goBack' => '<a href="?%s=%d">здесь</a> чтобы перейти к предыдущей странице.',
'note' => "Примечание: Ваше видео должно быть одобрено, прежде чем появится на сайте. Это может занять до 72 часов."
),
'error' => array(
'isPrivate' => "Предложенное видео является приватным.",
'noExist' => "Видео по предоставленной ссылке не найдено.",
'selectVI' => "введите корректную информацию о видео.", // message_novideo
'notAllowed' => "У вас нет прав предлагать видео!",
)
),
'game' => array(
// type strings
'npc' => "НИП",

View file

@ -278,7 +278,22 @@ $lang = array(
'notAllowed' => "你不允许上传截图!",
)
),
'video' => array(
'submission' => "视频建议",
'thanks' => array(
'contrib' => "非常感谢你的贡献!",
'goBack' => '<a href="?%s=%d">点击这里</a>返回上一页。',
'note' => "注意您的视频需要经过审核后才能显示在网站上。这需要最多72小时。"
),
'error' => array(
'isPrivate' => "建议的视频为私有。",
'noExist' => "在提供的链接中未找到视频。",
'selectVI' => "请输入有效的视频信息。", // message_novideo
'notAllowed' => "您没有权限建议视频!",
)
),
'game' => array(
// type strings
'npc' => "NPC",
'npcs' => "NPC",
'object' => "对象",

View file

@ -0,0 +1,8 @@
-- `key` is too small for our new configs
ALTER TABLE `aowow_config`
MODIFY COLUMN `key` varchar(50) NOT NULL;
-- split generic upload in ss / vi
UPDATE `aowow_config` SET `key` = 'rep_reward_submit_screenshot', `comment` = 'uploaded screenshot was approved' WHERE `key` = 'rep_reward_upload';
DELETE FROM `aowow_config` WHERE `key` = 'rep_reward_suggest_video';
INSERT INTO `aowow_config` VALUES ('rep_reward_suggest_video', '10', '10', 5, 129, 'suggested video was approved');

View file

@ -0,0 +1,8 @@
-- update video storage
ALTER TABLE `aowow_videos`
ADD COLUMN `pos` tinyint unsigned NOT NULL AFTER `videoId`,
ADD COLUMN `url` varchar(64) NOT NULL COMMENT 'preview thumb' AFTER `pos`,
ADD COLUMN `width` smallint unsigned NOT NULL AFTER `url`,
ADD COLUMN `height` smallint unsigned NOT NULL AFTER `width`,
ADD COLUMN `name` varchar(64) DEFAULT NULL AFTER `height`,
MODIFY COLUMN `caption` varchar(200) DEFAULT NULL;

View file

@ -0,0 +1,4 @@
-- update article affected by cfg change
UPDATE `aowow_acticles` SET
`article` = '[b]Reputation[/b] is a rough measurement of how much you participate in the community--it is earned by convincing your peers that you know what youre talking about. Our community puts just as much work as our developers do into making our site as awesome as it is and reputation is meant as a way for you to track just how much work you\'re putting into us.\r\n\r\nThe primary means of gaining reputation is by posting quality comments on database entries (which are then voted up by other site members) and by general contributions to the site which can include actions like data and screenshot submissions. Whenever you leave a comment on a database entry, your peers can then vote on these comments, and those votes will cause you to gain reputation. You can also earn reputation by voting on other users\' comments and by sending in reports!\r\n\r\nBy being a good-standing and contributing user you will be able to earn both reputation and achievements for many of the same actions!\r\n\r\n[h3]Reputation Gains[/h3]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td][url=?account=signup]Registering[/url] an account[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_REGISTER reputation[/td]\r\n[/tr]\r\n[tr][td]Daily visit[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_DAILYVISIT reputation[/td]\r\n[/tr]\r\n[tr][td]Posting a comment[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Your comment was voted up (each upvote)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_UPVOTED reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a screenshot[/td]\r\n[td align=right class=no-wrap]REP_REWARD_SUBMIT_SCREENSHOT reputation[/td]\r\n[/tr]\r\n[tr][td]Suggesting a video[/td]\r\n[td align=right class=no-wrap]REP_REWARD_SUGGEST_VIDEO reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a guide (approved)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_ARTICLE reputation[/td]\r\n[/tr]\r\n[tr][td]Filing a report (accepted)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_GOOD_REPORT reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n\r\n\r\n[h3]Site Privileges[/h3]\r\nThe higher your reputation level, the more privileges you gain. Earn a high enough reputation to unlock additional rewards, in the form of new privileges around the site!\r\n[pad]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td]Post comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Upvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_UPVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]Downvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_DOWNVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]More votes per day[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_VOTEMORE_BASE reputation[/td]\r\n[/tr]\r\n[tr][td]Comment votes worth more[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_SUPERVOTE reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n[pad]\r\n[url=?privileges]Check out full details on site privileges you can earn![/url]\r\n'
WHERE `url` = 'reputation' AND `locale` = 0;

View file

@ -2831,9 +2831,10 @@ var vi_siteurls = {
1: 'https://www.youtube.com/watch?v=$1' // YouTube
};
var vi_sitevalidation = {
1: /^https?:\/\/www\.youtube\.com\/watch\?v=([^& ]{11})/ // YouTube
};
var vi_sitevalidation = [
/https?:\/\/(?:www\.)?youtube\.com\/watch\?v=([^& ]{11})/i,
/https?:\/\/(?:www\.)?youtu\.be\/([^& ]{11})/i
];
function vi_submitAVideo() {
tabsContribute.focus(2);
@ -2882,7 +2883,7 @@ function vi_appendSticky() {
};
var img = $WH.ce('img');
img.src = $WH.sprintf(vi_thumbnails[video.videoType], video.videoId);
img.src = $WH.sprintf(vi_thumbnails[video.videoType].replace(/\/default\.jpg/, '/mqdefault.jpg'), video.videoId);
img.className = 'border';
$WH.ae(a, img);
@ -3227,7 +3228,7 @@ var VideoViewer = new function() {
aCover.onclick = Lightbox.hide;
var foo = $WH.ce('span');
var b = $WH.ce('b');
$WH.ae(b, $WH.ct(LANG.close));
// $WH.ae(b, $WH.ct(LANG.close));
$WH.ae(foo, b);
$WH.ae(aCover, foo);
@ -3312,7 +3313,7 @@ var VideoViewer = new function() {
onShow: onShow,
onHide: onHide,
onResize: onResize
},opt);
}, opt);
return false;
}

View file

@ -13,7 +13,7 @@ var l_reputation_names = [
"Bronzeerfolg",
"Silbererfolg",
"Golderfolg",
'Test 1',
'Video vorgeschlagen', // aowow - originally: Test 1
'Test 2',
"Leitfaden zugelassen",
"Warnung durch Moderator",

View file

@ -13,7 +13,7 @@ var l_reputation_names = [
"Copper Achievement",
"Silver Achievement",
"Gold Achievement",
'Test 1',
'Video suggested', // aowow - originally: Test 1
'Test 2',
"Guide approved",
"Moderator Warning",

View file

@ -13,7 +13,7 @@ var l_reputation_names = [
"Logro de Cobre",
"Logro de Plata",
"Logro de Oro",
'Test 1',
"Vídeo sugerido", // aowow - originally: Test 1
'Test 2',
"Guía aprobada",
"Aviso de moderador",

View file

@ -13,7 +13,7 @@ var l_reputation_names = [
"Haut-fait de bronze",
"Haut-fait d'argent",
"Haut-fait d'or",
'Test 1',
"Vidéo suggérée", // aowow - originally: Test 1
'Test 2',
"Guide approuvé",
"Avertissement d'un modérateur",

View file

@ -13,7 +13,7 @@ var l_reputation_names = [
"Бронзовое достижение",
"Серебряное достижение",
"Золотое достижение",
"Test 1",
"Видео предложено", // aowow - originally: Test 1
"Test 2",
"Гайд одобрен",
"Пожаловаться модератору",

View file

@ -13,7 +13,7 @@ var l_reputation_names = [
"铜牌成就",
"银牌成就",
"金牌成就",
'Test 1',
'建议视频', // aowow - originally: Test 1
'Test 2',
"指南通过审核",
"管理员警告",

1135
static/js/video.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,135 @@
<?php
namespace Aowow\Template;
$this->brick('header');
?>
<div class="main" id="main">
<div class="main-precontents" id="main-precontents"></div>
<div class="main-contents" id="main-contents">
<?php
$this->brick('announcement');
$this->brick('pageTemplate');
?>
<div class="text">
<h1><?=$this->h1; ?></h1>
<table>
<tr>
<td>User: </td>
<td colspan="2"><input type="text" id="usermanage" size="23"></td>
<td>&raquo;&nbsp;<a href="#" onClick="vi_ManageUser()">Search by User</a></td>
</tr>
<tr>
<td>Page: </td>
<td>
<select id="pagetype">
<?=$this->makeOptionsList($this->pageTypes, null, 32); ?>
</select>
</td>
<td>#<input type="number" size="6" id="pagetypeid"></td>
<td>&raquo;&nbsp;<a href="#" onClick="vi_Manage(null, $('#pagetype').val(), parseInt($('#pagetypeid').val()) || 0)">Search by Page</a></td>
</tr>
</table>
<hr />
<table style="width:100%;">
<thead><tr><th style="width:135px;"><div>Menu</div></th><th style="width:400px;">Pages</th><th>Videos: <span id="videoTotal"></span></th></tr></thead>
<tbody><tr>
<td id="menu-container" style="vertical-align: top;">
<div id="show-all-pages"><?=($this->viNFound ? ' &ndash; <a href="?admin=videos&all">Show All</a> ('.$this->viNFound.')' : ''); ?></div>
<h4>Mass Select</h4>
&ndash; <a href="#" onClick="vim_MassSelect(1);">Select All</a><br />
&ndash; <a href="#" onClick="vim_MassSelect(0);">Deselect All</a><br />
&ndash; <a href="#" onClick="vim_MassSelect(-1);">Toggle Selection</a><br />
&ndash; <a href="#" onClick="vim_MassSelect(2);">Select All Pending</a><br />
&ndash; <a href="#" onClick="vim_MassSelect(5);">Select All Unique</a><br />
&ndash; <a href="#" onClick="vim_MassSelect(3);">Select All Approved</a><br />
&ndash; <a href="#" onClick="vim_MassSelect(4);">Select All Sticky</a><br />
<div id="withselected" style="display:none;">
<h4>Mass Action <b>(0)</b></h4>
&ndash; <a href="#" id="massapprove">Approve All</a><br />
&ndash; <a href="#" id="massdelete">Delete All</a><br />
&ndash; <a href="#" id="masssticky">Sticky All</a><br />
</div>
</td>
<td id="pages-container" style="vertical-align: top;"></td>
<td id="data-container" style="vertical-align: top;"><table class="grid" id="theVideosList"><thead><tr>
<th>Video</th>
<th>Id</th>
<th>Title</th>
<th>Date</th>
<th>Uploader</th>
<th>Status</th>
<th>Options</th>
</tr></thead></table></td>
</tr></tbody>
</table>
<script type="text/javascript">
var hasLoader = false;
function ajaxAnchor(el)
{
if (!el.href || hasLoader)
return;
$('#withselected').find('h4').append("&nbsp;").append(CreateAjaxLoader());
hasLoader = true;
new Ajax(el.href, {
method: 'get',
onSuccess: function(xhr) {
hasLoader = false;
$('#withselected img').remove();
var g = $WH.g_getGets();
if (g.type && g.typeid)
vi_Manage(null, g.type, g.typeid);
else if (g.user)
vi_ManageUser();
else
vi_Refresh();
}
});
}
$WH.ge('usermanage').onkeydown = function(e)
{
e = $WH.$E(e);
if (e.keyCode != 13)
return;
vi_ManageUser();
}
$WH.ge('pagetypeid').onkeydown = function(e)
{
e = $WH.$E(e);
var validKeys = [8, 9, 13, 35, 36, 37, 38, 39, 40, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 173];
if (!e.ctrlKey && $WH.in_array(validKeys, e.keyCode) == -1)
return false;
if (e.keyCode == 13 && this.value != '')
vi_Manage(null, $('#pagetype').val(), parseInt($('#pagetypeid').val()) || 0);
return true;
}
<?php
if ($this->getAll):
echo " var vi_getAll = true;\n";
endif;
if ($this->viPages):
echo " var vim_videoPages = ".$this->json($this->viPages).";\n";
echo " vim_UpdatePages();\n";
elseif ($this->viData):
echo " var vim_videoData = ".$this->json($this->viData).";\n";
echo " vim_UpdateList();\n";
endif;
?>
vi_OnResize();
</script>
</div>
</div><!-- main-contents -->
</div><!-- main -->
<?php $this->brick('footer'); ?>

View file

@ -0,0 +1,82 @@
<?php
namespace Aowow\Template;
use \Aowow\Lang;
$this->brick('header');
?>
<div class="main" id="main">
<div class="main-precontents" id="main-precontents"></div>
<div class="main-contents" id="main-contents">
<?php
$this->brick('announcement');
$this->brick('pageTemplate');
$this->brick('infobox');
?>
<div class="text">
<h1><?=$this->h1; ?></h1>
<h3><?=$this->viTitle;?></h3>
<div class="pad"></div>
<div id="vi-container"></div><script type="text/javascript">//<![CDATA[
let video = <?=$this->json($this->video); ?>;
//]]></script>
<a href="#" onClick="VideoViewer.show({ videos: video });"><img class="border" src="<?=$this->url; ?>" width="<?=$this->width; ?>" heigth="<?=$this->height; ?>"></img></a>
<div class="pad3"></div>
<form action="?video=complete&amp;<?=$this->destType.'.'.$this->destTypeId.'.'.$this->videoHash; ?>" method="post">
<?=Lang::screenshot('caption').Lang::main('colon'); ?><input type="text" name="caption" style="width: 55%" maxlength="200" /> <small> <?=Lang::screenshot('charLimit'); ?></small><br />
<div class="pad3"></div>
<input type="submit" value="<?=Lang::main('submit'); ?>" />
</form>
<script type="text/javascript">//<![CDATA[
var
captionMaxLen = 200;
Body = $('#main-contents').find('input[type=text]');
TextCounter = $('#main-contents').find('small');
Body.focus();
Body.keyup(function (e) { return UpdateTextCounter(); });
Body.keypress(function (e) { // ENTER
if (e.keyCode == 13 || captionMaxLen - getCaptionLen() <= 0 )
return false;
});
function getCaptionLen()
{
return Body.val().replace(/(\s+)/g, ' ').replace(/^\s*/, '').replace(/\s*$/, '').length;
}
function UpdateTextCounter()
{
var text = '(error)';
var cssClass = 'q0';
var chars = getCaptionLen();
var charsLeft = captionMaxLen - chars;
text = $WH.sprintf(charsLeft == 1 ? LANG.replylength4_format : LANG.replylength3_format, charsLeft);
if (charsLeft < 25)
cssClass = 'q10';
else if (charsLeft < 50)
cssClass = 'q5';
else if (charsLeft < 75)
cssClass = 'q11';
TextCounter.html(text).attr('class', cssClass);
}
//]]></script>
</div>
</div><!-- main-contents -->
</div><!-- main -->
<?php $this->brick('footer'); ?>