Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion admin_console/configs/navigation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,16 @@
<controller>index</controller>
<action>testme-doc</action>
</testme-doc>
<entry-restoration>
<label>Entry Restoration</label>
<controller>index</controller>
<action>entry-restoration</action>
</entry-restoration>
<apc>
<label>APC</label>
<controller>index</controller>
<action>apc</action>
</apc>
</apc>
<memcache>
<label>Memcache</label>
<controller>index</controller>
Expand Down
137 changes: 135 additions & 2 deletions admin_console/controllers/IndexController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,141 @@ public function kelloggsAction()
$this->view->kelloggsUrl = $kelloggsDashboard->url;
$this->view->kelloggsServiceUrl = $kelloggsDashboard->serviceUrl;
$this->view->kelloggsJwt = Form_JwtHelper::getJwt(
$kelloggsDashboard->jwtKey,
$settings->partnerId,
$kelloggsDashboard->jwtKey,
$settings->partnerId,
$settings->sessionExpiry);
}

public function entryRestorationAction()
{
if (!Infra_AclHelper::isAllowed('developer', 'entry-restoration'))
{
return;
}

$this->view->errors = array();
$this->view->results = null;
$this->view->isDryRun = false;

if ($this->getRequest()->isPost())
{
try {
$partnerId = $this->getRequest()->getParam('partnerId');
$inputMode = $this->getRequest()->getParam('inputMode', 'textarea');
$dryRun = $this->getRequest()->getParam('dryRun', 'false') === 'true';
$entryIdsString = '';

// Validate partner ID
if (empty($partnerId) || !is_numeric($partnerId))
{
$this->view->errors[] = "Invalid Partner ID";
return;
}

// Get entry IDs based on input mode
if ($inputMode === 'file')
{
// Handle file upload
if (!empty($_FILES['entryFile']['tmp_name']))
{
// Validate file size (max 5MB)
if ($_FILES['entryFile']['size'] > 5 * 1024 * 1024)
{
$this->view->errors[] = "File is too large. Maximum size is 5MB";
return;
}

// Validate file extension
$fileName = $_FILES['entryFile']['name'];
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (!in_array($fileExtension, array('txt', 'csv')))
{
$this->view->errors[] = "Invalid file type. Only .txt and .csv files are allowed";
return;
}

$fileContent = file_get_contents($_FILES['entryFile']['tmp_name']);
if ($fileContent === false)
{
$this->view->errors[] = "Failed to read uploaded file";
return;
}

// Validate that file contains at least one entry ID
$fileContent = trim($fileContent);
if (empty($fileContent))
{
$this->view->errors[] = "Uploaded file is empty";
return;
}

// Basic validation - check if content looks like entry IDs
// Entry IDs follow format: {dc_id}_{random_string} where dc_id is datacenter ID
// and random_string is typically 8-10 characters alphanumeric (e.g., 0_abc123xy, 0_abc123xy12)
$lines = preg_split('/[\r\n,]+/', $fileContent);
$validEntryIdPattern = '/^[0-9]+_[a-z0-9]{8,}$/i';
$hasValidEntryId = false;

foreach ($lines as $line)
{
$line = trim($line);
if (!empty($line))
{
if (preg_match($validEntryIdPattern, $line))
{
$hasValidEntryId = true;
break;
}
}
}

if (!$hasValidEntryId)
{
$this->view->errors[] = "File does not contain valid entry IDs. Entry IDs should be in format: 0_abc123xy (datacenter_alphanumeric)";
return;
}

$entryIdsString = $fileContent;
}
else
{
$this->view->errors[] = "No file uploaded";
return;
}
}
else
{
// Handle textarea or single input
$entryIdsString = $this->getRequest()->getParam('entryIds', '');
if (empty($entryIdsString))
{
$this->view->errors[] = "No entry IDs provided";
return;
}
}

// Initialize Kaltura client
$client = Infra_ClientHelper::getClient();
// Do NOT set partner ID - keep it as admin console partner (-2)
// so the service permission check passes
// $client->setPartnerId($partnerId);

// Prepare bulk restore data
$bulkRestoreData = new Kaltura_Client_AdminConsole_Type_BulkRestoreEntryData();
$bulkRestoreData->partnerId = $partnerId;
$bulkRestoreData->entryIds = $entryIdsString;
$bulkRestoreData->dryRun = $dryRun;

// Call the bulk restore API
$entryAdminPlugin = Kaltura_Client_AdminConsole_Plugin::get($client);
$response = $entryAdminPlugin->entryAdmin->bulkRestoreDeletedEntries($bulkRestoreData);

$this->view->results = $response->objects;
$this->view->isDryRun = $dryRun;

} catch (Exception $e) {
$this->view->errors[] = "Error: " . $e->getMessage();
}
}
}
}
178 changes: 178 additions & 0 deletions admin_console/views/scripts/index/entry-restoration.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<h2>Bulk Entry Restoration</h2>

