instances.php 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  1. <?php
  2. if (!defined('N')) {
  3. echo('You probably meant <a href="'.preg_replace('/\.php$/','',$_SERVER['REQUEST_URI']).'">this</a>.');
  4. exit(0);
  5. }
  6. $bt=hrtime(true);
  7. $btu=time();
  8. $dlanguc=strtoupper($dlang);
  9. require '../lib/n2es.php';
  10. require '../lib/grace.php';
  11. require '../lib/fnum.php';
  12. require '../lib/realfloat.php';// had to resort to this because php 7.3 has a bug: when echoing a float, it uses the decimal separator of the locale that was set with setlocale, making a mess when the separator is different than "." with stuff expecting it to be "."
  13. use function mysqli_real_escape_string as myesc;
  14. /*$dlang='fr';
  15. $dtzbl=['ca'=>'Europe/Madrid','en'=>'Europe/London','es'=>'Europe/Madrid','fr'=>'Europe/Paris','it'=>'Europe/Rome'];
  16. date_default_timezone_set($dtzbl[$dlang]);*/
  17. $lc=localeconv();
  18. $lcc=['decimal_point'=>'.', 'thousands_sep'=>''];
  19. $debug='';
  20. debug(date('r',$btu).N,$debug);
  21. debug('REQUEST_URI: '.$_SERVER['REQUEST_URI'].N,$debug);
  22. debug('$_GET: '.print_r($_GET,true),$debug);
  23. debug('LOCALE: '.setlocale(LC_ALL,0).N,$debug);
  24. // an instance is displayed as "New" if its age, relative to the InsertTS field, is less or equal than this (currently 30 days)
  25. $oldline=30*24*60*60;// todo: do it relative to firstseen?
  26. if (array_key_exists('id',$_GET) && preg_match('/^\d+$/',$_GET['id'])===1) {
  27. $_GET['id']+=0;
  28. $single=true;
  29. } else {
  30. $single=false;
  31. }
  32. //$getc=count($_GET);
  33. //forcing $getc to 1 to never display the explanation of search engine workings
  34. $getc=1;
  35. $minudef=10;
  36. $minumax=4294967295;
  37. $maxudef=30000;
  38. $maxumax=4294967295;
  39. $minaudef=5;
  40. $minaumax=4294967295;
  41. $mincdef='';
  42. $mincmax=16777215;
  43. $link=mysqli_connect($conf['db_host'],$conf['db_user_name'],$conf['db_user_password'],$conf['db_name'],$conf['db_port'],$conf['db_socket']) or muorimeglio(_('Couldn’t connect to database: ').mysqli_connect_error().' ['.mysqli_connect_errno().']',false);
  44. mysqli_set_charset($link,'utf8mb4');
  45. // if and when ... put it soon after </nav> below
  46. /*<div id="popupback">
  47. <div id="fseenhelp" class="dhelp">
  48. <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  49. </div>
  50. </div>*/
  51. echo('</nav>
  52. <div class="scrwide">
  53. <div class="scrwidein">
  54. <section class="sectcontm">'.N);
  55. if ($single) {
  56. setfedipact();
  57. echo('<p><a href="'.$conf['instpath'].'/instances/'.$dlang.'">↑ '._('Go to search').'</a></p>'.N);
  58. $p=1;
  59. $que='SELECT * FROM Instances WHERE ID='.$_GET['id'];
  60. } else {
  61. echo('<img src="'.$prepath.'imgs/helpbutti.svg" id="helpbutt" class="helpb" onmouseover="swhelpi(true);" onmouseout="swhelpi(false);" onclick="swhelp();">
  62. <h3>'._('Instances').'</h3>
  63. <script language="JavaScript">
  64. let helpsh='.(($getc==0) ? 'false' : 'true').';
  65. function swhelp() {
  66. let hdiv=document.getElementById("help");
  67. let himg=document.getElementById("helpbutt");
  68. if (helpsh) {
  69. hdiv.style.height="0px";
  70. himg.title="'._('Show introduction').'";
  71. himg.src="'.$prepath.'imgs/helpbutti.svg";
  72. helpsh=false;
  73. } else {
  74. hdiv.style.height=hdiv.scrollHeight+"px";
  75. himg.title="'._('Hide introduction').'";
  76. himg.src="'.$prepath.'imgs/helpbutta.svg";
  77. helpsh=true;
  78. }
  79. }
  80. function swhelpi(over) {
  81. let himg=document.getElementById("helpbutt"), ia;
  82. if (helpsh) {
  83. (over) ? ia="i" : ia="a";
  84. } else {
  85. (over) ? ia="a" : ia="i";
  86. }
  87. himg.src="'.$prepath.'imgs/helpbutt"+ia+".svg";
  88. }
  89. let msshow=true;
  90. function swmetastats() {
  91. let msdiv=document.getElementById("metastats");
  92. let msswitch=document.getElementById("msswitch");
  93. if (msshow) {
  94. msdiv.style.height="0px";
  95. msswitch.innerText="'._('Show Threads moderation statistics').'";
  96. msshow=false;
  97. } else {
  98. msdiv.style.height=msdiv.scrollHeight+"px";
  99. msswitch.innerText="'._('Hide Threads moderation statistics').'";
  100. msshow=true;
  101. }
  102. }
  103. function swp(pn) {
  104. document.curvf.p.value=pn;
  105. document.curvf.submit();
  106. }
  107. function sethid(obj) {
  108. if (document.getElementById("cb"+obj).checked)
  109. document.getElementById(obj).value=1;
  110. else
  111. document.getElementById(obj).value=0;
  112. }
  113. function updord() {
  114. let langsel=document.getElementById("lang"), ordsel=document.getElementById("ord");
  115. if (langswri.indexOf(langsel.value)>=0)
  116. ordsel.options[0].innerHTML="'._('Random, recommended first').'";
  117. else
  118. ordsel.options[0].innerHTML="'._('Random').'";
  119. }
  120. function nocrit() {
  121. document.getElementById("lang").selectedIndex=0;
  122. document.getElementById("desc").value="";
  123. document.getElementById("minu").value="";
  124. document.getElementById("maxu").value="";
  125. document.getElementById("minau").value="";
  126. document.getElementById("minc").value="";
  127. document.getElementById("cbexnox").checked=false;
  128. sethid("exnox");
  129. document.getElementById("cbexregc").checked=false;
  130. sethid("exregc");
  131. document.getElementById("cbinths").checked=false;
  132. sethid("inths");
  133. document.getElementById("cbexappr").checked=false;
  134. sethid("exappr");
  135. document.getElementById("cbexlcko").checked=false;
  136. sethid("exlcko");
  137. updord();
  138. }
  139. function presub() {
  140. document.getElementById("p").value=1;
  141. if (document.getElementById("ord").value=="noxd") {
  142. document.getElementById("cbexnox").checked=false;
  143. document.getElementById("exnox").value=0;
  144. }
  145. }
  146. </script>
  147. '.N);
  148. setfedipact();
  149. $que='SELECT COUNT(ID) AS Unknown, SUM(UserCount) AS UnknownU FROM Instances WHERE IsMastodon=1 AND Visible=1 AND LastOkCheckTS>='.$graceline.' AND Threads IS NULL';
  150. if ($cfedipact>0)
  151. $que.=' AND URI NOT IN ('.$fedipact['uris_me'].')';
  152. $res=tquery($que,__LINE__);
  153. $row=mysqli_fetch_assoc($res);
  154. $metastats['unknown']=['instances'=>$row['Unknown'], 'users'=>nulltozero($row['UnknownU'])];
  155. $que='SELECT COUNT(ID) AS Suspending, SUM(UserCount) AS SuspendingU FROM Instances WHERE IsMastodon=1 AND Visible=1 AND LastOkCheckTS>='.$graceline;
  156. if ($cfedipact>0)
  157. $que.=' AND (Threads="suspended" OR URI IN ('.$fedipact['uris_me'].'))';
  158. else
  159. $que.=' AND Threads="suspended"';
  160. $res=tquery($que,__LINE__);
  161. $row=mysqli_fetch_assoc($res);
  162. $metastats['suspending']=['instances'=>$row['Suspending'], 'users'=>nulltozero($row['SuspendingU'])];
  163. $res=tquery('SELECT COUNT(ID) AS Silencing, SUM(UserCount) AS SilencingU FROM Instances WHERE IsMastodon=1 AND Visible=1 AND LastOkCheckTS>='.$graceline.' AND Threads="limited"',__LINE__);
  164. $row=mysqli_fetch_assoc($res);
  165. $metastats['limiting']=['instances'=>$row['Silencing'], 'users'=>nulltozero($row['SilencingU'])];
  166. $metastats['fpsuspending']['instances']=0;
  167. $metastats['fpsuspending']['users']=0;
  168. $metastats['fpliars']['instances']=0;
  169. $metastats['fpliars']['users']=0;
  170. $metastats['fpliars']['instlist']=[];
  171. if ($cfedipact>0) {
  172. $res=tquery('SELECT COUNT(ID) AS FPSuspending, SUM(UserCount) AS FPSuspendingU FROM Instances WHERE IsMastodon=1 AND Visible=1 AND LastOkCheckTS>='.$graceline.' AND URI IN ('.$fedipact['uris_me'].')',__LINE__);
  173. $row=mysqli_fetch_assoc($res);
  174. $metastats['fpsuspending']['instances']=$row['FPSuspending'];
  175. $metastats['fpsuspending']['users']=nulltozero($row['FPSuspendingU']);
  176. $res=tquery('SELECT URI, UserCount FROM Instances WHERE IsMastodon=1 AND Visible=1 AND LastOkCheckTS>='.$graceline.' AND Threads="accessible" AND URI IN ('.$fedipact['uris_me'].')',__LINE__);
  177. while ($row=mysqli_fetch_assoc($res)) {
  178. $metastats['fpliars']['instances']++;
  179. $metastats['fpliars']['users']+=nulltozero($row['UserCount']);
  180. $metastats['fpliars']['instlist'][]=['uri'=>$row['URI'], 'usercount'=>nulltozero($row['UserCount'])];
  181. }
  182. }
  183. $res=tquery('SELECT COUNT(ID) AS tinsts, SUM(UserCount) AS tusers, SUM(StatusCount) AS tstatuses, SUM(ActiveUsersMonth) AS tactusers FROM Instances WHERE IsMastodon=1 AND Visible=1 AND LastOkCheckTS>='.$graceline,__LINE__);
  184. $row=mysqli_fetch_assoc($res);
  185. echo(_('<div id="help" class="hiddiv"><p class="intro">This search engine for Mastodon instances is based on a database that gets updated by our crawler every night (CET). Instances are considered new for 30 days after they are discovered. When an instance has not responded to our daily checks for more than 30 days it’s no longer considered in statistics and searches and it gets checked only on the first of the month, rather than daily, until it possibly responds again. “Random ordering” of results varies daily.</p><p class="intro">Default search criteria reflect our fondness for a decentralized and egalitarian Fediverse and our attempt to exclude instances accepting fascist, racist, sexist, ableist or sovereignist contents by marking them as noxious.</p></div>').N);
  186. printf(_('<p class="intro">We currently count <span class="statd">%s</span> Mastodon instances, with <span class="statd">%s</span> users (<span class="statd">%s</span> active during last month) and <span class="statd">%s</span> published statuses. [<a id="msswitch" onclick="swmetastats();" style="cursor:pointer;">Show Threads moderation statistics</a>]</p>'), fnum($row['tinsts'],$lc), fnum($row['tusers'],$lc), fnum($row['tactusers'],$lc), fnum($row['tstatuses'],$lc));
  187. //echo '<pre>'.print_r($metastats,true).'</pre>'.N;
  188. $metastats['known']=[
  189. 'instances'=>$row['tinsts']-$metastats['unknown']['instances'],
  190. 'users'=>$row['tusers']-$metastats['unknown']['users']
  191. ];
  192. $metastats['known']['iperc']=$metastats['known']['instances']*100/$row['tinsts'];
  193. $metastats['known']['uperc']=$metastats['known']['users']*100/$row['tusers'];
  194. $metastats['unknown']['iperc']=$metastats['unknown']['instances']*100/$row['tinsts'];
  195. $metastats['unknown']['uperc']=$metastats['unknown']['users']*100/$row['tusers'];
  196. $metastats['suspending']['iperc']=$metastats['suspending']['instances']*100/$row['tinsts'];
  197. $metastats['suspending']['uperc']=$metastats['suspending']['users']*100/$row['tusers'];
  198. $metastats['suspending']['kiperc']=$metastats['suspending']['instances']*100/$metastats['known']['instances'];
  199. $metastats['suspending']['kuperc']=$metastats['suspending']['users']*100/$metastats['known']['users'];
  200. $metastats['fpsuspending']['iperc']=$metastats['fpsuspending']['instances']*100/$row['tinsts'];
  201. $metastats['fpsuspending']['uperc']=$metastats['fpsuspending']['users']*100/$row['tusers'];
  202. $metastats['fpsuspending']['kiperc']=$metastats['fpsuspending']['instances']*100/$metastats['known']['instances'];
  203. $metastats['fpsuspending']['kuperc']=$metastats['fpsuspending']['users']*100/$metastats['known']['users'];
  204. $metastats['limiting']['iperc']=$metastats['limiting']['instances']*100/$row['tinsts'];
  205. $metastats['limiting']['uperc']=$metastats['limiting']['users']*100/$row['tusers'];
  206. $metastats['limiting']['kiperc']=$metastats['limiting']['instances']*100/$metastats['known']['instances'];
  207. $metastats['limiting']['kuperc']=$metastats['limiting']['users']*100/$metastats['known']['users'];
  208. $metastats['notsusporlim']=[
  209. 'instances'=>$row['tinsts']-$metastats['unknown']['instances']-$metastats['suspending']['instances']-$metastats['limiting']['instances'],
  210. 'users'=>$row['tusers']-$metastats['unknown']['users']-$metastats['suspending']['users']-$metastats['limiting']['users']
  211. ];
  212. $metastats['notsusporlim']['iperc']=$metastats['notsusporlim']['instances']*100/$row['tinsts'];
  213. $metastats['notsusporlim']['uperc']=$metastats['notsusporlim']['users']*100/$row['tusers'];
  214. $metastats['notsusporlim']['kiperc']=$metastats['notsusporlim']['instances']*100/$metastats['known']['instances'];
  215. $metastats['notsusporlim']['kuperc']=$metastats['notsusporlim']['users']*100/$metastats['known']['users'];
  216. $metastats['susporlim']=[
  217. 'instances'=>$metastats['suspending']['instances']+$metastats['limiting']['instances'],
  218. 'users'=>$metastats['suspending']['users']+$metastats['limiting']['users']
  219. ];
  220. $metastats['susporlim']['iperc']=$metastats['susporlim']['instances']*100/$row['tinsts'];
  221. $metastats['susporlim']['uperc']=$metastats['susporlim']['users']*100/$row['tusers'];
  222. $metastats['susporlim']['kiperc']=$metastats['susporlim']['instances']*100/$metastats['known']['instances'];
  223. $metastats['susporlim']['kuperc']=$metastats['susporlim']['users']*100/$metastats['known']['users'];
  224. debug('fpsuspending: instances: '.$metastats['fpsuspending']['instances'].'; users: '.$metastats['fpsuspending']['users'].N.'fpliars: instances: '.$metastats['fpliars']['instances'].'; users: '.$metastats['fpliars']['users'].N,$debug);
  225. if (count($metastats['fpliars']['instlist'])>0)
  226. foreach ($metastats['fpliars']['instlist'] as $val)
  227. debug($val['uri'].': '.$val['usercount'].N,$debug);
  228. echo
  229. '<div id="metastats" class="hiddiv">
  230. <script language="JavaScript">swmetastats();</script>
  231. <h3>'._('Threads moderation statistics').'</h3>
  232. <p class="introe">'._('These statistics on the status of Threads moderation are based on the list of moderated instances, which is accessible only on some instances, and on the list of instances adhering to the <a href="https://fedipact.online/" target="_blank">Anti-Meta Fedi Pact</a>.').'</p>
  233. <h4>'._('Statistics on the state of Threads moderation<br>related to instances on which it is known').'</h4>
  234. <table class="ms_leg_tab"><tr><td class="ms_suspending"></td><td class="ms_leg_td">&nbsp;'._('Instances suspending Threads').': '.fnum($metastats['suspending']['instances'],$lc).' / '.fnum($metastats['known']['instances'],$lc).' ('.fnum($metastats['suspending']['kiperc'],$lc,2).'%)</td></tr></table>
  235. <table class="ms_leg_tab"><tr><td class="ms_limiting"></td><td class="ms_leg_td">&nbsp;'._('Instances limiting Threads').': '.fnum($metastats['limiting']['instances'],$lc).' / '.fnum($metastats['known']['instances'],$lc).' ('.fnum($metastats['limiting']['kiperc'],$lc,2).'%)</td></tr></table>
  236. <table class="ms_leg_tab"><tr><td class="ms_suspending"></td><td class="ms_leg_td">+</td><td class="ms_limiting"></td><td class="ms_leg_td">&nbsp;'._('Instances suspending or limiting Threads').': '.fnum($metastats['suspending']['instances']+$metastats['limiting']['instances'],$lc).' / '.fnum($metastats['known']['instances'],$lc).' ('.fnum($metastats['suspending']['kiperc']+$metastats['limiting']['kiperc'],$lc,2).'%)</td></tr></table>
  237. <table class="ms_leg_tab"><tr><td class="ms_notsusporlim"></td><td class="ms_leg_td">&nbsp;'._('Instances not suspending or limiting Threads').': '.fnum($metastats['notsusporlim']['instances'],$lc).' / '.fnum($metastats['known']['instances'],$lc).' ('.fnum($metastats['notsusporlim']['kiperc'],$lc,2).'%)</td></tr></table>
  238. <table class="ms_cont"><tr><td class="ms_suspending" style="width:'.rf($metastats['suspending']['kiperc']).'%"></td><td class="ms_limiting" style="width:'.rf($metastats['limiting']['kiperc']).'%"></td><td class="ms_notsusporlim" style="width:'.rf($metastats['notsusporlim']['kiperc']).'%"></td></tr></table>
  239. <div class="sep"></div>
  240. <table class="ms_leg_tab"><tr><td class="ms_suspending_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances suspending Threads').': '.fnum($metastats['suspending']['users'],$lc).' / '.fnum($metastats['known']['users'],$lc).' ('.fnum($metastats['suspending']['kuperc'],$lc,2).'%)</td></tr></table>
  241. <table class="ms_leg_tab"><tr><td class="ms_limiting_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances limiting Threads').': '.fnum($metastats['limiting']['users'],$lc).' / '.fnum($metastats['known']['users'],$lc).' ('.fnum($metastats['limiting']['kuperc'],$lc,2).'%)</td></tr></table>
  242. <table class="ms_leg_tab"><tr><td class="ms_suspending_u"></td><td class="ms_leg_td">+</td><td class="ms_limiting_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances suspending or limiting Threads').': '.fnum($metastats['suspending']['users']+$metastats['limiting']['users'],$lc).' / '.fnum($metastats['known']['instances'],$lc).' ('.fnum($metastats['suspending']['kuperc']+$metastats['limiting']['kuperc'],$lc,2).'%)</td></tr></table>
  243. <table class="ms_leg_tab"><tr><td class="ms_notsusporlim_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances not suspending or limiting Threads').': '.fnum($metastats['notsusporlim']['users'],$lc).' / '.fnum($metastats['known']['users'],$lc).' ('.fnum($metastats['notsusporlim']['kuperc'],$lc,2).'%)</td></tr></table>
  244. <table class="ms_cont"><tr><td class="ms_suspending_u" style="width:'.rf($metastats['suspending']['kuperc']).'%"></td><td class="ms_limiting_u" style="width:'.rf($metastats['limiting']['kuperc']).'%"></td><td class="ms_notsusporlim_u" style="width:'.rf($metastats['notsusporlim']['kuperc']).'%"></td></tr></table>
  245. <h4>'._('Statistics on the state of Threads<br>moderation related to all instances.').'</h4>
  246. <table class="ms_leg_tab"><tr><td class="ms_suspending"></td><td class="ms_leg_td">&nbsp;'._('Instances suspending Threads').': '.fnum($metastats['suspending']['instances'],$lc).' / '.fnum($row['tinsts'],$lc).' ('.fnum($metastats['suspending']['iperc'],$lc,2).'%)</td></tr></table>
  247. <table class="ms_leg_tab"><tr><td class="ms_limiting"></td><td class="ms_leg_td">&nbsp;'._('Instances limiting Threads').': '.fnum($metastats['limiting']['instances'],$lc).' / '.fnum($row['tinsts'],$lc).' ('.fnum($metastats['limiting']['iperc'],$lc,2).'%)</td></tr></table>
  248. <table class="ms_leg_tab"><tr><td class="ms_suspending"></td><td class="ms_leg_td">+</td><td class="ms_limiting"></td><td class="ms_leg_td">&nbsp;'._('Instances suspending or limiting Threads').': '.fnum($metastats['suspending']['instances']+$metastats['limiting']['instances'],$lc).' / '.fnum($row['tinsts'],$lc).' ('.fnum($metastats['suspending']['iperc']+$metastats['limiting']['iperc'],$lc,2).'%)</td></tr></table>
  249. <table class="ms_leg_tab"><tr><td class="ms_notsusporlim"></td><td class="ms_leg_td">&nbsp;'._('Instances not suspending or limiting Threads').': '.fnum($metastats['notsusporlim']['instances'],$lc).' / '.fnum($row['tinsts'],$lc).' ('.fnum($metastats['notsusporlim']['iperc'],$lc,2).'%)</td></tr></table>
  250. <table class="ms_leg_tab"><tr><td class="ms_unknown"></td><td class="ms_leg_td">&nbsp;'._('Instances whose Threads moderation status is unknown').': '.fnum($metastats['unknown']['instances'],$lc).' / '.fnum($row['tinsts'],$lc).' ('.fnum($metastats['unknown']['iperc'],$lc,2).'%)</td></tr></table>
  251. <table class="ms_cont"><tr><td class="ms_suspending" style="width:'.rf($metastats['suspending']['iperc']).'%"></td><td class="ms_limiting" style="width:'.rf($metastats['limiting']['iperc']).'%"></td><td class="ms_notsusporlim" style="width:'.rf($metastats['notsusporlim']['iperc']).'%"></td><td class="ms_unknown" style="width:'.$metastats['unknown']['iperc'].'%"></td></tr></table>
  252. <div class="sep"></div>
  253. <table class="ms_leg_tab"><tr><td class="ms_suspending_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances suspending Threads').': '.fnum($metastats['suspending']['users'],$lc).' / '.fnum($row['tusers'],$lc).' ('.fnum($metastats['suspending']['uperc'],$lc,2).'%)</td></tr></table>
  254. <table class="ms_leg_tab"><tr><td class="ms_limiting_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances limiting Threads').': '.fnum($metastats['limiting']['users'],$lc).' / '.fnum($row['tusers'],$lc).' ('.fnum($metastats['limiting']['uperc'],$lc,2).'%)</td></tr></table>
  255. <table class="ms_leg_tab"><tr><td class="ms_suspending_u"></td><td class="ms_leg_td">+</td><td class="ms_limiting_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances suspending or limiting Threads').': '.fnum($metastats['suspending']['users']+$metastats['limiting']['users'],$lc).' / '.fnum($row['tusers'],$lc).' ('.fnum($metastats['suspending']['uperc']+$metastats['limiting']['uperc'],$lc,2).'%)</td></tr></table>
  256. <table class="ms_leg_tab"><tr><td class="ms_notsusporlim_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances not suspending or limiting Threads').': '.fnum($metastats['notsusporlim']['users'],$lc).' / '.fnum($row['tusers'],$lc).' ('.fnum($metastats['notsusporlim']['uperc'],$lc,2).'%)</td></tr></table>
  257. <table class="ms_leg_tab"><tr><td class="ms_unknown_u"></td><td class="ms_leg_td">&nbsp;'._('Users on instances whose Threads mod. status is unknown').': '.fnum($metastats['unknown']['users'],$lc).' / '.fnum($row['tusers'],$lc).' ('.fnum($metastats['unknown']['uperc'],$lc,2).'%)</td></tr></table>
  258. <table class="ms_cont"><tr><td class="ms_suspending_u" style="width:'.rf($metastats['suspending']['uperc']).'%"></td><td class="ms_limiting_u" style="width:'.rf($metastats['limiting']['uperc']).'%"></td><td class="ms_notsusporlim_u" style="width:'.rf($metastats['notsusporlim']['uperc']).'%"></td><td class="ms_unknown_u" style="width:'.rf($metastats['unknown']['uperc']).'%"></td></tr></table>
  259. </div>'.N;
  260. if ((array_key_exists('exnox',$_GET) && $_GET['exnox']=='1') || !array_key_exists('exnox',$_GET)) {
  261. $_GET['cbexnox']=' checked';
  262. $_GET['exnox']=1;
  263. } else {
  264. $_GET['cbexnox']='';
  265. $_GET['exnox']=0;
  266. }
  267. if ((array_key_exists('exregc',$_GET) && $_GET['exregc']=='1') || !array_key_exists('exregc',$_GET)) {
  268. $_GET['cbexregc']=' checked';
  269. $_GET['exregc']=1;
  270. } else {
  271. $_GET['cbexregc']='';
  272. $_GET['exregc']=0;
  273. }
  274. // if ((array_key_exists('inths',$_GET) && $_GET['inths']=='1') || !array_key_exists('inths',$_GET)) {
  275. if (array_key_exists('inths',$_GET) && $_GET['inths']=='1') {
  276. $_GET['cbinths']=' checked';
  277. $_GET['inths']=1;
  278. } else {
  279. $_GET['cbinths']='';
  280. $_GET['inths']=0;
  281. }
  282. if (array_key_exists('exappr',$_GET) && $_GET['exappr']=='1') {
  283. $_GET['cbexappr']=' checked';
  284. $_GET['exappr']=1;
  285. } else {
  286. $_GET['cbexappr']='';
  287. $_GET['exappr']=0;
  288. }
  289. if (array_key_exists('exlcko',$_GET) && $_GET['exlcko']=='1') {
  290. $_GET['cbexlcko']=' checked';
  291. $_GET['exlcko']=1;
  292. } else {
  293. $_GET['cbexlcko']='';
  294. $_GET['exlcko']=0;
  295. }
  296. if (array_key_exists('lang',$_GET)) {
  297. if (preg_match('#^[0-9]+$#',$_GET['lang'])===1)
  298. $_GET['lang']=$_GET['lang']+0;
  299. else
  300. $_GET['lang']=0;
  301. } else {
  302. $res=tquery('SELECT ID FROM Languages WHERE Code="'.myesc($link,$dlang).'"',__LINE__);
  303. if (mysqli_num_rows($res)>0) {
  304. $row=mysqli_fetch_assoc($res);
  305. $_GET['lang']=$row['ID'];
  306. } else {
  307. $_GET['lang']=0;
  308. }
  309. }
  310. if ($_GET['lang']>5000000000) $_GET['lang']=0;
  311. if (array_key_exists('desc',$_GET) && preg_match('#^.+$#',$_GET['lang'])===1)
  312. $_GET['desc']=trim(n2es($_GET['desc']));
  313. else
  314. $_GET['desc']='';
  315. if (mb_strlen($_GET['desc'])>64) $_GET['desc']='';
  316. ckgnum('minu',$minudef,$minumax);
  317. ckgnum('maxu',$maxudef,$maxumax);
  318. if (is_int($_GET['maxu']) && is_int($_GET['minu']) && $_GET['maxu']<$_GET['minu']) $_GET['maxu']=$_GET['minu'];
  319. ckgnum('minau',$minaudef,$minaumax);
  320. ckgnum('minc',$mincdef,$mincmax);
  321. $order=[
  322. 'rand'=>['t'=>_('Random'), 'q'=>'Instances.RPos ASC'],
  323. 'invold'=>['t'=>_('By users’ involvement (active users / users), descending'), 'q'=>'(Instances.ActiveUsersMonth / Instances.UserCount) DESC'],
  324. 'invola'=>['t'=>_('By users’ involvement (active users / users), ascending'), 'q'=>'(Instances.ActiveUsersMonth / Instances.UserCount) ASC'],
  325. 'fseend'=>['t'=>_('By date and time of first sighting, descending'), 'q'=>'Instances.FirstSeen DESC'],
  326. 'fseena'=>['t'=>_('By date and time of first sighting, ascending'), 'q'=>'Instances.FirstSeen ASC'],
  327. 'tusersd'=>['t'=>_('By number of users, descending'), 'q'=>'Instances.UserCount DESC'],
  328. 'tusersa'=>['t'=>_('By number of users, ascending'), 'q'=>'Instances.UserCount ASC'],
  329. 'ausersd'=>['t'=>_('By number of active users, descending'), 'q'=>'Instances.ActiveUsersMonth DESC'],
  330. 'ausersa'=>['t'=>_('By number of active users, ascending'), 'q'=>'Instances.ActiveUsersMonth ASC'],
  331. 'checksd'=>['t'=>_('By responsiveness to our checks, descending'), 'q'=>'(Instances.OkChecks / Instances.TotChecks) DESC, Instances.TotChecks DESC'],
  332. 'checksa'=>['t'=>_('By responsiveness to our checks, ascending'), 'q'=>'(Instances.OkChecks / Instances.TotChecks) ASC, Instances.TotChecks ASC'],
  333. 'charsd'=>['t'=>_('By available characters per post, descending'), 'q'=>'Instances.MaxTootChars DESC'],
  334. 'charsa'=>['t'=>_('By available characters per post, ascending'), 'q'=>'Instances.MaxTootChars ASC'],
  335. 'noxd'=>['t'=>_('Noxious first (switches off “Exclude noxious”)'), 'q'=>'Instances.Noxious DESC']
  336. ];
  337. if (!array_key_exists('ord',$_GET) || !array_key_exists($_GET['ord'],$order))
  338. $_GET['ord']='rand';
  339. // $res=tquery('SELECT COUNT(Instances.ID) AS Recomm FROM Instances LEFT JOIN InstOurLangs ON InstOurLangs.InstID=Instances.ID WHERE InstOurLangs.OurLangID='.$_GET['lang'].' AND Instances.IsMastodon=1 AND Instances.Visible=1 AND Instances.LastOkCheckTS>='.$graceline.' AND InstOurLangs.Pos=1 AND Instances.Priority>127',__LINE__);
  340. $res=tquery('SELECT InstOurLangs.OurLangID FROM Instances LEFT JOIN InstOurLangs ON InstOurLangs.InstID=Instances.ID WHERE Instances.IsMastodon=1 AND Instances.Visible=1 AND Instances.LastOkCheckTS>='.$graceline.' AND InstOurLangs.Pos=1 AND Instances.Priority>127 GROUP BY InstOurLangs.OurLangID',__LINE__);
  341. $langswri=[];// langs with recommended instances
  342. while ($row=mysqli_fetch_assoc($res))
  343. $langswri[]=$row['OurLangID'];
  344. if (count($langswri)>0)
  345. array_unshift($langswri,0);
  346. if (in_array($_GET['lang'],$langswri))
  347. $order['rand']=['t'=>_('Random, recommended first'), 'q'=>'Instances.Priority DESC, Instances.RPos ASC'];
  348. $p=1;
  349. if (array_key_exists('p',$_GET) && preg_match('#^[0-9]+$#',$_GET['p'])===1) $p=$_GET['p']+0;
  350. echo('<h3>'._('Search instances').'</h3>
  351. <form method="get" id="curvf" name="curvf">
  352. <input name="exnox" type="hidden" value="'.$_GET['exnox'].'">
  353. <input name="exregc" type="hidden" value="'.$_GET['exregc'].'">
  354. <input name="exappr" type="hidden" value="'.$_GET['exappr'].'">
  355. <input name="exlcko" type="hidden" value="'.$_GET['exlcko'].'">
  356. <input name="inths" type="hidden" value="'.$_GET['inths'].'">
  357. <input name="lang" type="hidden" value="'.$_GET['lang'].'">
  358. <input name="desc" type="hidden" value="'.htmlentities($_GET['desc']).'">
  359. <input name="minu" type="hidden" value="'.$_GET['minu'].'">
  360. <input name="maxu" type="hidden" value="'.$_GET['maxu'].'">
  361. <input name="minau" type="hidden" value="'.$_GET['minau'].'">
  362. <input name="minc" type="hidden" value="'.$_GET['minc'].'">
  363. <input name="ord" type="hidden" value="'.$_GET['ord'].'">
  364. <input name="p" type="hidden" value="'.$p.'">
  365. </form>'.N);
  366. ($_GET['lang']==0) ? $selected=' selected' : $selected='';
  367. echo '<form method="get" id="searchf" class="sdbox" onsubmit="presub();">
  368. <div class="sdtit">'._('Search criteria').'</div>
  369. <div class="sddesc">'._('Show instances where...').'</div>
  370. <div class="sdrow">
  371. <div class="sdlabel"><label for="lang" title="'._('Include only instances where the most used language is the one selected here').'">'._('Most used language is').'</label></div>
  372. <div class="sdinput">
  373. <select id="lang" name="lang" class="sselect" onchange="updord();">
  374. <option value="0"'.$selected.'>'._('Irrelevant').'</option>'.N;
  375. $res=tquery('SELECT Languages.ID AS Lid, CONCAT(Name'.$dlanguc.', \' [\', Code, \'] (\', COUNT(Languages.ID), \')\') AS Txt FROM InstOurLangs LEFT JOIN Instances ON Instances.ID=InstOurLangs.InstID LEFT JOIN Languages ON Languages.ID=OurLangID WHERE InstOurLangs.Pos=1 AND Instances.IsMastodon=1 AND Instances.Visible=1 AND Instances.LastOkCheckTS>='.$graceline.' GROUP BY Languages.ID ORDER BY Languages.Name'.$dlanguc.' ASC',__LINE__);
  376. while ($row=mysqli_fetch_assoc($res)) {
  377. ($_GET['lang']==$row['Lid']) ? $selected=' selected' : $selected='';
  378. echo '<option value="'.$row['Lid'].'"'.$selected.'>'.$row['Txt'].'</option>'.N;
  379. }
  380. echo '</select>
  381. </div>
  382. </div>
  383. <script language="JavaScript">
  384. let langswri=["'.implode('","',$langswri).'"];
  385. </script>
  386. <div class="sdrow">
  387. <div class="sdlabel"><label for="desc" title="'._('Include only instances whose URI, name or description (short or long) contains the specified expression').'">'._('URI, name or descriptions contain').'</label></div>
  388. <div class="sdinput"><input type="text" id="desc" name="desc" class="sinput" maxlength="64" value="'.hspech($_GET['desc']).'" autofocus></div>
  389. </div>
  390. <div class="sdrow">
  391. <div class="sdlabel"><label for="minu" title="'._('Include only instances which have at least this number of users [set to empty to disable this criterion]').'">'._('Users are at least').'</label></div>
  392. <div class="sdinput"><input type="number" id="minu" name="minu" min="0" max="'.$minumax.'" class="sinput" value="'.$_GET['minu'].'"></div>
  393. </div>
  394. <div class="sdrow">
  395. <div class="sdlabel"><label for="maxu" title="'._('Include only instances which have at most this number of users [set to empty to disable this criterion]').'">'._('Users are at most').'</label></div>
  396. <div class="sdinput"><input type="number" id="maxu" name="maxu" min="0" max="'.$maxumax.'" class="sinput" value="'.$_GET['maxu'].'"></div>
  397. </div>
  398. <div class="sdrow">
  399. <div class="sdlabel"><label for="minau" title="'._('Include only instances which had at least this number of active users during the last 30 days [set to empty to disable this criterion]').'">'._('Active users are at least').'</label></div>
  400. <div class="sdinput"><input type="number" id="minau" name="minau" min="0" max="'.$minaumax.'" class="sinput" value="'.$_GET['minau'].'"></div>
  401. </div>
  402. <div class="sdrow">
  403. <div class="sdlabel"><label for="minc" title="'._('Include only instances on which the number of available characters per post is at least this [set to empty to disable this criterion]').'">'._('Max. chars per post are at least').'</label></div>
  404. <div class="sdinput"><input type="number" id="minc" name="minc" min="0" max="'.$mincmax.'" class="sinput" value="'.$_GET['minc'].'"></div>
  405. </div>
  406. <div class="hrd"></div>
  407. <div class="sdrow">
  408. <div class="sdlabel"><label for="cbexnox" title="'._('Exclude noxious instances').'">'._('Exclude if noxious').'</label></div>
  409. <div class="sdinput">
  410. <input type="checkbox" class="sckbox" id="cbexnox" value="1"'.$_GET['cbexnox'].' onchange="sethid(\'exnox\');">
  411. <input type="hidden" id="exnox" name="exnox" value="'.$_GET['exnox'].'">
  412. </div>
  413. </div>
  414. <div class="sdrow">
  415. <div class="sdlabel"><label for="cbexregc" title="'._('Exclude instances which don’t accept new registrations').'">'._('Exclude if registrations are closed').'</label></div>
  416. <div class="sdinput">
  417. <input type="checkbox" class="sckbox" id="cbexregc" value="1"'.$_GET['cbexregc'].' onchange="sethid(\'exregc\');">
  418. <input type="hidden" id="exregc" name="exregc" value="'.$_GET['exregc'].'">
  419. </div>
  420. </div>
  421. <div class="sdrow">
  422. <div class="sdlabel"><label for="cbinths" title="'._('Include only instances which surely have Meta’s Threads *suspended*').'">'._('Include only if Threads is surely <i>suspended</i>').'</label></div>
  423. <div class="sdinput">
  424. <input type="checkbox" class="sckbox" id="cbinths" value="1"'.$_GET['cbinths'].' onchange="sethid(\'inths\');">
  425. <input type="hidden" id="inths" name="inths" value="'.$_GET['inths'].'">
  426. </div>
  427. </div>
  428. <div class="sdrow">
  429. <div class="sdlabel"><label for="cbexappr" title="'._('Exclude instances on which admin approval is required for registration').'">'._('Exclude if registration requires approval').'</label></div>
  430. <div class="sdinput">
  431. <input type="checkbox" class="sckbox" id="cbexappr" value="1"'.$_GET['cbexappr'].' onchange="sethid(\'exappr\');">
  432. <input type="hidden" id="exappr" name="exappr" value="'.$_GET['exappr'].'">
  433. </div>
  434. </div>
  435. <div class="sdrow">
  436. <div class="sdlabel"><label for="cbexlcko" title="'._('Exclude instances which didn’t respond to last check').'">'._('Exclude if offline on last check').'</label></div>
  437. <div class="sdinput">
  438. <input type="checkbox" class="sckbox" id="cbexlcko" value="1"'.$_GET['cbexlcko'].' onchange="sethid(\'exlcko\');">
  439. <input type="hidden" id="exlcko" name="exlcko" value="'.$_GET['exlcko'].'">
  440. </div>
  441. </div>
  442. <div class="sdtitb">'._('Order of results').'</div>
  443. <div class="sdcrow">
  444. <select id="ord" name="ord" class="sselect100">'.N;
  445. foreach ($order as $key=>$buf) {
  446. ($key!=$_GET['ord']) ? $selected='' : $selected=' selected';
  447. echo '<option value="'.$key.'"'.$selected.'>'.$buf['t'].'</option>'.N;
  448. }
  449. echo('</select>
  450. </div>
  451. <div class="sdlrow">
  452. <input type="submit" value="'._('Search').'" class="ssubmit">
  453. </div>
  454. <div class="sdcrow">
  455. <input type="button" class="litbut" value="'._('Set criteria to include all instances').'" onclick="nocrit();">
  456. </div>
  457. <div class="sdlcrow">
  458. <input type="button" class="litbut" value="'._('Reset to default criteria').'" onclick="document.location.href=document.location.href.replace(/\\?.*$/,\'\');">
  459. </div>
  460. <input type="hidden" id="p" name="p" value="'.$p.'">
  461. </form>
  462. <script language="JavaScript">
  463. swhelp();
  464. </script>'.N);
  465. $joins=[];
  466. $wheres=[];
  467. $wheres[]='Instances.IsMastodon=1 AND Instances.Visible=1 AND Instances.LastOkCheckTS>='.$graceline;
  468. if ($_GET['exnox']==1) $wheres[]='Instances.Noxious=0';
  469. if ($_GET['exregc']==1) $wheres[]='Instances.RegOpen=1';
  470. if ($_GET['exappr']==1) $wheres[]='Instances.RegReqApproval=0';
  471. if ($_GET['exlcko']==1) $wheres[]='Instances.WasLastCheckOk=1';
  472. /*if ($_GET['exlcko']==1) {
  473. $joins[]='LEFT JOIN InstChecks AS InstChecks ON InstChecks.InstID=Instances.ID AND InstChecks.Time=(SELECT MAX(InstChecks.Time) AS MaxTime FROM InstChecks WHERE InstChecks.InstID=Instances.ID)';
  474. $wheres[]='InstChecks.Status=1';
  475. }*/
  476. if ($_GET['inths']==1) {
  477. if ($cfedipact>0)
  478. $wheres[]='Instances.Threads="suspended" OR Instances.ID IN ('.implode(', ',$fedipact['instids']).')';
  479. else
  480. $wheres[]='Instances.Threads="suspended"';
  481. }
  482. if ($_GET['lang']>0) {
  483. $joins[]='LEFT JOIN InstOurLangs ON InstOurLangs.InstID=Instances.ID';
  484. $wheres[]='(InstOurLangs.OurLangID='.$_GET['lang'].' AND InstOurLangs.Pos=1)';
  485. }
  486. $buf=preg_replace('#%#','\%',myesc($link,$_GET['desc']));
  487. if ($_GET['desc']!='') $wheres[]='(Instances.URI LIKE "%'.$buf.'%" OR Instances.Title LIKE "%'.$buf.'%" OR Instances.ShortDesc LIKE "%'.$buf.'%" OR Instances.LongDesc LIKE "%'.$buf.'%")';
  488. if ($_GET['minu']!='') $wheres[]='Instances.UserCount>='.$_GET['minu'];
  489. if ($_GET['maxu']!='') $wheres[]='Instances.UserCount<='.$_GET['maxu'];
  490. if ($_GET['minau']!='') $wheres[]='Instances.ActiveUsersMonth>='.$_GET['minau'];
  491. if ($_GET['minc']!='' && $_GET['minc']!='""') $wheres[]='Instances.MaxTootChars>='.$_GET['minc'];
  492. $joins=implode(' ',$joins);
  493. $wheres='WHERE '.implode(' AND ',$wheres);
  494. $que='SELECT * FROM Instances '.$joins.' '.$wheres.' ORDER BY '.$order[$_GET['ord']]['q'];
  495. }
  496. debug('MAIN QUERY: «'.$que.'»'.N,$debug);
  497. $res=tquery($que,__LINE__);
  498. $itot=mysqli_num_rows($res);
  499. debug('RESULTS: '.$itot.N,$debug);
  500. /*if ($itot==0) {
  501. echo(_('No instance matches with the search criteria.').N.'</section>'.N.'</div>'.N.'</div>'.N.'</body>'.N.'</html>'.N);
  502. exit(0);
  503. }*/
  504. $ipp=10;// istanze per pagina
  505. $if=$ipp*($p-1);
  506. if ($if>=$itot) {
  507. $if=0;
  508. $p=1;
  509. }
  510. $ptot=ceil($itot/$ipp);
  511. ($p!=$ptot) ? $il=$if+$ipp : $il=$itot;
  512. $dfmt=datefmt_create($blang,IntlDateFormatter::MEDIUM,IntlDateFormatter::NONE,'UTC',IntlDateFormatter::GREGORIAN);
  513. $defmt=datefmt_create($blang,IntlDateFormatter::SHORT,IntlDateFormatter::MEDIUM,'UTC',IntlDateFormatter::GREGORIAN);
  514. $wrfss=1602633600;// we record FirstSeen since October 14, 2020, 0:0:0 UTC
  515. $wrfsstxt=datefmt_format($dfmt,$wrfss).' UTC';
  516. mysqli_data_seek($res,$if);
  517. while ($if<$il && $row=mysqli_fetch_assoc($res)) {
  518. $if++;
  519. $out='<div class="ihead"><a href="https://'.hspech($row['URI']).'" target="_blank" title="'._('Go to instance').'">'.hspech($row['URI']).'</a>';
  520. if (!$single) $out.=' <a href="'.$conf['instpath'].'/instances?id='.$row['ID'].'" title="'._('Direct link to this instance’s card').'">('.$if.'/'.$itot.')</a>';
  521. $out.='</div>'.N;
  522. ($if<$il) ? $out.='<div class="ibody">'.N : $out.='<div class="ibodylast">'.N;
  523. $rres=tquery('SELECT CONCAT(Languages.Name'.$dlanguc.'," (",Languages.Code,")") AS Lang FROM InstOurLangs LEFT JOIN Languages ON Languages.ID=InstOurLangs.OurLangID WHERE InstOurLangs.InstID='.$row['ID'].' ORDER BY InstOurLangs.Pos ASC',__LINE__);
  524. $buf=[];
  525. while ($rrow=mysqli_fetch_assoc($rres))
  526. $buf[]=hspech($rrow['Lang']);
  527. ($row['Thumb']=='unavailable') ? $thumb=$prepath.'imgs/InstThumbUnavailable.svg' : $thumb=$row['Thumb'];
  528. $out.='<div class="iimgc"><a href="https://'.hspech($row['URI']).'" target="_blank" title="'._('Go to instance').'"><img class="iimg" src="'.$thumb.'"></a>';
  529. // if (!is_null($row['Priority'])) $out.='<img src="'.$prepath.'imgs/featured-it.svg" class="ifeat">';
  530. if (!is_null($row['Priority'])) $out.='<div class="dfeat">'._('Recommended').'</div>';
  531. if ($row['Thumb']=='unavailable' && $row['WasLastCheckOk']==1) $out.='<div class="unavmsg">'._('It seems like this instance’s server thumbnail’s URL points to a non existent file. This is usually easily fixable by the instance’s admin(s) by uploading again the image to their server. Note that the change won’t be displayed here before the nightly automatic update of this instance’s infos occurs.').'</div>';
  532. $out.='</div>'.N;
  533. $out.='<div class="icol">'.N;
  534. $out.='<div><span class="ilab">'._('Name').'</span> <a href="https://'.hspech($row['URI']).'" target="_blank" title="'._('Go to instance').'">'.nully(hspech($row['Title'])).'</a></div>'.N;
  535. /*$out.='<div><span class="ilab">'._('Consigliata').'</span> ';
  536. (!is_null($row['Priority'])) ? $out.='<span class="ivgood">'._('Si!').'</span>' : $out.=_('No');
  537. $out.='</div>'.N;*/
  538. $out.='<div><span class="ilab">'._('Languages').'</span> '.nully(implode(', ',$buf)).'</div>'.N;
  539. $out.='<div><span class="ilab">'._('Users').'</span> '.nully(fnum($row['UserCount'],$lc)).'</div>'.N;
  540. $out.='<div><span class="ilab">'._('Active users (last month)').'</span> '.nully(fnum($row['ActiveUsersMonth'],$lc)).'</div>'.N;
  541. $out.='<div><span class="ilab">'._('Active users (last six months)').'</span> '.nully(fnum($row['ActiveUsersHalfYear'],$lc)).'</div>'.N;
  542. $out.='<div><span class="ilab">'._('Characters per post (max)').'</span> ';
  543. if (nullemp(fnum($row['MaxTootChars'],$lc)))
  544. $out.='<span class="null">500</span>';
  545. else
  546. $out.=$row['MaxTootChars'];
  547. $out.='</div>'.N;
  548. $out.='<div><span class="ilab">'._('Known instances').'</span> '.nully(fnum($row['DomainCount'],$lc)).'</div>'.N;
  549. $out.='</div>'.N;
  550. $out.='<div class="icol">'.N;
  551. if (is_null($row['FirstSeen'])) {// can't currently happen, but we put it there in any event
  552. $fdate='<span class="null">'._('Not available{singular}').'</span>';
  553. } elseif ($row['FirstSeen']<$wrfss) {
  554. $fdate='<span class="nulltip" title="'._('We record this value only since').' '.$wrfsstxt.' ;-)">'._('Before').' '.$wrfsstxt.'</span>';
  555. } else {
  556. $fdate=datefmt_format($dfmt,$row['FirstSeen']).' UTC';
  557. }
  558. $out.='<div><span class="ilab">'._('First sight').'</span> '.$fdate.'</div>'.N;
  559. (is_null($row['LastOkCheckTS'])) ? $fdate='<span class="null">'._('Not available{singular}').'</span>' : $fdate=datefmt_format($dfmt,$row['LastOkCheckTS']).' UTC';
  560. $out.='<div><span class="ilab">'._('Last successful check').'</span> '.$fdate.'</div>'.N;
  561. $out.='<div><span class="ilab">'._('Noxious').'</span> ';
  562. ($row['Noxious']==1) ? $out.='<span class="ibad">'._('Yes (see why below)').'</span>' : $out.='<span class="igood">'._('No').'</span>';
  563. $out.='</div>'.N;
  564. $out.='<div><span class="ilab">'._('New').'</span> ';
  565. ($btu-$row['InsertTS']<=$oldline) ? $out.='<span class="ivgood">'._('Yes!').'</span>' : $out.=_('No');
  566. $out.='</div>'.N;
  567. $software='';
  568. if (!is_null($row['Software'])) $software.=ucfirst($row['Software']);
  569. if (!is_null($row['Version'])) $software.=' '.$row['Version'];
  570. $out.='<div><span class="ilab">'._('Software').'</span> '.nully(hspech($software)).'</div>'.N;
  571. $out.='<div><span class="ilab">'._('Registrations').'</span> ';
  572. ($row['RegReqApproval']==1) ? $buf=' <span class="iwarn">('._('by admin approval').')</span>' : $buf='';
  573. if (nullemp($row['RegOpen']))
  574. $out.='<span class="null">'._('Not available{singular}').'</span>';
  575. elseif ($row['RegOpen']==1)
  576. $out.='<span class="igood">'._('Open').'</span>'.$buf;
  577. else
  578. $out.='<span class="iwarn">'._('Closed').'</span>';
  579. $out.='</div>'.N;
  580. $out.='<div><span class="ilab">'._('E-mail').'</span> ';
  581. if (nullemp($row['Email']))
  582. $out.='<span class="null">'._('Not available{singular}').'</span>';
  583. else
  584. $out.='<a href="mailto:'.$row['Email'].'">'.$row['Email'].'</a>';
  585. $out.='</div>'.N;
  586. $out.='</div>'.N;
  587. $out.='<div><span class="ilab">'._('Meta’s Threads moderation').'</span> ';
  588. if (in_array($row['ID'],$fedipact['instids'])) {
  589. $out.=_('Threads is <i>suspended</i>').', '._('according to the <a href="https://fedipact.online/" target="_blank">Anti-Meta Fedi Pact</a> list');
  590. if (is_null($row['Threads']))
  591. $out.=', '._('but it’s currently impossible to verify it because this instance’s list of moderated instances is not accessible').'.';
  592. elseif ($row['Threads']=='accessible')
  593. $out.=', '._('but it appears to be <i>accessible</i>, instead').', '._('according to this instance’s list of moderated instances').'.';
  594. elseif ($row['Threads']=='suspended')
  595. $out.=', '._('and also').' '._('according to this instance’s list of moderated instances').'.';
  596. elseif ($row['Threads']=='limited')
  597. $out.=', '._('but it appears to be only <i>limited</i>, instead').', '._('according to this instance’s list of moderated instances').'.';
  598. else
  599. $out.='.';
  600. } elseif (is_null($row['Threads'])) {
  601. $out.=_('<i>Unknown</i> (this instance is not listed in the <a href="https://fedipact.online/" target="_blank">Anti-Meta Fedi Pact</a> list), and its list of moderated instances is not accessible').'.';
  602. } elseif ($row['Threads']=='accessible') {
  603. $out.=_('Threads is neither <i>suspended</i> nor <i>limited<i>').', '._('according to this instance’s list of moderated instances').'.';
  604. } elseif ($row['Threads']=='suspended') {
  605. $out.=_('Threads is <i>suspended</i>').', '._('according to this instance’s list of moderated instances').'.';
  606. } elseif ($row['Threads']=='limited') {
  607. $out.=_('Threads is <i>limited</i>').', '._('according to this instance’s list of moderated instances').'.';
  608. } else {
  609. $out.=_('<i>Unknown value</i>').': «'.htmlentities($row['Threads']).'»; '._('please contact masthelp[at]insicuri.net').'.';
  610. }
  611. $out.='</div>'.N;
  612. $out.='<div><span class="ilab">'._('Most used hashtags (last week)').'</span> ';
  613. $rres=tquery('SELECT * FROM InstTrends WHERE InstID='.$row['ID'].' ORDER BY Pos ASC',__LINE__);
  614. if (mysqli_num_rows($rres)>0) {
  615. $buf=[];
  616. while ($rrow=mysqli_fetch_assoc($rres))
  617. $buf[]='<a href="'.hspech($rrow['URL']).'" target="_blank">'.hspech($rrow['Name']).'</a>';
  618. $out.=implode(', ',$buf);
  619. } else {
  620. $out.='<span class="null">'._('Not available{plural}').'</span>';
  621. }
  622. $out.='</div>'.N;
  623. if ($row['Noxious']) {
  624. $out.='<div><div class="noxlab">'._('Why we consider this instance noxious').'</div><div class="noxreas">'.nully(strip($row['NoxReason'],$row['URI'])).'</div></div>'.N;
  625. }
  626. $out.='<div><div class="idlab">'._('Short description').'</div><div class="idesc">'.nully(strip($row['ShortDesc'],$row['URI'])).'</div></div>'.N;
  627. $out.='<div><div class="idlab">'._('Long description').'</div><div class="idesc">'.nully(strip($row['LongDesc'],$row['URI'])).'</div></div>'.N;
  628. $out.='<div><div class="idlab">'._('Instance rules').'</div><div class="idesc">';
  629. $rres=tquery('SELECT Text FROM InstRules WHERE InstID='.$row['ID'],__LINE__);
  630. if (mysqli_num_rows($rres)>0) {
  631. $out.='<ol class="nobott">'.N;
  632. while ($rrow=mysqli_fetch_assoc($rres))
  633. $out.='<li>'.hspech($rrow['Text']).'</li>'.N;// no strip, do hspech, because instance rules don’t support html nor markdown in mastodon
  634. $out.='</ol>'.N;
  635. } else {
  636. $out.='<span class="null">'._('Not available{plural}').'</span>';
  637. }
  638. $out.='</div></div>'.N;
  639. $out.='<div><div class="idlab">'._('Moderated instances').'</div><div class="idesc">';
  640. $sevmap=['silence'=>_('limited'), 'suspend'=>_('sospeso')];
  641. $rres=tquery('SELECT Domain, Severity, Comment FROM InstBlocks WHERE InstID='.$row['ID'],__LINE__);
  642. if (mysqli_num_rows($rres)>0) {
  643. $out.='<ul class="nobott">'.N;
  644. while ($rrow=mysqli_fetch_assoc($rres)) {
  645. $out.='<li><strong>'.hspech($rrow['Domain']).'</strong>: '.$sevmap[$rrow['Severity']];
  646. if (!is_null($rrow['Comment'])) $out.=' - '.hspech($rrow['Comment']);// no strip, do hspech, because moderated instances comments don’t support html nor markdown in mastodon
  647. $out.='</li>'.N;
  648. }
  649. $out.='</ul>'.N;
  650. } else {
  651. $out.='<span class="null">'._('Not available{plural}').'</span>';
  652. }
  653. $out.='</div></div>'.N;
  654. $admacc=nully(null);
  655. $thumb=$prepath.'imgs/AdmAccThumbUnavailable.svg';
  656. if (!is_null($row['AdmAccount']) && $row['AdmAccount']!='OPTED OUT') {
  657. $admacc='<a href="https://' . hspech($row['URI']) . '/@' . hspech($row['AdmAccount']) . '" target="_blank">' . hspech($row['AdmAccount']) . '@' . hspech($row['URI']) . '</a>';
  658. if ($row['AdmAvatar']!='unavailable') $thumb=$row['AdmAvatar'];
  659. } elseif ($row['AdmAccount']=='OPTED OUT') {
  660. $admacc='<span class="null">'._('Opted out of search engines indexing').'</span>';
  661. }
  662. (is_null($row['AdmCreatedAt'])) ? $fdate='<span class="null">'._('Not available{singular}').'</span>' : $fdate=datefmt_format($dfmt,$row['AdmCreatedAt']).' UTC';
  663. $out.='<div class="abox"><img class="aimg" src="'.$thumb.'"><div><span class="ilab">'._('Admin account').'</span> '.$admacc.'</div><div><span class="ilab">'._('Date of creation').'</span> '.$fdate.'</div><div><span class="ilab">'._('Display name').'</span> '.nully(hspech($row['AdmDisplayName'])).'</div><div><div class="idlab">'._('Bio').'</div><div class="inote">'.nully(strip($row['AdmNote'],$row['URI'])).'</div>'.N;
  664. $out.='</div></div>';
  665. $out.='<div class="ghost"><div class="idlab">'._('Statistics').'</div><div class="istat">';
  666. $rres=tquery('SELECT COUNT(InstID) AS cnt, SUM(Statuses) AS tstatuses, SUM(Logins) AS tlogins, SUM(Registrations) AS tregs FROM InstActivity WHERE InstID='.$row['ID'],__LINE__);
  667. $out.='<div class="dida">'._('Last 12 weeks activity');
  668. if (mysqli_num_rows($rres)>0) {
  669. $rrow=mysqli_fetch_assoc($rres);
  670. if ($rrow['cnt']>0)
  671. $out.=' ('._('totals:').' '.$rrow['tstatuses'].' '._('statuses').', '.$rrow['tlogins'].' '._('logins').', '.$rrow['tregs'].' '._('registrations').')';
  672. }
  673. $out.='</div>'.N;
  674. $rres=tquery('SELECT * FROM InstActivity WHERE InstID='.$row['ID'].' ORDER BY Week ASC',__LINE__);
  675. $out.='<table class="abar"><tr>';
  676. $tot=mysqli_num_rows($rres);
  677. if ($tot>0) {
  678. while ($rrow=mysqli_fetch_assoc($rres))
  679. // initials for Statuses, Logins, Registrations
  680. $out.='<td width="'.(100/$tot).'%">'._('S').': '.$rrow['Statuses'].'<br>'._('L').': '.$rrow['Logins'].'<br>'._('R').': '.$rrow['Registrations'].'</td>';
  681. } else {
  682. $out.='<td><span class="null">'._('Not available{singular}').'</span></td>';
  683. }
  684. $out.='</tr></table>'.N;
  685. $rres=tquery('SELECT * FROM InstChecks WHERE InstID='.$row['ID'].' ORDER BY Time DESC LIMIT 0,8',__LINE__);
  686. $tot=mysqli_num_rows($rres);
  687. $buf=[];
  688. while ($rrow=mysqli_fetch_assoc($rres)) $buf[]=$rrow;
  689. $out.='<div class="dida">'._('Last checks (green: OK; red: KO)').'</div><table class="cbar"><tr>';
  690. for ($i=$tot-1; $i>=0; $i--) {
  691. $out.='<td width="'.(100/$tot).'%"';
  692. if ($buf[$i]['Status']==1) $out.=' class="cbarok"';
  693. $out.='>'.datefmt_format($defmt,$buf[$i]['Time']).' UTC</td>';
  694. }
  695. $out.='</tr></table>'.N;
  696. $out.='<div class="dida100">'._('Succesful checks').': '.$row['OkChecks'].'/'.$row['TotChecks'].' ('.round($row['OkChecks']*100/$row['TotChecks'], 2).'%)</div>'.N;
  697. $out.='</div></div>'.N;
  698. $out.='</div>'.N;
  699. echo($out);
  700. }
  701. mysqli_close($link);
  702. echo('</section>'.N);
  703. if ($ptot>1) {
  704. echo('<div id="footmarg"></div>'.N);
  705. echo('<div id="bmenu">'.N);
  706. $pnav='<table id="pnav"><tr>';
  707. if ($p>1)
  708. $pnav.='<td><img src="'.$prepath.'imgs/nav_first.svg" onclick="swp(1);" class="hov" title="'._('Go to first page').'"></td><td><img src="'.$prepath.'imgs/nav_prev.svg" onclick="swp('.($p-1).');" class="hov" title="'._('Go to previous page').'"></td>';
  709. else
  710. $pnav.='<td><img src="'.$prepath.'imgs/nav_first_off.svg"></td><td><img src="'.$prepath.'imgs/nav_prev_off.svg"></td>';
  711. //$pnav.='<td>Page '.$p.'/'.$ptot.'</td>';
  712. $pnav.='<td><select id="pagesel" onchange="swp(this.value);" title="'._('Select page').'">'.N;
  713. for ($i=1; $i<=$ptot; $i++) {
  714. ($i!=$p) ? $selected='' : $selected=' selected';
  715. $li=$i*$ipp;
  716. if ($li>$itot) $li=$itot;
  717. //page number prefix
  718. $pnav.='<option value="'.$i.'"'.$selected.'>'._('Page').' '.$i.' '._('of').' '.$ptot.' ('._('instances').': '.(($i-1)*$ipp+1).'-'.$li.'/'.$itot.')</option>'.N;
  719. }
  720. $pnav.='</select></td>'.N;
  721. if ($p<$ptot)
  722. $pnav.='<td><img src="'.$prepath.'imgs/nav_next.svg" onclick="swp('.($p+1).');" class="hov" title="'._('Go to next page').'"></td><td><img src="'.$prepath.'imgs/nav_last.svg" onclick="swp('.($ptot).');" class="hov" title="'._('Go to last page').'"></td>';
  723. else
  724. $pnav.='<td><img src="'.$prepath.'imgs/nav_next_off.svg"></td><td><img src="'.$prepath.'imgs/nav_last_off.svg"></td>';
  725. $pnav.='</tr></table>'.N;
  726. echo($pnav);
  727. echo('</div>'.N);
  728. }
  729. echo('</div>'.N);
  730. echo('</div>'.N);
  731. debug('TOTAL RENDERING TIME: '.hrte($bt).N,$debug);
  732. if (array_key_exists('debug',$_GET) && $_GET['debug']=='1') echo('<!--'.N.'--- DEBUG INFO ---'.N.$debug.'//-->'.N);
  733. // functions
  734. function setfedipact() {
  735. global $link, $graceline, $fedipact, $cfedipact;
  736. $fedipact=['uris'=>[], 'uris_me'=>[], 'instids'=>[]];
  737. $fedipactfp='../clitools/fedipact.list';
  738. $fedipact['uris']=@file($fedipactfp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
  739. if (!is_array($fedipact['uris'])) {
  740. debug('WARNING: could not open «'.$fedipactfp.'».'.N,$debug);
  741. $fedipact['uris']=[];
  742. }
  743. $cfedipact=count($fedipact['uris']);
  744. if ($cfedipact>0) {
  745. foreach ($fedipact['uris'] as $val)
  746. $fedipact['uris_me'][]=myesc($link,$val);
  747. $fedipact['uris_me']='"'.implode('", "',$fedipact['uris_me']).'"';
  748. //echo '<pre>'.print_r($fedipact,true).'</pre>'.N;
  749. $res=tquery('SELECT ID, URI FROM Instances WHERE IsMastodon=1 AND Visible=1 AND LastOkCheckTS>='.$graceline.' AND URI IN ('.$fedipact['uris_me'].')', __LINE__);
  750. $fedipact['uris']=[];
  751. $fedipact['uris_me']=[];
  752. while ($row=mysqli_fetch_assoc($res)) {
  753. $fedipact['uris'][]=$row['URI'];
  754. $fedipact['instids'][]=$row['ID'];
  755. }
  756. foreach ($fedipact['uris'] as $val)
  757. $fedipact['uris_me'][]=myesc($link,$val);
  758. $fedipact['uris_me']='"'.implode('", "',$fedipact['uris_me']).'"';
  759. $cfedipact=count($fedipact['instids']);
  760. } else {
  761. $fedipact['uris_me']='';
  762. }
  763. }
  764. function debug($msg,&$debug) {
  765. if (array_key_exists('debug',$_GET) && $_GET['debug']=='1')
  766. $debug.=$msg;
  767. }
  768. function ckgnum($key,$def,$max) {
  769. if (array_key_exists($key,$_GET)) {
  770. $_GET[$key]=trim($_GET[$key]);
  771. if (preg_match('#^\d+$#',$_GET[$key])===1) {
  772. $_GET[$key]=$_GET[$key]+0;
  773. if ($_GET[$key]>$max) $_GET[$key]=$max;
  774. } elseif ($_GET[$key]!='') {
  775. $_GET[$key]=$def;
  776. }
  777. } else {
  778. $_GET[$key]=$def;
  779. }
  780. }
  781. function nullemp($inp) {
  782. if (is_null($inp) || preg_match('/^\s*$/',$inp)===1) return true;
  783. return false;
  784. }
  785. function nulltozero($inp) {
  786. if (is_null($inp)) return 0;
  787. return $inp;
  788. }
  789. function hspech($str) {
  790. if (nullemp($str)) return(null);
  791. return htmlspecialchars($str,ENT_QUOTES|ENT_HTML5,'UTF-8');
  792. }
  793. function muorimeglio($msg,$close) {
  794. global $link;
  795. if ($close)
  796. mysqli_close($link);
  797. echo('<p>'.$msg.'</p>'.N.'</section>'.N.'</div>'.N.'</div>'.N.'</body>'.N);
  798. exit(2);
  799. }
  800. function nully($str) {
  801. // "Not available" in singular form - translators, please omit "{singular}" from translation,
  802. // it's there just to diversify this "Not available" from the next one
  803. if (nullemp($str)) return('<span class="null">'._('Not available{singular}').'</span>');
  804. return $str;
  805. }
  806. function nullyp($str) {
  807. // "Not available" in plural form - translators, please omit "{plural}" from translation,
  808. // it's there just to diversify this "Not available" from the previous one
  809. if (nullemp($str)) return('<span class="null">'._('Not available{plural}').'</span>');
  810. return $str;
  811. }
  812. function strip($str,$uri) {
  813. if (nullemp($str)) return(null);
  814. $str=preg_replace('#<style( [^>]*)?>.*</style>#is','',$str);
  815. $str=preg_replace('#<a href="(?![a-zA-Z]+://)([^"]+)#i','<a href="https://'.$uri.'$1>',$str);
  816. $str=preg_replace(['#<h[1-9][^>]*>#i','#</h[1-9]>#i'],['<p class="exh">','</p>'],$str);
  817. $str=preg_replace(['#</p><br>#i','#</li><br>#i','#</ul><br>#i','#<ul><br>#i'],['</p>','</li>','</ul>','<ul>'],$str);
  818. $str=preg_replace(['#<b>#i','#</b>#i','#<i>#i','#</i>#i'],['<strong>','</strong>','<em>','</em>'],$str);
  819. $str=preg_replace('#<p>\s*</p>#is','',$str);
  820. $str=strip_tags($str,'<a><br><ol><ul><li><p><div><strong><em><small><img>');
  821. // all this part below is to try and assign the css "nobott" css class to a possible closing <p>/<ol>/<ul>/<div>,
  822. // to avoid the useless and UGLY last bottom-margin :-))
  823. $str=preg_replace('#^\s*#m','',$str);// strip all spaces from empty lines
  824. $str=preg_replace('#[\r\n]#',' ',$str);// strip all "wrap chars"
  825. $str=preg_replace('#(</p>|</ol>|</ul>|</div>)#i','$1'.N,$str);// now add a newline after every </p> and so on
  826. $str=rtrim($str);// trim the newline at the end of the whole text block in order for the next preg_replace to match against $ as end of the whole text block
  827. $str=preg_replace(['#<p[^>]*>(.*)</p>$#i', '#<ol[^>]*>(.*)</ol>$#i', '#<ul[^>]*>(.*)</ul>$#i', '#<div[^>]*>(.*)</div>$#i'],['<p class="nobott">$1</p>', '<ol class="nobott">$1</ol>', '<ul class="nobott">$1</ul>', '<div class="nobott">$1</div>'],$str);
  828. return $str;
  829. }
  830. function hrte($bt) {
  831. if (function_exists('bcdiv'))
  832. return bcdiv(bcsub(hrtime(true),$bt,9),1000000000,9);
  833. else
  834. return (hrtime(true)-$bt)/1000000000;
  835. }
  836. function tquery($query,$line) {
  837. global $link, $debug;
  838. $bt=hrtime(true);
  839. //usleep(rand(10000,1050000));
  840. $res=mysqli_query($link,$query) or muorimeglio('Error on line '.$line.': '.mysqli_error($link),true);
  841. debug(sprintf('%f secs. for query «'.$query.'»'.N,hrte($bt)),$debug);
  842. return $res;
  843. }
  844. ?>