* finish task #6539,6537.

This commit is contained in:
wangyidong
2019-11-18 14:14:03 +08:00
parent eb1267ebe7
commit 6434e06342
12 changed files with 329 additions and 11 deletions
+1
View File
@@ -120,6 +120,7 @@ define('TABLE_LOG', '`' . $config->db->prefix . 'log`');
define('TABLE_SCORE', '`' . $config->db->prefix . 'score`');
define('TABLE_NOTIFY', '`' . $config->db->prefix . 'notify`');
define('TABLE_TRANSLATION', '`' . $config->db->prefix . 'translation`');
define('TABLE_DINGUSERID', '`' . $config->db->prefix . 'dinguserid`');
if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`');
$config->objectTables['product'] = TABLE_PRODUCT;
+6
View File
@@ -1 +1,7 @@
ALTER TABLE `zt_dept` CHANGE `order` `order` smallint(4) unsigned NOT NULL DEFAULT '0' AFTER `grade`;
CREATE TABLE `zt_dinguserid` (
`webhook` mediumint(8) unsigned NOT NULL,
`account` varchar(30) NOT NULL,
`userid` varchar(255) NOT NULL,
UNIQUE KEY `webhook_account_userid` (`webhook`,`account`,`userid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+114
View File
@@ -0,0 +1,114 @@
<?php
class dingapi
{
public $apiUrl = 'https://oapi.dingtalk.com/';
private $appKey;
private $appSecret;
private $token;
private $expires;
private $errors = array();
public function __construct($appKey, $appSecret, $agentId, $apiUrl = '')
{
$this->appKey = $appKey;
$this->appSecret = $appSecret;
$this->agentId = $agentId;
if($apiUrl) $this->apiUrl = rtrim($apiUrl, '/') . '/';
if(!$this->getToken()) return array('result' => 'fail', 'message' => $this->errors);
}
public function getToken()
{
if($this->token and (time() - $this->expires) >= 0) return $this->token;
$response = $this->queryAPI($this->apiUrl . "gettoken?appkey={$this->appKey}&appsecret={$this->appSecret}");
if($this->isError()) return false;
$this->token = $response->access_token;
$this->expires = time() + $response->expires_in;
return $this->token;
}
public function getAllUsers()
{
$depts = $this->getAllDepts();
if($this->isError()) return array('result' => 'fail', 'message' => $this->errors);
$users = array();
foreach($depts as $deptID => $deptName)
{
$response = $this->queryAPI($this->apiUrl . "user/simplelist?access_token={$this->token}&department_id={$deptID}");
if($this->isError()) return array('result' => 'fail', 'message' => $this->errors);
foreach($response->userlist as $user) $users[$user->name] = $user->userid;
}
return array('result' => 'success', 'data' => $users);
}
public function getAllDepts()
{
$response = $this->queryAPI($this->apiUrl . "department/list?access_token={$this->token}");
if($this->isError()) return false;
$deptPairs = array();
foreach($response->department as $dept) $deptPairs[$dept->id] = $dept->name;
return $deptPairs;
}
public function send($userList, $message)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($curl, CURLOPT_USERAGENT, 'Sae T OAuth2 v0.1');
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_setopt($curl, CURLOPT_HEADER, FALSE);
curl_setopt($curl, CURLOPT_URL, $this->apiUrl . 'topapi/message/corpconversation/asyncsend_v2?access_token=' . $this->token);
curl_setopt($curl, CURLINFO_HEADER_OUT, TRUE);
$postData = array();
$postData['agent_id'] = $this->agentId;
$postData['userid_list'] = $userList;
$postData['msg'] = $message;
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
$response = curl_exec($curl);
curl_close($curl);
$response = json_encode($response);
if(isset($response->errcode) and $response->errcode == 0) return array('result' => 'success');
$this->errors[$response->errcode] = "Errcode:{$response->errcode}, Errmsg:{$response->errmsg}";
return array('result' => 'fail', 'message' => $this->errors);
}
public function queryAPI($url)
{
$response = json_decode(file_get_contents($url));
if(isset($response->errcode) and $response->errcode == 0) return $response;
$this->errors[$response->errcode] = "Errcode:{$response->errcode}, Errmsg:{$response->errmsg}";
return false;
}
public function isError()
{
return !empty($this->errors);
}
public function getErrors()
{
$errors = $this->errors;
$this->errors = array();
return $errors;
}
}
+3 -3
View File
@@ -28,9 +28,9 @@ class commonModel extends model
$this->setUser();
$this->loadConfigFromDB();
$this->app->setTimezone();
if((strpos($this->config->global->version, 'pro') !== false && version_compare($this->config->global->version, 'pro2.3.beta', '>'))
|| (strpos($this->config->global->version, 'biz') !== false)
|| version_compare($this->config->global->version, '4.3.beta', '>'))
if((strpos($this->config->version, 'pro') !== false && version_compare($this->config->version, 'pro2.3.beta', '>'))
|| (strpos($this->config->version, 'biz') !== false)
|| version_compare($this->config->version, '4.3.beta', '>'))
{
$this->loadCustomFromDB();
}
+2
View File
@@ -201,6 +201,8 @@ class upgrade extends control
if(empty($extensions)) $this->locate(inlink('selectVersion'));
/* Check network. */
if(!extension_loaded('curl')) $this->locate(inlink('selectVersion'));
$curl = curl_init();
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
+67
View File
@@ -137,6 +137,73 @@ class webhook extends control
$this->display();
}
/**
* Bind dingtalk userid.
*
* @param int $id
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function bind($id, $recTotal = 0, $recPerPage = 50, $pageID = 1)
{
if($_POST)
{
$this->webhook->bind($id);
if(dao::isError()) die(js::error(dao::getError()));
die(js::reload('parent'));
}
$webhook = $this->webhook->getById($id);
if($webhook->type != 'dingapi')
{
echo js::alert($this->lang->webhook->note->bind);
die(js::locate($this->createLink('webhook', 'browse')));
}
$webhook->secret = json_decode($webhook->secret);
$this->app->loadClass('dingapi', true);
$dingapi = new dingapi($webhook->secret->appKey, $webhook->secret->appSecret, $webhook->secret->agentId);
$response = $dingapi->getAllUsers();
if($response['result'] == 'fail')
{
echo js::error($response->message);
die(js::locate($this->createLink('webhook', 'browse')));
}
$dingUsers = $response['data'];
$bindedPairs = $this->webhook->getBindUsers($id);
$useridPairs = array('' => '');
foreach($dingUsers as $name => $userid) $useridPairs[$userid] = $name;
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$users = $this->loadModel('user')->getByQuery($query = '', $pager);
$unbindUsers = array();
$bindedUsers = array();
foreach($users as $user)
{
if(isset($bindedPairs[$user->account])) $bindedUsers[$user->account] = $user;
if(!isset($bindedPairs[$user->account])) $unbindUsers[$user->account] = $user;
}
$users = $unbindUsers + $bindedUsers;
$this->view->title = $this->lang->webhook->bind;
$this->view->position[] = html::a($this->createLink('webhook', 'browse'), $this->lang->webhook->common);
$this->view->position[] = $this->lang->webhook->bind;
$this->view->dingUsers = $dingUsers;
$this->view->useridPairs = $useridPairs;
$this->view->users = $users;
$this->view->pager = $pager;
$this->view->bindedUsers = $bindedPairs;
$this->display();
}
/**
* Send data by async.
*
+10 -2
View File
@@ -4,6 +4,7 @@ $lang->webhook->list = 'Webhook列表';
$lang->webhook->api = '接口';
$lang->webhook->entry = '应用';
$lang->webhook->log = '日志';
$lang->webhook->bind = '绑定用户';
$lang->webhook->assigned = '指派给';
$lang->webhook->setting = '设置';
@@ -41,8 +42,14 @@ $lang->webhook->typeList['default'] = '其他';
$lang->webhook->sendTypeList['sync'] = '同步';
$lang->webhook->sendTypeList['async'] = '异步';
$lang->webhook->dingAppKey = '钉钉AppKey';
$lang->webhook->dingAppSecret = '钉钉AppSecret';
$lang->webhook->dingAgentId = '钉钉AgentId';
$lang->webhook->dingAppKey = '钉钉AppKey';
$lang->webhook->dingAppSecret = '钉钉AppSecret';
$lang->webhook->dingUserid = '钉钉Userid';
$lang->webhook->dingBindStatus = '钉钉绑定状态';
$lang->webhook->dingBindStatusList['0'] = '未绑定';
$lang->webhook->dingBindStatusList['1'] = '已绑定';
$lang->webhook->paramsList['objectType'] = '对象类型';
$lang->webhook->paramsList['objectID'] = '对象ID';
@@ -60,6 +67,7 @@ $lang->webhook->trimWords = '了';
$lang->webhook->note = new stdClass();
$lang->webhook->note->async = '异步需要打开计划任务';
$lang->webhook->note->bind = '只有钉钉工作通知类型才需要绑定用户。';
$lang->webhook->note->product = "此项为空时所有{$lang->productCommon}的动作都会触发钩子,否则只有关联{$lang->productCommon}的动作才会触发。";
$lang->webhook->note->project = "此项为空时所有{$lang->projectCommon}的动作都会触发钩子,否则只有关联{$lang->projectCommon}的动作才会触发。";
+51 -3
View File
@@ -113,6 +113,19 @@ class webhookModel extends model
return $this->dao->select('*')->from(TABLE_NOTIFY)->where('status')->eq('wait')->andWhere('objectType')->eq('webhook')->orderBy('id')->fetchAll('id');
}
/**
* Get bind users.
*
* @param int $webhookID
* @access public
* @return array
*/
public function getBindUsers($webhookID)
{
return $this->dao->select('*')->from(TABLE_DINGUSERID)->where('webhook')->eq($webhookID)
->fetchPairs('account', 'userid');
}
/**
* Create a webhook.
*
@@ -133,14 +146,20 @@ class webhookModel extends model
if($webhook->type == 'dingapi')
{
$webhook->secret = array();
$webhook->secret['agentId'] = $webhook->agentId;
$webhook->secret['appKey'] = $webhook->appKey;
$webhook->secret['appSecret'] = $webhook->appSecret;
if(empty($webhook->agentId)) dao::$errors['agentId'] = sprintf($this->lang->error->notempty, $this->lang->webhook->dingAgentId);
if(empty($webhook->appKey)) dao::$errors['appKey'] = sprintf($this->lang->error->notempty, $this->lang->webhook->dingAppKey);
if(empty($webhook->appSecret)) dao::$errors['appSecret'] = sprintf($this->lang->error->notempty, $this->lang->webhook->dingAppSecret);
if(dao::isError()) return false;
$webhook->secret = json_encode($webhook->secret);
$webhook->url = $this->config->webhook->dingapiUrl;
}
$this->dao->insert(TABLE_WEBHOOK)->data($webhook, 'appKey,appSecret')
$this->dao->insert(TABLE_WEBHOOK)->data($webhook, 'agentId,appKey,appSecret')
->batchCheck($this->config->webhook->create->requiredFields, 'notempty')
->autoCheck()
->exec();
@@ -170,13 +189,19 @@ class webhookModel extends model
if($webhook->type == 'dingapi')
{
$webhook->secret = array();
$webhook->secret['agentId'] = $webhook->agentId;
$webhook->secret['appKey'] = $webhook->appKey;
$webhook->secret['appSecret'] = $webhook->appSecret;
if(empty($webhook->agentId)) dao::$errors['agentId'] = sprintf($this->lang->error->notempty, $this->lang->webhook->dingAgentId);
if(empty($webhook->appKey)) dao::$errors['appKey'] = sprintf($this->lang->error->notempty, $this->lang->webhook->dingAppKey);
if(empty($webhook->appSecret)) dao::$errors['appSecret'] = sprintf($this->lang->error->notempty, $this->lang->webhook->dingAppSecret);
if(dao::isError()) return false;
$webhook->secret = json_encode($webhook->secret);
}
$this->dao->update(TABLE_WEBHOOK)->data($webhook, 'appKey,appSecret')
$this->dao->update(TABLE_WEBHOOK)->data($webhook, 'agentId,appKey,appSecret')
->batchCheck($this->config->webhook->edit->requiredFields, 'notempty')
->autoCheck()
->where('id')->eq($id)
@@ -184,6 +209,29 @@ class webhookModel extends model
return !dao::isError();
}
/**
* Bind ding userid.
*
* @param int $webhookID
* @access public
* @return bool
*/
public function bind($webhookID)
{
$data = fixer::input('post')->get();
foreach($data->userid as $account => $userid)
{
if(empty($userid)) continue;
$dingUser = new stdclass();
$dingUser->webhook = $webhookID;
$dingUser->account = $account;
$dingUser->userid = $userid;
$this->dao->replace(TABLE_DINGUSERID)->data($dingUser)->exec();
}
return !dao::isError();
}
/**
* Send data.
*
@@ -288,7 +336,7 @@ class webhookModel extends model
foreach(explode(',', $webhook->params) as $param) $data->$param = $action->$param;
}
return helper::jsonEncode($data);
return json_encode($data);
}
/**
+63
View File
@@ -0,0 +1,63 @@
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-content'>
<div class='center-block mw-800px'>
<div class='main-header'>
<h2><?php echo $lang->webhook->bind?></h2>
</div>
<form class='main-form' id='bindForm' target='hiddenwin' method='post' data-ride='table'>
<table id='bindList' class='table table-fixed table-bordered active-disabled'>
<thead>
<tr class='text-center'>
<th class='text-left'><?php echo $lang->user->account?></th>
<th class='w-200px text-left'><?php echo $lang->user->realname?></th>
<th class='w-200px'><?php echo $lang->webhook->dingUserid?></th>
<th class='w-100px'><?php echo $lang->webhook->dingBindStatus?></th>
</tr>
</thead>
<tbody>
<?php $inputVars = 0;?>
<?php foreach($users as $user):?>
<tr>
<td><?php echo $user->account;?></td>
<td><?php echo $user->realname;?></td>
<?php
$userid = '';
$bindStatus = 0;
if(isset($bindedUsers[$user->account]))
{
$userid = $bindedUsers[$user->account];
$bindStatus = 1;
}
elseif(isset($dingUsers[$user->realname]))
{
$userid = $dingUsers[$user->realname];
}
?>
<td><?php echo html::select("userid[{$user->account}]", $useridPairs, $userid, 'class="form-control"')?></td>
<td class='text-center'><?php echo zget($lang->webhook->dingBindStatusList, $bindStatus, '');?></td>
</tr>
<?php $inputVars += 1;?>
<?php endforeach;?>
</tbody>
</table>
<?php if($users):?>
<div class='table-footer'>
<div class='text'>
<?php echo html::submitButton($lang->save, '', 'btn btn-primary');?>
<?php echo html::a($this->createLink('webhook', 'browse'), $lang->goback, '', "class='btn'");?>
</div>
<?php $pager->show('right', 'pagerjs');?>
</div>
<?php endif;?>
</form>
</div>
</div>
<script>
<?php if(common::judgeSuhosinSetting($inputVars)):?>
$(function()
{
$('.table-footer').before("<div class='alert alert-info'><?php echo extension_loaded('suhosin') ? trim(sprintf($lang->suhosinInfo, $inputVars)) : trim(sprintf($lang->maxVarsInfo, $inputVars));?></div>")
})
<?php endif;?>
</script>
<?php include '../../common/view/footer.html.php';?>
+3 -2
View File
@@ -22,7 +22,7 @@
<th class='w-60px'><?php common::printOrderLink('type', $orderBy, $vars, $lang->webhook->type);?></th>
<th class='w-200px text-left'><?php common::printOrderLink('name', $orderBy, $vars, $lang->webhook->name);?></th>
<th><?php common::printOrderLink('url', $orderBy, $vars, $lang->webhook->url);?></th>
<th class='c-actions-3'><?php echo $lang->actions;?></th>
<th class='c-actions-4'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody>
@@ -32,8 +32,9 @@
<td class='text-center'><?php echo zget($lang->webhook->typeList, $webhook->type);?></td>
<td class='text' title='<?php echo $webhook->name;?>'><?php echo $webhook->name;?></td>
<td class='text' title='<?php echo $webhook->url;?>'><?php echo $webhook->url;?></td>
<td class='c-actions'>
<td class='c-actions text-right'>
<?php
if($webhook->type == 'dingapi') common::printIcon('webhook', 'bind', "webhookID=$id", '', 'list', 'link');
common::printIcon('webhook', 'log', "webhookID=$id", '', 'list', 'file-text');
common::printIcon('webhook', 'edit', "webhookID=$id", '', 'list');
if(common::hasPriv('webhook', 'delete'))
+4
View File
@@ -39,6 +39,10 @@
<th><?php echo $lang->webhook->secret;?></th>
<td><?php echo html::input('secret', '', "class='form-control'");?></td>
</tr>
<tr class='dingapiTR'>
<th><?php echo $lang->webhook->dingAgentId;?></th>
<td><?php echo html::input('agentId', '', "class='form-control'");?></td>
</tr>
<tr class='dingapiTR'>
<th><?php echo $lang->webhook->dingAppKey;?></th>
<td><?php echo html::input('appKey', '', "class='form-control'");?></td>
+5 -1
View File
@@ -22,7 +22,7 @@
<tr>
<th class='thWidth'><?php echo $lang->webhook->type;?></th>
<td><?php echo zget($lang->webhook->typeList, $webhook->type);?></td>
<td></td>
<td><?php echo html::hidden('type', $webhook->type);?></td>
</tr>
<tr>
<th><?php echo $lang->webhook->name;?></th>
@@ -42,6 +42,10 @@
<?php endif;?>
<?php if($webhook->type == 'dingapi'):?>
<?php $secret = json_decode($webhook->secret);?>
<tr class='dingapiTR'>
<th><?php echo $lang->webhook->dingAgentId;?></th>
<td><?php echo html::input('agentId', $secret->agentId, "class='form-control'");?></td>
</tr>
<tr class='dingapiTR'>
<th><?php echo $lang->webhook->dingAppKey;?></th>
<td><?php echo html::input('appKey', $secret->appKey, "class='form-control'");?></td>