Create accounts search API for searching local and remote accounts
This commit is contained in:
parent
423c5f4567
commit
1aa7682a91
|
@ -0,0 +1,142 @@
|
|||
<?php chdir('../../../../../../') ?>
|
||||
<?php require 'base.php' ?>
|
||||
<?php
|
||||
|
||||
ini_set('max_execution_time', 120);
|
||||
|
||||
if (count($_GET) < 2)
|
||||
apiresult(['error' => 'you must specify filter key-values for searching'], 400);
|
||||
|
||||
$pg = new PgDatabase();
|
||||
if (!$pg->is_ok())
|
||||
apiresult(['error' => 'Error on database'], 500);
|
||||
|
||||
$filtered_accounts = [];
|
||||
$filtered_accounts_index = [];
|
||||
|
||||
$sql_all = '
|
||||
SELECT
|
||||
a.id, a.username, a.domain, a.fields, a.note, a.avatar_file_name,
|
||||
a.avatar_storage_schema_version
|
||||
FROM accounts AS a
|
||||
{userLocalInnerJoin}
|
||||
WHERE
|
||||
{userFilter}
|
||||
{userLocalWhere1}
|
||||
{userLocalWhere2}
|
||||
a.suspended_at IS NULL
|
||||
ORDER BY a.updated_at DESC
|
||||
';
|
||||
|
||||
$user_filter = 'all';
|
||||
if (isset($_GET['user_filter'])) {
|
||||
$user_filter = strtolower(trim($_GET['user_filter']));
|
||||
if (!in_array($user_filter, ['all','local','remote']))
|
||||
$user_filter = 'all';
|
||||
}
|
||||
switch ($user_filter) {
|
||||
case 'all':
|
||||
$sql_all = str_replace('{userFilter}', '', $sql_all);
|
||||
$sql_all = str_replace('{userLocalInnerJoin}', '', $sql_all);
|
||||
$sql_all = str_replace('{userLocalWhere1}', '', $sql_all);
|
||||
$sql_all = str_replace('{userLocalWhere2}', '', $sql_all);
|
||||
break;
|
||||
case 'local':
|
||||
$sql_all = str_replace('{userFilter}', 'a.domain IS NULL AND', $sql_all);
|
||||
$sql_all = str_replace('{userLocalInnerJoin}', 'INNER JOIN users AS u ON u.account_id = a.id', $sql_all);
|
||||
$sql_all = str_replace('{userLocalWhere1}', 'u.disabled = false AND', $sql_all);
|
||||
$sql_all = str_replace('{userLocalWhere2}', 'u.approved = true AND', $sql_all);
|
||||
break;
|
||||
case 'remote':
|
||||
$sql_all = str_replace('{userFilter}', 'NOT a.domain IS NULL AND', $sql_all);
|
||||
$sql_all = str_replace('{userLocalInnerJoin}', '', $sql_all);
|
||||
$sql_all = str_replace('{userLocalWhere1}', '', $sql_all);
|
||||
$sql_all = str_replace('{userLocalWhere2}', '', $sql_all);
|
||||
break;
|
||||
}
|
||||
$all_accounts = $pg->fetch_all($sql_all);
|
||||
|
||||
if (isset($_GET['profile']) && trim($_GET['profile']) != '')
|
||||
{
|
||||
$q = strtolower(trim($_GET['profile']));
|
||||
if ($q === '<empty>') $q = '';
|
||||
$q = normalize_for_search($q);
|
||||
|
||||
$ai = -1;
|
||||
foreach ($all_accounts as $account)
|
||||
{
|
||||
$ai++;
|
||||
$a_note = trim($account['note']);
|
||||
$a_fields = json_decode($account['fields'], true);
|
||||
$fields = '';
|
||||
foreach ($a_fields as $field)
|
||||
$fields .= $field['name'].' '.$field['value'].' ';
|
||||
$a_note = trim($fields) . $a_note;
|
||||
|
||||
if ($q === '') {
|
||||
if ($a_note === '') {
|
||||
$filtered_accounts []= $account['id'];
|
||||
$filtered_accounts_index[$account['id']] = $ai;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($a_note === '')
|
||||
continue;
|
||||
$words = explode(' ', normalize_for_search($a_note));
|
||||
$matches = true;
|
||||
foreach (explode(' ', $q) as $qi) {
|
||||
$found = false;
|
||||
foreach ($words as $word) {
|
||||
if (str_starts_with($word, $qi)) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
$matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($matches) {
|
||||
$filtered_accounts []= $account['id'];
|
||||
$filtered_accounts_index[$account['id']] = $ai;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config = instance_config('mastodon');
|
||||
$filtered_accounts = array_unique($filtered_accounts);
|
||||
|
||||
$result_accounts = [];
|
||||
foreach ($filtered_accounts as $id) {
|
||||
$newacc = [];
|
||||
$account = $all_accounts[$filtered_accounts_index[$id]];
|
||||
$account['fields'] = json_decode($account['fields'], true);
|
||||
$account['avatar_storage_schema_version'] = intval($account['avatar_storage_schema_version']);
|
||||
$newacc['id'] = $id;
|
||||
$newacc['acct'] = $account['username'].'@'.($account['domain'] === null ? '' : $account['domain']);
|
||||
$newacc['acct'] = trim($newacc['acct'],'@');
|
||||
$newacc['note'] = $account['note'];
|
||||
$newacc['fields'] = $account['fields'];
|
||||
if ($account['avatar_storage_schema_version'] === 1) {
|
||||
$c = 0;
|
||||
$parts = [];
|
||||
$part = '';
|
||||
for ($i = 0; $i < strlen($id); $i++) {
|
||||
$part .= $id[$i];
|
||||
$c++;
|
||||
if ($c === 3) {
|
||||
$c = 0;
|
||||
$parts []= $part;
|
||||
$part = '';
|
||||
}
|
||||
}
|
||||
$newacc['avatar'] = $config['instance_url'].'/system';
|
||||
if ($account['domain'] !== null)
|
||||
$newacc['avatar'] .= '/cache';
|
||||
$newacc['avatar'] .= '/accounts/avatars/'.implode('/',$parts).'/original/'.$account['avatar_file_name'];
|
||||
}
|
||||
$result_accounts []= $newacc;
|
||||
}
|
||||
|
||||
apiresult($result_accounts);
|
149
base.php
149
base.php
|
@ -46,9 +46,12 @@ function instance_config($software, $instance=null) {
|
|||
if (!in_array($software, $GLOBALS['supported_ap_software']))
|
||||
return null;
|
||||
|
||||
if ($instance === null)
|
||||
if ($instance === null && isset($_GET['instance']))
|
||||
$instance = trim($_GET['instance']);
|
||||
|
||||
if ($instance === null)
|
||||
return apiresult(['error' => '<instance> GET argument must be provided'], 400);
|
||||
|
||||
if (isset($GLOBALS['_cache'][$software.$instance]))
|
||||
return $GLOBALS['_cache'][$software.$instance];
|
||||
|
||||
|
@ -126,5 +129,149 @@ function valid_mastodon_account_id($id) {
|
|||
return preg_match('/^\d+$/', strval($id));
|
||||
}
|
||||
|
||||
function remove_accents($string) {
|
||||
if ( !preg_match('/[\x80-\xff]/', $string) )
|
||||
return $string;
|
||||
|
||||
$chars = array(
|
||||
// Decompositions for Latin-1 Supplement
|
||||
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
|
||||
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
|
||||
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
|
||||
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
|
||||
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
|
||||
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
|
||||
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
|
||||
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
|
||||
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
|
||||
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
|
||||
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
|
||||
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
|
||||
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
|
||||
chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
|
||||
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
|
||||
chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
|
||||
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
|
||||
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
|
||||
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
|
||||
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
|
||||
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
|
||||
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
|
||||
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
|
||||
chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
|
||||
chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
|
||||
chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
|
||||
chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
|
||||
chr(195).chr(191) => 'y',
|
||||
// Decompositions for Latin Extended-A
|
||||
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
|
||||
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
|
||||
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
|
||||
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
|
||||
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
|
||||
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
|
||||
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
|
||||
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
|
||||
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
|
||||
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
|
||||
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
|
||||
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
|
||||
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
|
||||
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
|
||||
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
|
||||
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
|
||||
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
|
||||
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
|
||||
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
|
||||
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
|
||||
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
|
||||
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
|
||||
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
|
||||
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
|
||||
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
|
||||
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
|
||||
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
|
||||
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
|
||||
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
|
||||
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
|
||||
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
|
||||
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
|
||||
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
|
||||
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
|
||||
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
|
||||
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
|
||||
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
|
||||
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
|
||||
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
|
||||
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
|
||||
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
|
||||
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
|
||||
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
|
||||
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
|
||||
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
|
||||
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
|
||||
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
|
||||
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
|
||||
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
|
||||
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
|
||||
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
|
||||
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
|
||||
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
|
||||
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
|
||||
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
|
||||
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
|
||||
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
|
||||
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
|
||||
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
|
||||
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
|
||||
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
|
||||
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
|
||||
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
|
||||
chr(197).chr(190) => 'z', chr(197).chr(191) => 's'
|
||||
);
|
||||
|
||||
$string = strtr($string, $chars);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
if (!function_exists('str_starts_with')) {
|
||||
function str_starts_with($haystack, $needle) {
|
||||
return (string)$needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
function normalize_for_search($str) {
|
||||
if (trim($str) === '') return '';
|
||||
$str = preg_replace('/\s\s+/', ' ', trim($str));
|
||||
$words = explode(' ', $str);
|
||||
$newwords = [];
|
||||
foreach ($words as $word) {
|
||||
$word = remove_accents($word);
|
||||
$word = strtolower($word);
|
||||
$word = preg_replace('/[^a-z0-9]+/', '', $word);
|
||||
if (trim($word) === '')
|
||||
continue;
|
||||
$word = str_replace('4', 'a', $word);
|
||||
$word = str_replace('3', 'e', $word);
|
||||
$word = str_replace('1', 'i', $word);
|
||||
$word = str_replace('0', 'o', $word);
|
||||
$word = str_replace('5', 's', $word);
|
||||
$word = str_replace('8', 'b', $word);
|
||||
$nword = '';
|
||||
for ($i = 0; $i < strlen($word); $i++) {
|
||||
if ($i === 0) {
|
||||
$nword .= $word[0];
|
||||
continue;
|
||||
}
|
||||
if ($word[$i] !== $word[$i-1]) {
|
||||
$nword .= $word[$i];
|
||||
}
|
||||
}
|
||||
$newwords [] = $nword;
|
||||
}
|
||||
return trim(implode(' ', $newwords));
|
||||
}
|
||||
|
||||
// classes
|
||||
require 'classes/PgDatabase.php';
|
||||
|
|
|
@ -26,8 +26,12 @@ class PgDatabase {
|
|||
return $this->db !== null;
|
||||
}
|
||||
|
||||
public function escape($str) {
|
||||
return pg_escape_string($this->db, $str);
|
||||
}
|
||||
|
||||
public function query($sql) {
|
||||
$result = pg_query($sql);
|
||||
$result = pg_query($this->db, $sql);
|
||||
if (!$result) apiresult('The postgres query has failed: '.pg_last_error());
|
||||
return $result;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue