1
0
Fork 0
mirror of https://github.com/Oreolek/ifhub.club.git synced 2024-06-26 11:40:48 +03:00

Центр администрирования плагинами (прототип)

This commit is contained in:
Alexey Kachayev 2009-12-29 22:15:25 +00:00
parent 8eca922c6a
commit ab0789fe82
10 changed files with 743 additions and 5 deletions

View file

@ -0,0 +1,109 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
* Класс обработки УРЛа вида /comments/
*
*/
class ActionPlugins extends Action {
/**
* Главное меню
*
* @var string
*/
protected $sMenuHeadItemSelect='plugins';
public function Init() {
if(!$this->User_IsAuthorization() or !$oUserCurrent=$this->User_GetUserCurrent() or !$oUserCurrent->isAdministrator()) {
return parent::EventNotFound();
}
$this->oUserCurrent=$oUserCurrent;
}
protected function RegisterEvent() {
$this->AddEventPreg('/^(page(\d+))?$/i','EventIndex');
}
/**********************************************************************************
************************ РЕАЛИЗАЦИЯ ЭКШЕНА ***************************************
**********************************************************************************
*/
protected function EventIndex() {
if($sPlugin=getRequest('plugin',null,'get') and $sAction=getRequest('action',null,'get')) {
return $this->SubmitManagePlugin($sPlugin,$sAction);
}
/**
* Передан ли номер страницы
*/
$iPage= preg_match("/^\d+$/i",$this->GetEventMatch(2)) ? $this->GetEventMatch(2) : 1;
/**
* Получаем список блогов
*/
$aPlugins=$this->Plugin_GetList();
/**
* Загружаем переменные в шаблон
*/
$this->Viewer_Assign("aPlugins",$aPlugins);
$this->Viewer_AddHtmlTitle($this->Lang_Get('plugins_administartion_title'));
/**
* Устанавливаем шаблон вывода
*/
$this->SetTemplateAction('index');
}
/**
* Активация\деактивация плагина
*
* @param string $sPlugin
* @param string $sAction
*/
protected function SubmitManagePlugin($sPlugin,$sAction) {
if(!in_array($sAction,array('activate','deactivate'))) {
$this->Message_AddErrorSingle($this->Lang_Get('plugins_unknown_action'),$this->Lang_Get('error'),true);
Router::Location(Router::GetPath('plugins'));
}
/**
* Активируем\деактивируем плагин
*/
if($bResult=$this->Plugin_Toggle($sPlugin,$sAction)) {
$this->Message_AddNotice($this->Lang_Get('plugins_action_ok'),$this->Lang_Get('attention'),true);
} else {
$this->Message_AddErrorSingle($this->Lang_Get('system_error'),$this->Lang_Get('error'),true);
}
/**
* Возвращаем на страницу управления плагинами
*/
Router::Location(Router::GetPath('plugins'));
}
/**
* Выполняется при завершении работы экшена
*
*/
public function EventShutdown() {
/**
* Загружаем в шаблон необходимые переменные
*/
$this->Viewer_Assign('sMenuHeadItemSelect',$this->sMenuHeadItemSelect);
}
}
?>

View file

@ -0,0 +1,207 @@
var lsProfilerClass = new Class({
Implements: Options,
options: {
img : {
path: DIR_STATIC_SKIN+'/images/',
openName: 'open.gif',
closeName: 'close.gif'
},
classes: {
visible: 'lsProfiler_visible',
hidden: 'lsProfiler_hidden',
openImg: 'lsProfiler_open',
closeImg: 'lsProfiler_close',
treeNode: 'lsProfiler_tree',
filterNode: 'lsProfiler_filter'
},
prefix: {
img: 'img_',
td: 'report_',
entry: 'entry_',
tree: 'tree_',
treeNode: 'tree_node_'
},
path: {
loadReport: aRouter['profiler']+'ajaxloadreport/',
loadEntries: aRouter['profiler']+'ajaxloadentriesbyfilter/'
}
},
initialize: function(options){
this.setOptions(options);
this.make();
},
make: function(expandClass){
var thisObj = this;
var aImgFolding=(!expandClass)?$$('img.folding'):$$('img.folding.'+expandClass);
aImgFolding.each(function(img, i){thisObj.makeImg(img);});
},
makeImg: function(img) {
var thisObj = this;
img.setStyles({
'cursor' : 'pointer',
'display' : 'inline'
});
img.removeClass(this.options.classes.closeImg);
img.addClass(this.options.classes.openImg);
img.removeEvents('click');
img.addEvent('click',function(){
thisObj.toggleNode(img);
});
},
toggleNode: function(img) {
if (img.hasClass(this.options.classes.closeImg)) {
this.collapseNode(img);
} else {
this.expandNode(img);
}
},
expandNode: function(img) {
var thisObj = this;
img.setProperties({'src': this.options.img.path + this.options.img.closeName});
img.removeClass(this.options.classes.openImg);
img.addClass(this.options.classes.closeImg);
if(img.hasClass(thisObj.options.classes.treeNode)) {
// Это элемент дерева - обрабатываем его соответствующим образом
ids = img.get('id').replace(this.options.prefix.tree,'').split('_');
reportId=ids[0];
var trReportId=this.options.prefix.treeNode+ids[0]+'_'+ids[1];
var trReport=$(trReportId);
var parentId=ids[1];
} else {
reportId=img.get('id').replace(this.options.prefix.img,this.options.prefix.td);
var trReport = $(reportId);
var parentId = 0;
var trReportId = 0;
}
if(trReport){
trReport.show();
} else {
thisObj.loadReport(img.getParent('tr'),reportId,parentId,trReportId);
}
},
loadReport: function(obj,reportId,parentId,namedId) {
var thisObj=this;
var trCurrent = obj;
JsHttpRequest.query(
'POST '+thisObj.options.path.loadReport,
{
reportId: reportId,
bTreeView: true,
parentId: parentId,
security_ls_key: LIVESTREET_SECURITY_KEY
},
function(result, errors) {
if (!result) {
msgErrorBox.alert('Error','Please try again later');
}
if (result.bStateError) {
msgErrorBox.alert(result.sMsgTitle,result.sMsg);
} else {
var trReport=new Element('tr', {'id':(!namedId)?reportId:namedId});
trReport.adopt(new Element('td',{
'colspan': 6,
'html' : result.sReportText
}));
trReport.inject(trCurrent,'after');
trReport.getElements('img').each(function(img, i){thisObj.makeImg(img);});
}
},
true
);
},
collapseNode: function(img) {
var thisObj = this;
img.setProperties({'src': this.options.img.path + this.options.img.openName});
img.removeClass(this.options.classes.closeImg);
img.addClass(this.options.classes.openImg);
if(img.hasClass(thisObj.options.classes.treeNode)) {
// Это элемент дерева - обрабатываем его соответствующим образом
trReport=img.getParent('tr').getNext('tr');
} else {
reportId=img.get('id').replace(this.options.prefix.img,this.options.prefix.td);
var trReport = $(reportId);
}
trReport.hide();
},
toggleEntriesByClass: function(reportId,name,link) {
var thisObj=this;
$$('a.profiler').removeClass('active');
$$('a.profiler.'+name).addClass('active');
//var trCurrent = link.getParent('tr').getPrevious('tr');
JsHttpRequest.query(
'POST '+thisObj.options.path.loadEntries+name+'/',
{
reportId: reportId,
security_ls_key: LIVESTREET_SECURITY_KEY
},
function(result, errors) {
if (!result) {
msgErrorBox.alert('Error','Please try again later');
}
if (result.bStateError) {
msgErrorBox.alert(result.sMsgTitle,result.sMsg);
} else {
var trReport = $(thisObj.options.prefix.td+reportId).empty();
trReport.adopt(new Element('td',{
'colspan': 5,
'html' : result.sReportText
}));
trReport.getElements('img').each(function(img, i){thisObj.makeImg(img);});
}
},
true
);
},
filterNode: function(obj) {
var thisObj = this;
var iTime=obj.get('value');
if(iTime!='' && parseFloat(iTime)){ thisObj.highlightFilterNode(iTime); }
},
highlightFilterNode: function(iTime) {
var thisObj = this;
$$('.time').each(function(el,i){
el.getParent('tr').removeClass(thisObj.options.classes.filterNode);
if(el.get('text')>iTime) {
el.getParent('tr').addClass(thisObj.options.classes.filterNode);
}
});
}
});
var lsProfiler;
window.addEvent('domready', function() {
lsProfiler = new lsProfilerClass({
img: {
path: DIR_STATIC_SKIN+'/images/'
},
classes: {
openImg: 'folding-open',
closeImg: 'folding',
filterNode: 'filter'
}
});
});

View file

