You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

729 lines
29 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
// +----------------------------------------------------------------------
// | OneThink [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.onethink.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace Admin\Controller;
use Think\Controller;
use Admin\Model\AuthRuleModel;
use Admin\Model\AuthGroupModel;
/**
* 后台首页控制器
* @author 麦当苗儿 <zuojiazi@vip.qq.com>
*/
class AdminController extends Controller {
/**
* 后台控制器初始化
*/
protected function _initialize(){
// 获取当前用户ID
if(defined('UID')) return ;
define('UID',is_login());
if( !UID ){// 还没登录 跳转到登录页面
$this->redirect('Admin/Public/login');
}
/* 读取数据库中的配置 */
$config = S('DB_CONFIG_DATA');
if(!$config){
$config = api('Config/lists');
S('DB_CONFIG_DATA',$config);
}
C($config); //添加配置
// 是否是超级管理员
define('IS_ROOT', is_administrator());
if(!IS_ROOT && C('ADMIN_ALLOW_IP')){
// 检查IP地址访问
if(!in_array(get_client_ip(),explode(',',C('ADMIN_ALLOW_IP')))){
$this->error('403:禁止访问');
}
}
// 检测系统权限
if(!IS_ROOT){
$access = $this->accessControl();
if ( false === $access ) {
$this->error('403:禁止访问');
}elseif(null === $access ){
$access_controller = ['Ajax', 'Finance', 'FinancePromote', 'PayChannel'];
if(!in_array(CONTROLLER_NAME, $access_controller)){//ajax放行
//检测访问权限
$rule = strtolower(MODULE_NAME.'/'.CONTROLLER_NAME.'/'.ACTION_NAME);
if($rule == "admin/statistics/overview"||$rule == "admin/spend/lists"){//第一级菜单单独判断
$this->strictCheckRule($rule);
}elseif ( !$this->checkRule($rule,array('in','1,2')) ){
if ($rule == "admin/index/index"){
//如果首页没有访问权限 自动检测有访问权限的页面然后跳转过去
$qx = M("Auth_group")->table("__AUTH_GROUP__ as ag")
->join("__AUTH_GROUP_ACCESS__ as aga on(ag.id=aga.group_id and aga.uid=".UID.")",'right')
->where("ag.status=1")->limit(1)->select();
if (empty($qx)){
//如果没有任何权限 直接登出
D('Member')->logout();
session('[destroy]');
}
$where['id'] = substr($qx[0]['rules'],0,strpos($qx[0]['rules'], ','));
//$where['id'] = substr($qx[0]['rules'],0,1)
$dz = M("auth_rule")->field('name')->where($where)->find();
$red = substr($dz['name'],6);
redirect(U("$red"));
}else{
$this->error('未授权访问!');
}
}else{
// 检测分类及内容有关的各项动态权限
$dynamic = $this->checkDynamic();
if( false === $dynamic ){
$this->error('未授权访问!');
}
}
}
}
}
$map2['uid'] = session("user_auth.uid");
$res = M('auth_group_access','sys_')->field('uid,group_id')->where($map2)->find();
$map1['id'] = $res['group_id'];
$res1 = M('auth_group','sys_')->field('title')->where($map1)->find();
$this->assign('res',$res);
$this->assign('res1',$res1);
$this->assign('__MENU__', $this->getMenus());
$this->assign('is_admin', IS_ROOT);
}
/**
* 大菜单存在两个及以上的同方法菜单,需要进行多级菜单确认
*/
protected function strictCheckRule($rule)
{
$flag =false;
$id= 0;
$ruleres = M("auth_rule")->field("id,type")->where("name LIKE '%{$rule}%'")->select();
foreach ($ruleres as $k => $v) {
$checkRule = $this->checkRule($rule,array('eq',$v['type']));
if(!$checkRule){
$flag = true;
}else{
$id = $v['id'];
}
}
if($flag){ //不通过
//获取菜单下的
$rulearr = explode("/",$rule);
$where = array();
$where['pid'] = 0;
$where['hide'] = 0;
$where['url'] = array("like",$rulearr[1]."/".$rulearr[2]);
$second_id = M('Menu')->where($where)->field('id')->find()['id'];
$where2['pid'] = $second_id;
$where2['hide'] = 0;
$menu = M('Menu')->where($where2)->field('id,url')->order('sort asc')->select();
foreach ($menu as $k=>$v) {
$to_check_url = $v['url'];
if( stripos($to_check_url,MODULE_NAME)!==0 ){
$trule = MODULE_NAME.'/'.$to_check_url;
}else{
$trule = $to_check_url;
}
if($this->checkRule($trule, AuthRuleModel::RULE_URL,null)){
redirect(U("$trule"));
}
}
}
# code...
}
/**
* 权限检测
* @param string $rule 检测的规则
* @param string $mode check模式
* @return boolean
* @author 朱亚杰 <xcoolcc@gmail.com>
*/
final protected function checkRule($rule, $type=AuthRuleModel::RULE_URL, $mode='url'){
static $Auth = null;
if (!$Auth) {
$Auth = new \Think\Auth();
}
if(!$Auth->check($rule,UID,$type,$mode)){
return false;
}
return true;
}
/**
* 检测是否是需要动态判断的权限
* @return boolean|null
* 返回true则表示当前访问有权限
* 返回false则表示当前访问无权限
* 返回null则表示权限不明
*
* @author 朱亚杰 <xcoolcc@gmail.com>
*/
protected function checkDynamic(){}
/**
* action访问控制,在 **登录成功** 后执行的第一项权限检测任务
*
* @return boolean|null 返回值必须使用 `===` 进行判断
*
* 返回 **false**, 不允许任何人访问(超管除外)
* 返回 **true**, 允许任何管理员访问,无需执行节点权限检测
* 返回 **null**, 需要继续执行节点权限检测决定是否允许访问
* @author 朱亚杰 <xcoolcc@gmail.com>
*/
final protected function accessControl(){
$allow = C('ALLOW_VISIT');
$deny = C('DENY_VISIT');
$check = strtolower(CONTROLLER_NAME.'/'.ACTION_NAME);
if ( !empty($deny) && in_array_case($check,$deny) ) {
return false;//非超管禁止访问deny中的方法
}
if ( !empty($allow) && in_array_case($check,$allow) ) {
return true;
}
return null;//需要检测节点权限
}
//驳回条目
public function reject ($model , $where = array() , $msg = array( 'success'=>'状态恢复成功!', 'error'=>'状态恢复失败!'),$fields='status'){
$data = array($fields => 2,'dispose_id'=>UID,'dispose_time'=>time());
$this->editRow( $model , $data, $where, $msg);
}
/**
* 对数据表中的单行或多行记录执行修改 GET参数id为数字或逗号分隔的数字
*
* @param string $model 模型名称,供M函数使用的参数
* @param array $data 修改的数据
* @param array $where 查询时的where()方法的参数
* @param array $msg 执行正确和错误的消息 array('success'=>'','error'=>'', 'url'=>'','ajax'=>false)
* url为跳转页面,ajax是否ajax方式(数字则为倒数计时秒数)
*
* @author 朱亚杰 <zhuyajie@topthink.net>
*/
final protected function editRow ( $model ,$data, $where , $msg ){
$id = array_unique((array)I('id',0));
$id = is_array($id) ? implode(',',$id) : $id;
//如存在id字段则加入该条件
$fields = D($model)->getDbFields();
if(in_array('id',$fields) && !empty($id)){
$where = array_merge( array('id' => array('in', $id )) ,(array)$where );
}
$msg = array_merge( array( 'success'=>'操作成功!', 'error'=>'操作失败!', 'url'=>'' ,'ajax'=>IS_AJAX) , (array)$msg );
if( D($model)->where($where)->save($data)!==false ) {
$this->success($msg['success'],$msg['url'],$msg['ajax']);
}else{
$this->error($msg['error'],$msg['url'],$msg['ajax']);
}
}
/**
* 禁用条目
* @param string $model 模型名称,供D函数使用的参数
* @param array $where 查询时的 where()方法的参数
* @param array $msg 执行正确和错误的消息,可以设置四个元素 array('success'=>'','error'=>'', 'url'=>'','ajax'=>false)
* url为跳转页面,ajax是否ajax方式(数字则为倒数计时秒数)
*
* @author 朱亚杰 <zhuyajie@topthink.net>
*/
protected function forbid ( $model , $where = array() , $msg = array( 'success'=>'状态禁用成功!', 'error'=>'状态禁用失败!'),$fields='status'){
$data = array($fields => 0);
$this->editRow( $model , $data, $where, $msg);
}
/**
* 恢复条目
* @param string $model 模型名称,供D函数使用的参数
* @param array $where 查询时的where()方法的参数
* @param array $msg 执行正确和错误的消息 array('success'=>'','error'=>'', 'url'=>'','ajax'=>false)
* url为跳转页面,ajax是否ajax方式(数字则为倒数计时秒数)
*
* @author 朱亚杰 <zhuyajie@topthink.net>
*/
protected function resume ( $model , $where = array() , $msg = array( 'success'=>'状态恢复成功!', 'error'=>'状态恢复失败!'),$fields='status'){
$data = array($fields => 1,'dispose_id'=>UID,'dispose_time'=>time());
$this->editRow( $model , $data, $where, $msg);
}
/**
* 还原条目
* @param string $model 模型名称,供D函数使用的参数
* @param array $where 查询时的where()方法的参数
* @param array $msg 执行正确和错误的消息 array('success'=>'','error'=>'', 'url'=>'','ajax'=>false)
* url为跳转页面,ajax是否ajax方式(数字则为倒数计时秒数)
* @author huajie <banhuajie@163.com>
*/
protected function restore ( $model , $where = array() , $msg = array( 'success'=>'状态还原成功!', 'error'=>'状态还原失败!'),$fields='status'){
$data = array($fields => 1);
$where = array_merge(array('status' => -1),$where);
$this->editRow( $model , $data, $where, $msg);
}
/**
* 条目假删除
* @param string $model 模型名称,供D函数使用的参数
* @param array $where 查询时的where()方法的参数
* @param array $msg 执行正确和错误的消息 array('success'=>'','error'=>'', 'url'=>'','ajax'=>false)
* url为跳转页面,ajax是否ajax方式(数字则为倒数计时秒数)
*
* @author 朱亚杰 <zhuyajie@topthink.net>
*/
protected function delete ( $model , $where = array() , $msg = array( 'success'=>'删除成功!', 'error'=>'删除失败!')) {
$data['status'] = -1;
$this->editRow( $model , $data, $where, $msg);
}
/**
* 设置一条或者多条数据的状态
*/
public function setStatus($Model=CONTROLLER_NAME){
$ids = I('request.ids');
$status = I('request.status');
if(empty($ids)){
$this->error('请选择要操作的数据');
}
$map['id'] = array('in',$ids);
switch ($status){
case -1 :
$this->delete($Model, $map, array('success'=>'删除成功','error'=>'删除失败'));
break;
case 0 :
$this->forbid($Model, $map, array('success'=>'禁用成功','error'=>'禁用失败'));
break;
case 1 :
$this->resume($Model, $map, array('success'=>'启用成功','error'=>'启用失败'));
break;
default :
$this->error('参数错误');
break;
}
}
/**
* 获取控制器菜单数组,二级菜单元素位于一级菜单的'_child'元素中
* @author 朱亚杰 <xcoolcc@gmail.com>
*/
final public function getMenus($controller=CONTROLLER_NAME){
//$menus = session('ADMIN_MENU_LIST.'.$controller);
if(empty($menus)){
// 获取主菜单
$where['pid'] = 0;
$where['hide'] = 0;
if(!C('DEVELOP_MODE')){ // 是否开发者模式
$where['is_dev'] = 0;
}
$menus['main'] = M('Menu')->where($where)->order('sort asc')->field('id,title,url')->select();
$menus['child'] = array(); //设置子节点
foreach ($menus['main'] as $key => $item) {
// 判断主菜单权限
if ( !IS_ROOT && !$this->checkRule(strtolower(MODULE_NAME.'/'.$item['url']),AuthRuleModel::RULE_MAIN,null) ) {
unset($menus['main'][$key]);
continue;//继续循环
}
if(strtolower(CONTROLLER_NAME.'/'.ACTION_NAME) == strtolower($item['url']) ){
$menus['main'][$key]['class']='current';
}
}
// 查找当前子菜单
$pid = M('Menu')->where("pid !=0 AND url like '%{$controller}/".ACTION_NAME."%'")->getField('pid');
if($pid){
// 查找当前主菜单
$nav = M('Menu')->find($pid);
// if($nav['pid']){
// $nav = M('Menu')->find($nav['pid']);
// }
while ($nav['pid'] != 0) {
$nav = M('Menu')->find($nav['pid']);
}
//var_dump($nav);exit;
foreach ($menus['main'] as $key => $item) {
// 获取当前主菜单的子菜单项
if($item['id'] == $nav['id']){
$menus['main'][$key]['class']='current';
//生成child树
$groups = M('Menu')->where(array('group'=>array('neq',''),'pid' =>$item['id']))->order('sort asc')->distinct(true)->getField("group",true);
//获取二级分类的合法url
$where = array();
$where['pid'] = $item['id'];
$where['hide'] = 0;
if(!C('DEVELOP_MODE')){ // 是否开发者模式
$where['is_dev'] = 0;
}
$second_urls = M('Menu')->where($where)->getField('id,url');
if(!IS_ROOT){
// 检测菜单权限
$to_check_urls = array();
foreach ($second_urls as $key=>$to_check_url) {
if( stripos($to_check_url,MODULE_NAME)!==0 ){
$rule = MODULE_NAME.'/'.$to_check_url;
}else{
$rule = $to_check_url;
}
if($this->checkRule($rule, AuthRuleModel::RULE_URL,null))
$to_check_urls[] = $to_check_url;
}
}
// 按照分组生成子菜单树
foreach ($groups as $g) {
$map = array('group'=>$g);
if(isset($to_check_urls)){
if(empty($to_check_urls)){
// 没有任何权限
continue;
}else{
$map['url'] = array('in', $to_check_urls);
}
}
$map['pid'] = $item['id'];
$map['hide'] = 0;
if(!C('DEVELOP_MODE')){ // 是否开发者模式
$map['is_dev'] = 0;
}
$menuList = M('Menu')->where($map)->field('id,pid,title,url,tip')->order('sort asc')->select();
$menus['child'][$g] = list_to_tree($menuList, 'id', 'pid', 'operater', $item['id']);
}
}
}
}
session('ADMIN_MENU_LIST.'.$controller,$menus);
}
return $menus;
}
/**
* 返回后台节点数据
* @param boolean $tree 是否返回多维数组结构(生成菜单时用到),为false返回一维数组(生成权限节点时用到)
* @retrun array
*
* 注意,返回的主菜单节点数组中有'controller'元素,以供区分子节点和主节点
*
* @author 朱亚杰 <xcoolcc@gmail.com>
*/
final protected function returnNodes($tree = true){
static $tree_nodes = array();
if ( $tree && !empty($tree_nodes[(int)$tree]) ) {
return $tree_nodes[$tree];
}
if((int)$tree){
$list = M('Menu')->field('id,pid,title,url,tip,hide')->where('hide = 0')->order('sort asc')->select();
foreach ($list as $key => $value) {
if( stripos($value['url'],MODULE_NAME)!==0 ){
$list[$key]['url'] = MODULE_NAME.'/'.$value['url'];
}
}
$nodes = list_to_tree($list,$pk='id',$pid='pid',$child='operator',$root=0);
foreach ($nodes as $key => $value) {
if(!empty($value['operator'])){
$nodes[$key]['child'] = $value['operator'];
unset($nodes[$key]['operator']);
}
}
}else{
$nodes = M('Menu')->field('title,url,tip,pid')->order('sort asc')->select();
foreach ($nodes as $key => $value) {
if( stripos($value['url'],MODULE_NAME)!==0 ){
$nodes[$key]['url'] = MODULE_NAME.'/'.$value['url'];
}
}
}
$tree_nodes[(int)$tree] = $nodes;
return $nodes;
}
/**
* 通用分页列表数据集获取方法
*
* 可以通过url参数传递where条件,例如: index.html?name=asdfasdfasdfddds
* 可以通过url空值排序字段和方式,例如: index.html?_field=id&_order=asc
* 可以通过url参数r指定每页数据条数,例如: index.html?r=5
*
* @param sting|Model $model 模型名或模型实例
* @param array $where where查询条件(优先级: $where>$_REQUEST>模型设定)
* @param array|string $order 排序条件,传入null时使用sql默认排序或模型属性(优先级最高);
* 请求参数中如果指定了_order和_field则据此排序(优先级第二);
* 否则使用$order参数(如果$order参数,且模型也没有设定过order,则取主键降序);
*
* @param boolean $field 单表模型用不到该参数,要用在多表join时为field()方法指定参数
* @author 朱亚杰 <xcoolcc@gmail.com>
*
* @return array|false
* 返回数据集
*/
protected function lists ($model,$where=array(),$order='',$field=true){
$options = array();
$REQUEST = (array)I('request.');
if(is_string($model)){
$model = M($model);
}
$OPT = new \ReflectionProperty($model,'options');
$OPT->setAccessible(true);
$pk = $model->getPk();
if($order===null){
//order置空
}else if ( isset($REQUEST['_order']) && isset($REQUEST['_field']) && in_array(strtolower($REQUEST['_order']),array('desc','asc')) ) {
$options['order'] = '`'.$REQUEST['_field'].'` '.$REQUEST['_order'];
}elseif( $order==='' && empty($options['order']) && !empty($pk) ){
$options['order'] = $pk.' desc';
}elseif($order){
$options['order'] = $order;
}
unset($REQUEST['_order'],$REQUEST['_field']);
if(empty($where)){
$where = array('status'=>array('egt',0));
}
if( !empty($where)){
$options['where'] = $where;
}
$options = array_merge( (array)$OPT->getValue($model), $options );
$total = $model->where($options['where'])->count();
if(isset($_REQUEST['row'])) {$listRows = $_REQUEST['row'];}else{$listRows = 10;}
$page = set_pagination($total,$listRows);
if($page) {$this->assign('_page', $page);}
$this->assign('_total',$total);
$options['limit'] = (I('get.p',1)-1)*$listRows.','.$listRows;
$model->setProperty('options',$options);
return $model->field($field)->select();
}
/**
* 处理文档列表显示
* @param array $list 列表数据
* @param integer $model_id 模型id
*/
protected function parseDocumentList($list,$model_id=null){
$model_id = $model_id ? $model_id : 1;
$attrList = get_model_attribute($model_id,false,'id,name,type,extra');
// 对列表数据进行显示处理
if(is_array($list)){
foreach ($list as $k=>$data){
foreach($data as $key=>$val){
if(isset($attrList[$key])){
$extra = $attrList[$key]['extra'];
$type = $attrList[$key]['type'];
if('select'== $type || 'checkbox' == $type || 'radio' == $type || 'bool' == $type) {
// 枚举/多选/单选/布尔型
$options = parse_field_attr($extra);
if($options && array_key_exists($val,$options)) {
$data[$key] = $options[$val];
}
}elseif('date'==$type){ // 日期型
$data[$key] = date('Y-m-d',$val);
}elseif('datetime' == $type){ // 时间型
$data[$key] = date('Y-m-d H:i:s',$val);
}
}
}
$data['model_id'] = $model_id;
$list[$k] = $data;
}
}
return $list;
}
function set_color_style($value) {
$result = M('config')->where('id=13')->setField('value',$value);
if($result) {
S('DB_CONFIG_DATA',null);
action_log('update_config','config',$data['id'],UID);
$this->success("更新成功", Cookie('__forward__'));
} else {
$this->error("主题设置失败!");
}
}
public function addShortcutIcon() {
$Kuaijieicon = M('Kuaijieicon');
$result = $Kuaijieicon->where(['url'=>$_REQUEST['url']])->find();
if ($result) {
if ($result['status'] == 0) {
$data = array('status'=>1,'id'=>$result['id']);
$id = $Kuaijieicon->save($data);
if($id){
//记录行为
action_log('Kuaijie/edit', 'Kuaijieicon', $result['id'], UID);
$this->success('添加成功');
} else {
$this->error('添加失败');
}
} else {
$this->error('已添加过常用设置');
}
} else {
$data = array('title'=>$_REQUEST['title'],'status'=>1,'url'=>$_REQUEST['url'],'value'=>0);
$id = $Kuaijieicon->add($data);
if($id){
//记录行为
action_log('Kuaijie/add', 'Kuaijieicon', $id, UID);
$this->success('添加成功');
} else {
$this->error('添加失败');
}
}
}
public function delShortcutIcon($id=0) {
if (!is_numeric($id) || $id<1) {$this->error('参数错误');}
$Kuaijieicon = M('Kuaijieicon');
$data = array('status'=>0,'id'=>$id);
$res = $Kuaijieicon->save($data);
if($res){
//记录行为
action_log('Kuaijie/del', 'Kuaijieicon', $id, UID);
$this->success('删除成功');
} else {
$this->error('删除失败');
}
}
/**
* 验证列表的展示或者统计权限
* @param [type] $type 0:"_list_check",1:"_count_check"
* @return void
*/
public function checkListOrCountAuthRestMap(&$map,$checkarr = false,$countfield = "rule_count_check"){
//验证count
if(IS_ROOT){
$this->assign($countfield,true);
}else{
$countRule = strtolower(MODULE_NAME.'/'.CONTROLLER_NAME.'/'.ACTION_NAME."_count_check");
$this->assign($countfield,$this->checkRule($countRule,array('in','1,2')));
//验证list
$listrule = strtolower(MODULE_NAME.'/'.CONTROLLER_NAME.'/'.ACTION_NAME."_list_check");
$listflag = $this->checkRule($listrule,array('in','1,2'));
if(!$listflag && $checkarr){
foreach ($checkarr as $v) {
if(isset($map[$v])){
//如果有模糊查询改精准查询
if($map[$v][0] == "like"){
$map[$v] = trim($map[$v][1],"%");
}
$listflag = true;
};
}
}
if(!$listflag){
$map["_string"] = "1=0";
}
}
}
/**
* 操作日志
* array(
* op_type=>操作类型 1:编辑 2:删除 3:下载,
* op_name=>操作说明 例如:编辑,删除,锁定等,
* url=>需要跳转的url 添加去列表加唯一id编辑直接去编辑页面下载可以重新下载(新开页面),
* key=>关键词:用户的账号,游戏的名词等
* )
*/
public function addOperationLog($options)
{
$checkarr = ['op_type','url','key'];
foreach ($checkarr as $v) {
if(!array_key_exists($v,$options)){
$this->error('操作日志添加,参数错误');
}
}
if(!array_key_exists("op_name",$options)){
$options['op_name'] = ($options['op_type'] == 1 ? "编辑" : ($options['op_type'] == 2 ? "删除" :"下载"));
}
//获取ip
$addarray = [
"op_ip"=>$_SERVER['REMOTE_ADDR'],
'admin_id'=>$_SESSION['onethink_admin']['user_auth']['uid'],
'admin_account'=>$_SESSION['onethink_admin']['user_auth']['username'],
'op_type'=>$options['op_type'],
'op_name'=>$options['op_name'],
'url'=>$options['url'],
'key'=>$options['key'],
'create_time'=>time()
];
//获取菜单名词
$act = CONTROLLER_NAME.'/'.ACTION_NAME;
$arr = [];
$menuname = D("menu")->field('title,pid,group')->where("url = '{$act}'")->find();
if($menuname){
$arr[] = $menuname['title'];
if(!empty($menuname['group'])){
$arr[] = $menuname['group'];
}
$this-> getLink($menuname['pid'],$arr);
}
$addarray['menu'] = implode ( "-",array_reverse($arr));
M("OperationLog","tab_")->add($addarray);
}
public function getLink($pid=0,&$arr){
if($pid == 0){
return;
}
$menuname = D("menu")->field('title,pid,group')->where("id = '{$pid}'")->find();
if($menuname){
$arr[] = $menuname['title'];
if(!empty($menuname['group'])){
$arr[] = $menuname['group'];
}
if($menuname['pid'] != 0){
$this->getLink($menuname['pid'], $arr);
}
}
}
}