<p>This tool allows you to restore multiple deleted entries in bulk. You can provide entry IDs via file upload, comma-separated list, or a single entry ID.</p>

<?php if (count($this->errors)): ?>
<div class="error">
<ul>
<?php foreach($this->errors as $error): ?>
<li><?php echo htmlspecialchars($error); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>

<form method="post" action="<?php echo $this->url(array('controller' => 'index', 'action' => 'entry-restoration'), null, true); ?>" enctype="multipart/form-data" id="frmEntryRestoration">
<fieldset>
<p style="margin-bottom: 15px;">
<label for="partnerId" style="display: inline-block; width: 120px; font-weight: bold;">Partner ID:</label>
<input type="text" name="partnerId" id="partnerId" required style="width: 200px;" />
</p>

<p style="margin-bottom: 15px;">
<label for="inputMode" style="display: inline-block; width: 120px; font-weight: bold;">Input Mode:</label>
<select name="inputMode" id="inputMode" onchange="toggleInputMode()" style="width: 350px;">
<option value="textarea" selected>Comma-separated or Line-separated List</option>
<option value="single">Single Entry ID</option>
<option value="file">File Upload (one entry ID per line)</option>
</select>
</p>

<p id="entryIdsField" style="margin-bottom: 15px;">
<label for="entryIds" style="display: inline-block; width: 120px; font-weight: bold; vertical-align: top;">Entry IDs:</label>
<textarea name="entryIds" id="entryIds" rows="8" cols="70" style="width: 500px; font-family: monospace; vertical-align: top;"></textarea>
</p>

<p id="entryFileField" style="display:none; margin-bottom: 15px;">
<label for="entryFile" style="display: inline-block; width: 120px; font-weight: bold;">Upload File:</label>
<input type="file" name="entryFile" id="entryFile" accept=".txt,.csv" />
</p>

<p style="margin-bottom: 15px; padding-left: 120px;">
<label>
<input type="checkbox" name="dryRun" id="dryRun" value="true" checked />
Dry Run (validation only, no actual restoration)
</label>
</p>

<p style="margin-bottom: 10px; padding-left: 120px;">
<button type="submit">Validate/Restore Entries</button>
</p>
</fieldset>
</form>

<script type="text/javascript">
function toggleInputMode() {
var mode = document.getElementById('inputMode').value;
var entryIdsField = document.getElementById('entryIdsField');
var entryFileField = document.getElementById('entryFileField');

if (mode === 'file') {
entryIdsField.style.display = 'none';
entryFileField.style.display = 'block';
document.getElementById('entryIds').required = false;
document.getElementById('entryFile').required = true;
} else {
entryIdsField.style.display = 'block';
entryFileField.style.display = 'none';
document.getElementById('entryIds').required = true;
document.getElementById('entryFile').required = false;

if (mode === 'single') {
document.getElementById('entryIds').rows = 1;
document.getElementById('entryIds').placeholder = 'Enter a single entry ID';
} else {
document.getElementById('entryIds').rows = 10;
document.getElementById('entryIds').placeholder = 'Enter entry IDs separated by commas or newlines';
}
}
}
</script>

<?php if (isset($this->results) && count($this->results) > 0): ?>
<div id="results" class="clear">
<?php
// Calculate statistics
$totalEntries = count($this->results);
$successCount = 0;
$errorCount = 0;
foreach($this->results as $result) {
if ($result->restored) {
$successCount++;
} else {
$errorCount++;
}
}
?>

<h2>
Restoration Results
<span>(<?php echo $totalEntries; ?> <?php echo $totalEntries == 1 ? 'entry' : 'entries'; ?>)</span>
<?php if (isset($this->isDryRun) && $this->isDryRun): ?>
<span style="color: #ff9800; font-size: 12px; font-weight: normal;"> - Dry Run (Validation Only)</span>
<?php endif; ?>
</h2>