@ -0,0 +1,207 @@
var lsProfilerClass = new Class({
Implements: Options,
options: {
img : {
path: DIR_STATIC_SKIN+'/images/',
openName: 'open.gif',
closeName: 'close.gif'
},
classes: {
visible: 'lsProfiler_visible',
hidden: 'lsProfiler_hidden',
openImg: 'lsProfiler_open',
closeImg: 'lsProfiler_close',
treeNode: 'lsProfiler_tree',
filterNode: 'lsProfiler_filter'
},
prefix: {
img: 'img_',
td: 'report_',
entry: 'entry_',
tree: 'tree_',
treeNode: 'tree_node_'
},
path: {
loadReport: aRouter['profiler']+'ajaxloadreport/',
loadEntries: aRouter['profiler']+'ajaxloadentriesbyfilter/'
}
},
initialize: function(options){
this.setOptions(options);
this.make();
},
make: function(expandClass){
var thisObj = this;
var aImgFolding=(!expandClass)?$$('img.folding'):$$('img.folding.'+expandClass);
aImgFolding.each(function(img, i){thisObj.makeImg(img);});
},
makeImg: function(img) {
var thisObj = this;
img.setStyles({
'cursor' : 'pointer',
'display' : 'inline'
});
img.removeClass(this.options.classes.closeImg);
img.addClass(this.options.classes.openImg);
img.removeEvents('click');
img.addEvent('click',function(){
thisObj.toggleNode(img);
});
},
toggleNode: function(img) {
if (img.hasClass(this.options.classes.closeImg)) {
this.collapseNode(img);
} else {
this.expandNode(img);
}
},
expandNode: function(img) {
var thisObj = this;
img.setProperties({'src': this.options.img.path + this.options.img.closeName});
img.removeClass(this.options.classes.openImg);
img.addClass(this.options.classes.closeImg);
if(img.hasClass(thisObj.options.classes.treeNode)) {
// Это элемент дерева - обрабатываем его соответствующим образом
ids = img.get('id').replace(this.options.prefix.tree,'').split('_');
reportId=ids[0];
var trReportId=this.options.prefix.treeNode+ids[0]+'_'+ids[1];
var trReport=$(trReportId);
var parentId=ids[1];
} else {
reportId=img.get('id').replace(this.options.prefix.img,this.options.prefix.td);
var trReport = $(reportId);
var parentId = 0;
var trReportId = 0;
}
if(trReport){
trReport.show();
} else {
thisObj.loadReport(img.getParent('tr'),reportId,parentId,trReportId);
}
},
loadReport: function(obj,reportId,parentId,namedId) {
var thisObj=this;
var trCurrent = obj;
JsHttpRequest.query(
'POST '+thisObj.options.path.loadReport,
{
reportId: reportId,
bTreeView: true,
parentId: parentId,
security_ls_key: LIVESTREET_SECURITY_KEY
},
function(result, errors) {
if (!result) {
msgErrorBox.alert('Error','Please try again later');
}
if (result.bStateError) {
msgErrorBox.alert(result.sMsgTitle,result.sMsg);
} else {
var trReport=new Element('tr', {'id':(!namedId)?reportId:namedId});
trReport.adopt(new Element('td',{
'colspan': 6,
'html' : result.sReportText
}));
trReport.inject(trCurrent,'after');
trReport.getElements('img').each(function(img, i){thisObj.makeImg(img);});
}
},
true
);
},
collapseNode: function(img) {
var thisObj = this;
img.setProperties({'src': this.options.img.path + this.options.img.openName});
img.removeClass(this.options.classes.closeImg);
img.addClass(this.options.classes.openImg);
if(img.hasClass(thisObj.options.classes.treeNode)) {
// Это элемент дерева - обрабатываем его соответствующим образом
trReport=img.getParent('tr').getNext('tr');
} else {
reportId=img.get('id').replace(this.options.prefix.img,this.options.prefix.td);
var trReport = $(reportId);
}
trReport.hide();
},
toggleEntriesByClass: function(reportId,name,link) {
var thisObj=this;
$$('a.profiler').removeClass('active');
$$('a.profiler.'+name).addClass('active');
//var trCurrent = link.getParent('tr').getPrevious('tr');
JsHttpRequest.query(
'POST '+thisObj.options.path.loadEntries+name+'/',
{
reportId: reportId,
security_ls_key: LIVESTREET_SECURITY_KEY
},
function(result, errors) {
if (!result) {
msgErrorBox.alert('Error','Please try again later');
}
if (result.bStateError) {
msgErrorBox.alert(result.sMsgTitle,result.sMsg);
} else {
var trReport = $(thisObj.options.prefix.td+reportId).empty();
trReport.adopt(new Element('td',{
'colspan': 5,
'html' : result.sReportText
}));
trReport.getElements('img').each(function(img, i){thisObj.makeImg(img);});
}
},
true
);
},
filterNode: function(obj) {
var thisObj = this;
var iTime=obj.get('value');
if(iTime!='' && parseFloat(iTime)){ thisObj.highlightFilterNode(iTime); }
},
highlightFilterNode: function(iTime) {
var thisObj = this;
$$('.time').each(function(el,i){
el.getParent('tr').removeClass(thisObj.options.classes.filterNode);
if(el.get('text')>iTime) {
el.getParent('tr').addClass(thisObj.options.classes.filterNode);
}
});
}
});
var lsProfiler;
window.addEvent('domready', function() {
lsProfiler = new lsProfilerClass({
img: {
path: DIR_STATIC_SKIN+'/images/'
},
classes: {
openImg: 'folding-open',
closeImg: 'folding',
filterNode: 'filter'
}
});
});

