You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
29 KiB
414 lines
29 KiB
<?php
|
|
if (file_exists(__DIR__."/../../pp-config.php")) {
|
|
if (file_exists(__DIR__.'/../../maintenance.lock')) {
|
|
if (file_exists(__DIR__.'/../../pp-include/pp-maintenance.php')) {
|
|
|
|
}else{
|
|
die('System is under maintenance. Please try again later.');
|
|
}
|
|
exit();
|
|
}else{
|
|
if (file_exists(__DIR__.'/../../pp-include/pp-controller.php')) {
|
|
if (file_exists(__DIR__.'/../../pp-include/pp-view.php')) {
|
|
|
|
}else{
|
|
echo 'System is under maintenance. Please try again later.';
|
|
exit();
|
|
}
|
|
}else{
|
|
echo 'System is under maintenance. Please try again later.';
|
|
exit();
|
|
}
|
|
|
|
if (file_exists(__DIR__.'/../../pp-include/pp-model.php')) {
|
|
include(__DIR__."/../../pp-include/pp-model.php");
|
|
}else{
|
|
echo 'System is under maintenance. Please try again later.';
|
|
exit();
|
|
}
|
|
}
|
|
}else{
|
|
echo 'System is under maintenance. Please try again later.';
|
|
exit();
|
|
}
|
|
|
|
if (!defined('pp_allowed_access')) {
|
|
die('Direct access not allowed');
|
|
}
|
|
|
|
if(isset($global_user_login) && $global_user_login == true){
|
|
?>
|
|
<style>
|
|
.plugin-card {
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
border-radius: 0.5rem;
|
|
overflow: hidden;
|
|
}
|
|
.plugin-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.1);
|
|
}
|
|
.plugin-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 0.5rem;
|
|
font-size: 1.5rem;
|
|
}
|
|
.plugin-icon img{
|
|
height: 40px;
|
|
}
|
|
.status-badge {
|
|
font-size: 0.65rem;
|
|
letter-spacing: 0.5px;
|
|
padding: 3px 6px;
|
|
}
|
|
.nav-pills .nav-link.active {
|
|
|
|
}
|
|
.nav-pills .nav-link {
|
|
color: #495057;
|
|
font-weight: 500;
|
|
}
|
|
.tab-content {
|
|
background: #fff;
|
|
border-radius: 0 0.5rem 0.5rem 0.5rem;
|
|
}
|
|
.search-box {
|
|
position: relative;
|
|
}
|
|
.search-box .form-control {
|
|
padding-left: 2.5rem;
|
|
border-radius: 2rem;
|
|
}
|
|
.search-box .bi {
|
|
position: absolute;
|
|
left: 1rem;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: #6c757d;
|
|
}
|
|
.plugin-actions .btn {
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.8rem;
|
|
}
|
|
</style>
|
|
|
|
<div class="row">
|
|
<div class="col-lg-12 mx-auto">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white border-0 pb-0">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h4 mb-0">
|
|
<i class="bi bi-plug me-2 text-primary"></i>Plugin Manager
|
|
</h1>
|
|
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#add-new-plugins">
|
|
<i class="bi bi-plus-lg me-1"></i> Add New
|
|
</button>
|
|
</div>
|
|
|
|
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="pills-installed-tab" data-bs-toggle="pill" data-bs-target="#pills-installed" type="button" role="tab">
|
|
<i class="bi bi-check-circle me-1"></i> Installed (<span class="installed-count">0</span>)
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<span class="response-action"></span>
|
|
|
|
<div class="card-body pt-0">
|
|
<div class="tab-content" id="pills-tabContent">
|
|
<!-- Installed Plugins Tab -->
|
|
<div class="tab-pane fade show active" id="pills-installed" role="tabpanel">
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="search-box">
|
|
<i class="bi bi-search"></i>
|
|
<input type="text" class="form-control" id="pluginSearch" onkeyup="filterPlugins('all')"placeholder="Search installed plugins...">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="d-flex justify-content-end">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="bi bi-funnel me-1"></i> Filter
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-end">
|
|
<li><a class="dropdown-item" href="#" onclick="filterPlugins('all')">All Plugins</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item" href="#" onclick="filterPlugins('active')">Active Only</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="filterPlugins('inactive')">Inactive Only</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<?php
|
|
$baseDir = __DIR__.'/../../pp-content/plugins/';
|
|
|
|
$mainFolders = array_filter(scandir($baseDir), function($item) use ($baseDir) {
|
|
return $item !== '.' && $item !== '..' && is_dir($baseDir . DIRECTORY_SEPARATOR . $item);
|
|
});
|
|
|
|
$foundPlugins = false;
|
|
|
|
$installed_count = 0;
|
|
|
|
foreach ($mainFolders as $mainFolder) {
|
|
$pluginBasePath = $baseDir . DIRECTORY_SEPARATOR . $mainFolder;
|
|
|
|
$pluginFolders = array_filter(scandir($pluginBasePath), function($item) use ($pluginBasePath) {
|
|
return $item !== '.' && $item !== '..' && is_dir($pluginBasePath . DIRECTORY_SEPARATOR . $item);
|
|
});
|
|
|
|
$rand_db = 0;
|
|
|
|
foreach ($pluginFolders as $pluginFolder) {
|
|
$rand_db = rand()+$rand_db;
|
|
|
|
$mainLocation = $pluginBasePath . DIRECTORY_SEPARATOR . $pluginFolder. DIRECTORY_SEPARATOR;
|
|
|
|
$mainFile = $pluginBasePath . DIRECTORY_SEPARATOR . $pluginFolder . DIRECTORY_SEPARATOR . $pluginFolder . '-class.php';
|
|
|
|
if (file_exists($mainFile)) {
|
|
$foundPlugins = true;
|
|
$pluginInfo = parsePluginHeader($mainFile);
|
|
|
|
$response_plugin = json_decode(getData($db_prefix.'plugins', 'WHERE plugin_slug="'.$pluginFolder.'" AND status="active"'), true);
|
|
|
|
$plugin_rand = rand();
|
|
|
|
$installed_count = $installed_count+1;
|
|
?>
|
|
<!-- Plugin Card 1 -->
|
|
<div class="col-md-6 col-lg-4 plugin-items <?php if($response_plugin['status'] == true){echo "active";}else{echo "inactive";}?>plugins">
|
|
<div class="plugin-card card h-100 border">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-start mb-3">
|
|
<div class="plugin-icon bg-primary bg-opacity-10 text-primary me-3">
|
|
<img src="https://<?php echo $_SERVER['HTTP_HOST']?>/pp-content/plugins/<?php echo $mainFolder.'/'.$pluginFolder?>/assets/icon.png" loading="lazy" alt="Image Description" width="auto" height="auto">
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h5 class="mb-1"><?php echo htmlspecialchars($pluginInfo['Plugin Name'] ?? '')?></h5>
|
|
<p class="text-primary small mb-2" onclick="location.href='<?php echo htmlspecialchars($pluginInfo['Author URI'] ?? '')?>'" style="cursor: pointer;">v<?php echo htmlspecialchars($pluginInfo['Version'] ?? '')?> by <?php echo htmlspecialchars($pluginInfo['Author'] ?? '')?></p>
|
|
<?php
|
|
if($response_plugin['status'] == true){
|
|
?>
|
|
<span class="badge bg-success status-badge me-1">Active</span>
|
|
<?php
|
|
}else{
|
|
?>
|
|
<span class="badge bg-danger status-badge me-1">Inactive</span>
|
|
<?php
|
|
}
|
|
?>
|
|
</div>
|
|
</div>
|
|
<p class="small text-muted mb-3"><?php echo htmlspecialchars($pluginInfo['Description'] ?? '')?></p>
|
|
|
|
<div class="plugin-actions d-flex justify-content-between border-top pt-3">
|
|
<button class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#pluginDetailsModal<?php echo $plugin_rand?>">
|
|
<i class="bi bi-info-circle me-1"></i> Details
|
|
</button>
|
|
|
|
<?php
|
|
if($response_plugin['status'] == true){
|
|
?>
|
|
<button class="btn btn-outline-danger btn-sm btn-deactivate<?php echo $plugin_rand?>" onclick="pluginhit('btn-deactivate<?php echo $plugin_rand?>', 'deactivate', '<?php echo $mainFolder?>', '<?php echo $pluginFolder?>', 'pluginDetailsModal<?php echo $plugin_rand?>')">
|
|
<i class="bi bi-power me-1"></i> Deactivate
|
|
</button>
|
|
<?php
|
|
}else{
|
|
?>
|
|
<button class="btn btn-outline-success btn-sm btn-activate<?php echo $plugin_rand?>" onclick="pluginhit('btn-activate<?php echo $plugin_rand?>', 'activate', '<?php echo $mainFolder?>', '<?php echo $pluginFolder?>', 'pluginDetailsModal<?php echo $plugin_rand?>')">
|
|
<i class="bi bi-power me-1"></i> Activate
|
|
</button>
|
|
<button class="btn btn-outline-dark btn-sm btn-delete<?php echo $plugin_rand?>" onclick="pluginhit('btn-delete<?php echo $plugin_rand?>', 'delete', '<?php echo $mainFolder?>', '<?php echo $pluginFolder?>', 'pluginDetailsModal<?php echo $plugin_rand?>')">
|
|
<i class="bi bi-trash3 me-1"></i> Delete
|
|
</button>
|
|
<?php
|
|
}
|
|
?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Plugin Details Modal -->
|
|
<div class="modal fade" id="pluginDetailsModal<?php echo $plugin_rand?>" tabindex="-1" aria-labelledby="pluginDetailsModalLabel<?php echo $plugin_rand?>" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="pluginDetailsModalLabel<?php echo $plugin_rand?>">Plugin Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<h4><?php echo htmlspecialchars($pluginInfo['Plugin Name'] ?? '')?></h4>
|
|
|
|
<?php
|
|
if (file_exists($mainLocation.'readme.txt')) {
|
|
$content = file_get_contents($mainLocation.'readme.txt');
|
|
$sections = parse_readme_sections($content);
|
|
|
|
$pluginHeader = parse_readme_header($content);
|
|
|
|
$tags = [];
|
|
if (!empty($pluginHeader['tags'])) {
|
|
$tags = array_map('trim', explode(',', $pluginHeader['tags']));
|
|
}
|
|
?>
|
|
<p class="text-muted"><?php echo htmlspecialchars($pluginInfo['Description'] ?? '')?></p>
|
|
|
|
<div class="d-flex flex-wrap gap-2 mb-4">
|
|
<?php foreach ($tags as $tag): ?>
|
|
<span class="badge bg-primary"><?php echo htmlspecialchars($tag); ?></span>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<h5>Description</h5>
|
|
<p><?php echo nl2br(htmlspecialchars($sections['description'] ?? 'N/A')); ?></p>
|
|
|
|
<h5 class="mt-4">Changelog</h5>
|
|
<?php
|
|
$changelogData = parse_readme_changelog($sections['changelog'] ?? '');
|
|
?>
|
|
<div class="accordion" id="changelogAccordion">
|
|
<?php
|
|
$index = 0;
|
|
foreach ($changelogData as $version => $items):
|
|
$isFirst = ($index === 0);
|
|
$collapseId = 'changelog' . $index;
|
|
?>
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button <?php echo $isFirst ? '' : 'collapsed'; ?>" type="button" data-bs-toggle="collapse" data-bs-target="#<?php echo $collapseId; ?>">
|
|
Version <?php echo htmlspecialchars($version); ?><?php echo $isFirst ? ' (Current)' : ''; ?>
|
|
</button>
|
|
</h2>
|
|
<div id="<?php echo $collapseId; ?>" class="accordion-collapse collapse <?php echo $isFirst ? 'show' : ''; ?>" data-bs-parent="#changelogAccordion">
|
|
<div class="accordion-body small">
|
|
<ul>
|
|
<?php foreach ($items as $item): ?>
|
|
<li><?php echo htmlspecialchars($item); ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
$index++;
|
|
endforeach;
|
|
?>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
<?php
|
|
if($response_plugin['status'] == true){
|
|
?>
|
|
<button type="button" class="btn btn-danger btn-deactivate<?php echo $plugin_rand?>-popup" onclick="pluginhit('btn-deactivate<?php echo $plugin_rand?>-popup', 'deactivate', '<?php echo $mainFolder?>', '<?php echo $pluginFolder?>', 'pluginDetailsModal<?php echo $plugin_rand?>')">Deactivate</button>
|
|
<?php
|
|
}else{
|
|
?>
|
|
<button type="button" class="btn btn-primary btn-activate<?php echo $plugin_rand?>-popup" onclick="pluginhit('btn-activate<?php echo $plugin_rand?>-popup', 'activate', '<?php echo $mainFolder?>', '<?php echo $pluginFolder?>', 'pluginDetailsModal<?php echo $plugin_rand?>')">Activate</button>
|
|
<?php
|
|
}
|
|
?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$foundPlugins) {
|
|
?>
|
|
<div class="col-md-12 col-lg-12">
|
|
<div class="alert alert-danger"><i class="bi bi-info-circle-fill me-2"></i> No plugins available</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<script>
|
|
function initialize(){
|
|
document.querySelector(".installed-count").innerHTML = "<?php echo $installed_count?>";
|
|
}
|
|
initialize();
|
|
|
|
function filterPlugins(type) {
|
|
currentFilter = type;
|
|
const search = document.getElementById('pluginSearch').value.toLowerCase();
|
|
|
|
document.querySelectorAll('.plugin-items').forEach(el => {
|
|
const name = el.querySelector('h5')?.textContent.toLowerCase() || '';
|
|
|
|
const matchesType = (type === "all") || el.classList.contains(type + "plugins");
|
|
const matchesSearch = name.includes(search);
|
|
|
|
if (matchesType && matchesSearch) {
|
|
el.style.display = 'block';
|
|
} else {
|
|
el.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
function pluginhit(btn, type, mainfolder, pluginfolder, model){
|
|
var btn_value = document.querySelector("."+btn).innerHTML;
|
|
|
|
$("."+btn).html('<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>');
|
|
$("."+btn).prop("disabled", true);
|
|
|
|
$.ajax
|
|
({
|
|
type: "POST",
|
|
url: "https://<?php echo $_SERVER['HTTP_HOST']?>/admin/plugin-manager",
|
|
data: { "action": "pp_plugin_manager", "type": type, "mainfolder": mainfolder, "pluginfolder": pluginfolder},
|
|
success: function (data) {
|
|
console.log(data);
|
|
$("."+btn).prop("disabled", false);
|
|
$("."+btn).html(btn_value);
|
|
|
|
$('#'+model).modal('hide');
|
|
|
|
var dedata = JSON.parse(data);
|
|
|
|
if(dedata.status == "false"){
|
|
document.querySelector(".response-action").innerHTML = '<div class="alert alert-danger" style="margin-top:10px;margin-bottom:10px"> <i class="fa fa-info-circle me-2"></i> '+dedata.message+'</div>';
|
|
}else{
|
|
$('#'+model).modal('hide');
|
|
|
|
load_content('Plugin','plugin-manager','nav-btn-plugin')
|
|
}
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
<?php
|
|
}
|
|
?>
|