<p style="margin: 10px 0;">
<strong>Restorable:</strong> <span style="color: green;"><?php echo $successCount; ?></span> |
<strong>Not Restorable:</strong> <span style="color: red;"><?php echo $errorCount; ?></span>
</p>

<table class="clear">
<thead>
<tr>
<th>Entry ID</th>
<th>Restorable</th>
<th>Status/Error</th>
</tr>
</thead>
<tbody>
<?php
foreach($this->results as $result):
$rowClass = $this->cycle(array('odd', 'even'))->next();
?>
<tr class="<?php echo $rowClass; ?>">
<td><?php echo htmlspecialchars($result->entryId); ?></td>
<td style="<?php echo $result->restored ? 'color:green; font-weight: bold;' : 'color:red; font-weight: bold;'; ?>">
<?php echo $result->restored ? 'Yes' : 'No'; ?>
</td>
<td style="<?php echo $result->restored ? 'color:green;' : 'color:red;'; ?>">
<?php
if ($result->restored) {
echo isset($this->isDryRun) && $this->isDryRun ? 'Ready to restore' : 'Successfully restored';
} else {
echo htmlspecialchars($result->error ? $result->error : 'Cannot be restored');
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>

<?php if (isset($this->isDryRun) && $this->isDryRun): ?>
<?php
// Only include entries that passed validation
$validEntries = array();
foreach($this->results as $result) {
if ($result->restored) {
$validEntries[] = $result->entryId;
}
}
?>
<br />
<?php if (count($validEntries) > 0): ?>
<div style="background: #fff3cd; border: 1px solid #ffc107; padding: 15px 20px; margin: 20px 0;">
<form method="post" action="<?php echo $this->url(array('controller' => 'index', 'action' => 'entry-restoration'), null, true); ?>" style="margin: 0; text-align: center;">
<input type="hidden" name="partnerId" value="<?php echo htmlspecialchars($_POST['partnerId']); ?>" />
<input type="hidden" name="entryIds" value="<?php echo htmlspecialchars(implode(',', $validEntries)); ?>" />
<input type="hidden" name="dryRun" value="false" />
<input type="hidden" name="inputMode" value="textarea" />
<p style="color: #856404; font-weight: bold; margin: 0 0 10px 0;">
<strong>Warning:</strong> This will actually restore the validated entries. This action cannot be undone automatically.
</p>
<button type="submit" style="background: #E8E8E8; background-image: none;">
Proceed with Real Restoration (<?php echo count($validEntries); ?> <?php echo count($validEntries) == 1 ? 'entry' : 'entries'; ?>)
</button>
</form>
</div>
<?php else: ?>
<div style="background: #f8d7da; border: 1px solid #f5c6cb; padding: 20px; margin: 20px 0;">
<p style="color: #721c24; font-weight: bold; margin: 0;">
No entries are eligible for restoration. Please fix the errors above and try again.
</p>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php endif; ?>
1 change: 1 addition & 0 deletions configurations/admin.template.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,7 @@ access.user.logout = *
access.user.reset-password = *
access.user.reset-password-link = *
access.index = *
access.index.entry-restoration = SYSTEM_ADMIN_DEVELOPERS_TAB
;access.accounts = ps

access.partner.all = SYSTEM_ADMIN_PUBLISHER_BASE
Expand Down
9 changes: 9 additions & 0 deletions deployment/permissions/service.adminconsole.entryadmin.ini
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ permissionItem5.param5 =
permissionItem5.tags =
permissionItem5.permissions = PARTNER_-2_GROUP_*_PERMISSION, RESTORE_DELETED_ENTRY

permissionItem6.service = adminconsole_entryadmin
permissionItem6.action = bulkrestoredeletedentries
permissionItem6.partnerId = -2
permissionItem6.param3 =
permissionItem6.param4 =
permissionItem6.param5 =
permissionItem6.tags =
permissionItem6.permissions = PARTNER_-2_GROUP_*_PERMISSION, RESTORE_DELETED_ENTRY

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
/**
* @package deployment
* @subpackage roles_and_permissions
*
* Update permission to adminconsole entryadmin - add bulkRestoreDeletedEntries action
*/

$addPermissionsAndItemsScript = realpath(dirname(__FILE__) . '/../../../../') . '/alpha/scripts/utils/permissions/addPermissionsAndItems.php';

$config = realpath(dirname(__FILE__)) . '/../../../permissions/service.adminconsole.entryadmin.ini';
passthru("php $addPermissionsAndItemsScript $config");

Loading