View file

@ -289,6 +289,7 @@ $config['router']['page']['link'] = 'ActionLink';
$config['router']['page']['question'] = 'ActionQuestion';
$config['router']['page']['blogs'] = 'ActionBlogs';
$config['router']['page']['search'] = 'ActionSearch';
$config['router']['page']['plugins'] = 'ActionPlugins';
// Глобальные настройки роутинга
$config['router']['config']['action_default'] = 'index';
$config['router']['config']['action_not_found'] = 'error';

View file

@ -274,8 +274,8 @@ class Engine extends Object {
protected function InitPlugins() {
if($aPluginList = @file(Config::Get('path.root.server').'/classes/plugins/plugins.dat')) {
foreach ($aPluginList as $sPluginName) {
$sDirHooks=Config::Get('path.root.server').'/classes/plugins/';
$sFile="{$sDirHooks}{$sPluginName}/classes/hooks/Plugin{$sPluginName}.class.php";
$sDirPlugins=Config::Get('path.root.server').'/classes/plugins/';
$sFile="{$sDirPlugins}{$sPluginName}/Plugin{$sPluginName}.class.php";
if(is_file($sFile)) {
require_once($sFile);

View file

@ -38,15 +38,15 @@ abstract class Plugin extends Object {
*
*/
public function Activate() {
return true;
}
/**
* Функция деактивации плагина
*
*/
public function Deativate() {
public function Deactivate() {
return true;
}
public function __call($sName,$aArgs) {

View file

@ -0,0 +1,157 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
* Модуль управления плагинами сообщений
*
*/
class LsPlugin extends Module {
/**
* Файл содержащий информацию об активированных плагинах
*
*/
const PLUGIN_ACTIVATION_FILE = 'plugins.dat';
/**
* Путь к директории с плагинами
*
* @var string
*/
protected $sPluginsDir;
/**
* Список плагинов
*
* @var unknown_type
*/
protected $aPluginsList=array();
/**
* Инициализация модуля
*
*/
public function Init() {
$this->sPluginsDir=Config::Get('path.root.server').'/classes/plugins/';
}
/**
* Получает список информации о всех плагинах, загруженных в plugin-директорию
*
* @return array
*/
public function GetList() {
$aList=array_map('basename',glob($this->sPluginsDir.'*',GLOB_ONLYDIR));
$aActivePlugins=$this->GetActivePlugins();
foreach($aList as $sPlugin) {
$this->aPluginsList[$sPlugin] = array(
'code' => $sPlugin,
'is_active' => in_array($sPlugin,$aActivePlugins),
'name' => '',
'author' => '',
);
}
return $this->aPluginsList;
}
public function Toggle($sPlugin,$sAction) {
$aPlugins=$this->GetList();
if(!isset($aPlugins[$sPlugin])) return null;
$sPluginName=ucfirst($sPlugin);
switch ($sAction) {
case 'activate':
case 'deactivate':
$sAction=ucfirst($sAction);
$sFile="{$this->sPluginsDir}{$sPlugin}/Plugin{$sPluginName}.class.php";
if(is_file($sFile)) {
require_once($sFile);
$sClassName="Plugin{$sPluginName}";
$oPlugin=new $sClassName;
$bResult=$oPlugin->$sAction();
}
if($bResult) {
/**
* Переопределяем список активированных пользователем плагинов
*/
$aActivePlugins=$this->GetActivePlugins();
if($sAction=='Activate') {
/**
* Вносим данные в файл об активации плагина
*/
$aActivePlugins[] = $sPlugin;
} else {
/**
* Вносим данные в файл о деактивации плагина
*/
$aIndex=array_keys($aActivePlugins,$sPlugin);
if(is_array($aIndex)) {
unset($aActivePlugins[array_shift($aIndex)]);
}
}
$this->SetActivePlugins($aActivePlugins);
}
return $bResult;
default:
return null;
}
}
/**
* Возвращает список активированных плагинов в системе
*
* @return array
*/
public function GetActivePlugins() {
/**
* Читаем данные из файла PLUGINS.DAT
*/
$aPlugins=@file($this->sPluginsDir.self::PLUGIN_ACTIVATION_FILE);
if(is_array($aPlugins)) $aPlugins = array_unique($aPlugins);
return $aPlugins;
}
/**
* Записывает список активных плагинов в файл PLUGINS.DAT
*
* @param array|string $aPlugins
*/
public function SetActivePlugins($aPlugins) {
if(!is_array($aPlugins)) $aPlugins = array($aPlugins);
$aPlugins=array_unique($aPlugins);
/**
* Записываем данные в файл PLUGINS.DAT
*/
file_put_contents($this->sPluginsDir.self::PLUGIN_ACTIVATION_FILE, implode(PHP_EOL,$aPlugins));
}
/**
* При завершении работы модуля
*
*/
public function Shutdown() {
}
}
?>

View file

@ -765,6 +765,19 @@ return array(
'notify_subject_reminder_code' => 'Восстановление пароля',
'notify_subject_reminder_password' => 'Новый пароль',
/**
* Страница администрирования плагинов
*/
'plugins_administartion_title' => 'Администрирование плагинов',
'plugins_plugin_name' => 'Название',
'plugins_plugin_author' => 'Автор',
'plugins_plugin_version' => 'Версия',
'plugins_plugin_action' => '',
'plugins_plugin_activate' => 'Активировать',
'plugins_plugin_deactivate' => 'Деактивировать',
'plugins_unknown_action' => 'Указано неизвестное действие',
'plugins_action_ok' => 'Успешно выполнено',
'system_error_event_args' => 'Некорректное число аргументов при добавлении евента',
'system_error_event_method' => 'Добавляемый метод евента не найден',

View file

@ -0,0 +1,27 @@
{include file='header.tpl' showWhiteBack=true}
<div class="page people top-blogs plugins">
<table>
<thead>
<tr>
<td class="name">{$aLang.plugins_plugin_name}</td>
<td class="version">{$aLang.plugins_plugin_version}</td>
<td class="author">{$aLang.plugins_plugin_author}</td>
<td class="action">{$aLang.plugins_plugin_action}</td>
</tr>
</thead>
<tbody>
{foreach from=$aPlugins item=aPlugin}
<tr>
<td class="name"><a class="title">{$aPlugin.code}</a></td>
<td class="version">-</td>
<td class="author">-</td>
<td class="{if $aPlugin.is_active}deactivate{else}activate{/if}"><strong>{if $aPlugin.is_active}<a href="{router page='plugins'}?plugin={$aPlugin.code}&action=deactivate">{$aLang.plugins_plugin_deactivate}</a>{else}<a href="{router page='plugins'}?plugin={$aPlugin.code}&action=activate">{$aLang.plugins_plugin_activate}</a>{/if}</strong></td>
</tr>
{/foreach}
</tbody>
</table>
</div>
{include file='footer.tpl'}

View file

@ -715,6 +715,23 @@ a.profiler.active { background: #70aae0; color: white; }
.profiler-table .filter { background:#ffb5c5; }
.profiler-highlight { margin: 0px; text-align:center; background: #dafad8; border: 1px solid #83c460; padding: 2px; color: #4bb23b; }
/* Plugins
-------------------------------------------------------------------*/
.plugins thead td.name { padding-left: 32px; }
.plugins thead td.version, .plugins td.version { width: 40px; text-align: center; }
.plugins thead td.author, .plugins td.author, .plugins tbody td.action { width: 120px; text-align: center; }
.plugins tbody td { padding: 7px 0; }
.plugins td.name { overflow: hidden; }
.plugins td.name img { float: left; padding-bottom: 5px; margin-right: 7px; width: 24px; height: 24px; }
.plugins td.name a.title { font-size: 18px; line-height: 14px; }
.plugins td.name a.author { line-height: 18px; background:url(../images/icons.gif) no-repeat scroll 0 -108px; padding-left: 12px; color: #999; font-weight: bold; }
.plugins td.activate, .plugins td.deactivate { width: 120px; text-align: right; }
.plugins td.activate strong { background: #6ec80d; color: #fff; padding: 5px 10px; }
.plugins td.deactivate strong { background: #ff68cf; color: #fff; padding: 5px 10px; }
.plugins td.activate strong a, .plugins td.deactivate strong a { color: white; text-decoration: none; }
/* Footer
-------------------------------------------------------------------*/
#footer .right { float: right; }