PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i5h-3LL

HOME


5h-3LL 1.0
DIR: /srv/http/adent.cz/kovarskadilna.cz/wp-content/plugins/broken-link-checker/includes
/srv/http/adent.cz/kovarskadilna.cz/wp-content/plugins/broken-link-checker/includes/
Upload File:
Current File : /srv/http/adent.cz/kovarskadilna.cz/wp-content/plugins/broken-link-checker/includes/link-query.php
<?php

/**
 * Class for querying, sorting and filtering links.
 * Used as a singleton.
 * 
 * @package Broken Link Checker
 * @access public
 */
class blcLinkQuery {
	
	var $native_filters;
	var $search_filter;
	var $custom_filters = array();
	
	var $valid_url_params = array(); 
	
	function __construct(){
		//Init. the available native filters.
		$this->native_filters = array(
			'broken' => array(
				'params' => array(
					'where_expr' => '( broken = 1 )',
					's_include_dismissed' => false,
				),
				'name' => __('Broken', 'broken-link-checker'),
				'heading' => __('Broken Links', 'broken-link-checker'),
				'heading_zero' => __('No broken links found', 'broken-link-checker'),
				'native' => true,
			 ), 
			 'redirects' => array(
			 	'params' => array(
					'where_expr' => '( redirect_count > 0 )',
					 's_include_dismissed' => false,
				),
				'name' => __('Redirects', 'broken-link-checker'),
				'heading' => __('Redirected Links', 'broken-link-checker'),
				'heading_zero' => __('No redirects found', 'broken-link-checker'),
				'native' => true,
			 ),

			'dismissed' => array(
				'params' => array(
					'where_expr' => '( dismissed = 1 )',
				),
				'name' => __('Dismissed', 'broken-link-checker'),
				'heading' => __('Dismissed Links', 'broken-link-checker'),
				'heading_zero' => __('No dismissed links found', 'broken-link-checker'),
				'native' => true,
			),
			 
			'all' => array(
				'params' => array(
					'where_expr' => '1',
				),
				'name' => __('All', 'broken-link-checker'),
				'heading' => __('Detected Links', 'broken-link-checker'),
				'heading_zero' => __('No links found (yet)', 'broken-link-checker'),
				'native' => true,
			 ), 
		);
		
		//Create the special "search" filter
		$this->search_filter = array(
			'name' => __('Search', 'broken-link-checker'),
			'heading' => __('Search Results', 'broken-link-checker'),
			'heading_zero' => __('No links found for your query', 'broken-link-checker'),
			'params' => array(),
			'use_url_params' => true,
			'hidden' => true,
		);
		
		//These search arguments may be passed via the URL if the filter's 'use_url_params' field is set to True.
		//They map to the fields of the search form on the Tools -> Broken Links page. Only these arguments
		//can be used in user-defined filters.
		$this->valid_url_params = array( 
 			's_link_text',
 			's_link_url',
 			's_parser_type',
 			's_container_type',
 			's_link_type',   
 			's_http_code',
 			's_filter',
		);
	}
	
	static function getInstance(){
		static $instance = null;
		if ( is_null($instance) ){
			$instance = new blcLinkQuery;
		}
		return $instance;
	}
	
  /**
   * Load and return the list of user-defined link filters.
   *
   * @return array An array of custom filter definitions. If there are no custom filters defined returns an empty array.
   */
	function load_custom_filters(){
		global $wpdb; /** @var wpdb $wpdb */
		
		$filter_data = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}blc_filters ORDER BY name ASC", ARRAY_A);
		$filters = array();
		
		if ( !empty($filter_data) ) {		
			foreach($filter_data as $data){
				wp_parse_str($data['params'], $params);
				
				$filters[ 'f'.$data['id'] ] = array(
					'name' => $data['name'],
					'params' => $params,
					'heading' => ucwords($data['name']),
					'heading_zero' => __('No links found for your query', 'broken-link-checker'),
					'custom' => true,
				);
			}
		}
		
		$this->custom_filters = $filters;
		
		return $filters;
	}
	
  /**
   * Add a custom link filter.
   *
   * @param string $name Filter name.
   * @param string|array $params Filter params. Either as a query string, or an array.
   * @return string|bool The ID of the newly added filter, or False.  
   */
	function create_custom_filter($name, $params){
		global $wpdb; /** @var wpdb $wpdb */
		
		if ( is_array($params) ){
			$params = http_build_query($params, null, '&');
		}
		
		//Save the new filter
		$q = $wpdb->prepare(
			"INSERT INTO {$wpdb->prefix}blc_filters(name, params) VALUES (%s, %s)",
			$name, $params
		);
		
		if ( $wpdb->query($q) !== false ){
			$filter_id = 'f'.$wpdb->insert_id;
			return $filter_id;
		} else {
			return false;
		}
	}
	
  /**
   * Delete a custom filter
   *
   * @param string $filter_id
   * @return bool True on success, False if a database error occured.
   */
	function delete_custom_filter($filter_id){
		global $wpdb; /** @var wpdb $wpdb */
		
		//Remove the "f" character from the filter ID to get its database key
		$filter_id = intval(ltrim($_POST['filter_id'], 'f'));
		
		//Try to delete the filter
		$q = $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_filters WHERE id = %d", $filter_id);
		if ( $wpdb->query($q) !== false ){
			return true;
		} else {
			return false;
		}
	}
	
	function get_filters(){
		$filters = array_merge($this->native_filters, $this->custom_filters);
		$filters['search'] = $this->search_filter;
		return $filters;
	}
	
  /**
   * Get a link search filter by filter ID.
   *
   * @param string $filter_id
   * @return array|null
   */
	function get_filter($filter_id){
		$filters = $this->get_filters();
		if ( isset($filters[$filter_id]) ){
			return $filters[$filter_id];
		} else {
			return null;
		}
	}
	
  /**
   * Get link search parameters from the specified filter. 
   *
   * @param array $filter
   * @return array An array of parameters suitable for use with blcLinkQuery::get_links()
   */
	function get_search_params( $filter = null ){
		//If present, the filter's parameters may be saved either as an array or a string.
		$params = array();
		if ( !empty($filter) && !empty($filter['params']) ){
			$params = $filter['params']; 
			if ( is_string( $params ) ){
				wp_parse_str($params, $params);
			}
		}
		
		//Merge in the parameters from the current request, if required
		if ( isset($filter['use_url_params']) && $filter['use_url_params'] ){
			$params = array_merge($params, $this->get_url_search_params());
		}
		
		return $params;
	}
	
  /**
   * Extract search query parameters from the current URL
   *
   * @return array
   */
	function get_url_search_params(){
		$url_params = array();
		foreach ($_GET as $param => $value){
			if ( in_array($param, $this->valid_url_params) ){
				$url_params[$param] = $value;
			}
		}
		return $url_params;
	}
	
	
	
  /**
   * A helper method for parsing a list of search criteria and generating the parts of the SQL query.
   *
   * @see blcLinkQuery::get_links() 
   *
   * @param array $params An array of search criteria.
   * @return array 'where_exprs' - an array of search expressions, 'join_instances' - whether joining the instance table is required. 
   */
	function compile_search_params($params){
		global $wpdb; /** @var wpdb $wpdb */
		
		//Track whether we'll need to left-join the instance table to run the query.
		$join_instances = false;
		
		//Generate the individual clauses of the WHERE expression and store them in an array.
		$pieces = array();
		
		//Convert parser and container type lists to arrays of valid values
		$s_parser_type = array();
		if ( !empty($params['s_parser_type']) ){
			$s_parser_type = $params['s_parser_type'];
			if ( is_string($s_parser_type) ){
				$s_parser_type =  preg_split('/[,\s]+/', $s_parser_type);
			}
		}
		
		$s_container_type = array();
		if ( !empty($params['s_container_type']) ){
			$s_container_type = $params['s_container_type'];
			if ( is_string($s_container_type) ){
				$s_container_type =  preg_split('/[,\s]+/', $s_container_type);
			}
		}
		
		//Don't include links with instances that reference invalid (not currently loaded) 
		//containers and parsers (unless specifically told to also include invalid links).
		if ( empty($params['include_invalid']) ){
			$join_instances = true;
			
			$module_manager = blcModuleManager::getInstance();
			$loaded_containers = array_keys($module_manager->get_active_by_category('container'));
			$loaded_parsers = array_keys($module_manager->get_active_by_category('parser'));
			
			if ( empty($s_parser_type) ){
				$s_parser_type = $loaded_parsers;
			} else {
				$s_parser_type = array_intersect($s_parser_type, $loaded_parsers);
			}
			
			if ( empty($s_container_type) ){
				$s_container_type = $loaded_containers;
			} else {
				$s_container_type = array_intersect($s_container_type, $loaded_containers);
			}
		}
		
		//Parser type should match the parser_type column in the instance table.
		if ( !empty($s_parser_type) ){
			$s_parser_type = array_map('trim', array_unique($s_parser_type));
			$s_parser_type = array_map('esc_sql', $s_parser_type);
			
			if ( count($s_parser_type) == 1 ){
				$pieces[] = sprintf("instances.parser_type = '%s'", reset($s_parser_type));
			} else {
				$pieces[] = "instances.parser_type IN ('" . implode("', '", $s_parser_type) . "')";
			}
			
			$join_instances = true;
		}
		
		//Container type should match the container_type column in the instance table.
		if ( !empty($s_container_type) ){
			//Sanitize for use in SQL
			$s_container_type = array_map('trim', array_unique($s_container_type));
			$s_container_type = array_map('esc_sql', $s_container_type);
			
			if ( count($s_container_type) == 1 ){
				$pieces[] = sprintf("instances.container_type = '%s'", reset($s_container_type));
			} else {
				$pieces[] = "instances.container_type IN ('" . implode("', '", $s_container_type) . "')";
			}
			
			$join_instances = true;
		}
		
		//A part of the WHERE expression can be specified explicitly
		if ( !empty($params['where_expr']) ){
			$pieces[] = $params['where_expr'];
			$join_instances = $join_instances || ( stripos($params['where_expr'], 'instances') !== false );
		}
		
		//List of allowed link ids (either an array or comma-separated)
		if ( !empty($params['link_ids']) ){
			$link_ids = $params['link_ids'];
			
			if ( is_string($link_ids) ){
				$link_ids = preg_split('/[,\s]+/', $link_ids);
			}
			
			//Only accept non-zero integers
			$sanitized_link_ids = array();
			foreach($link_ids as $id){
				$id = intval($id);
				if ( $id != 0 ){
					$sanitized_link_ids[] = $id;
				}
			}
			
			$pieces[] = 'links.link_id IN (' . implode(', ', $sanitized_link_ids) . ')';
		}
		
		//Anchor text - use LIKE search
		if ( !empty($params['s_link_text']) ){
			$s_link_text = like_escape(esc_sql($params['s_link_text']));
			$s_link_text  = str_replace('*', '%', $s_link_text);
			
			$pieces[] = '(instances.link_text LIKE "%' . $s_link_text . '%")';
			$join_instances = true;
		}
		
		//URL - try to match both the initial URL and the final URL.
		//There is limited wildcard support, e.g. "google.*/search" will match both 
		//"google.com/search" and "google.lv/search" 
		if ( !empty($params['s_link_url']) ){
			$s_link_url = like_escape(esc_sql($params['s_link_url']));
			$s_link_url = str_replace('*', '%', $s_link_url);
			
			$pieces[] = '(links.url LIKE "%'. $s_link_url .'%") OR '.
				        '(links.final_url LIKE "%'. $s_link_url .'%")';
		}
		
		//Container ID should match... you guessed it - container_id
		if ( !empty($params['s_container_id']) ){
			$s_container_id = intval($params['s_container_id']);
			if ( $s_container_id != 0 ){
				$pieces[] = "instances.container_id = $s_container_id";
				$join_instances = true;
			}
		}
			
		//Link type can match either the the parser_type or the container_type.
		if ( !empty($params['s_link_type']) ){
			$s_link_type = esc_sql($params['s_link_type']);
			$pieces[] = "instances.parser_type = '$s_link_type' OR instances.container_type='$s_link_type'";
			$join_instances = true;
		}
			
		//HTTP code - the user can provide a list of HTTP response codes and code ranges.
		//Example : 201,400-410,500 
		if ( !empty($params['s_http_code']) ){
			//Strip spaces.
			$params['s_http_code'] = str_replace(' ', '', $params['s_http_code']);
			//Split by comma
			$codes = explode(',', $params['s_http_code']);
			
			$individual_codes = array();
			$ranges = array();
			
			//Try to parse each response code or range. Invalid ones are simply ignored.
			foreach($codes as $code){
				if ( is_numeric($code) ){
					//It's a single number
					$individual_codes[] = abs(intval($code));
				} elseif ( strpos($code, '-') !== false ) {
					//Try to parse it as a range
					$range = explode( '-', $code, 2 );
					if ( (count($range) == 2) && is_numeric($range[0]) && is_numeric($range[0]) ){
						//Make sure the smaller code comes first
						$range = array( intval($range[0]), intval($range[1]) );
						$ranges[] = array( min($range), max($range) );
					}
				}
			}
			
			$piece = array();
			
			//All individual response codes get one "http_code IN (...)" clause 
			if ( !empty($individual_codes) ){
				$piece[] = '(links.http_code IN ('. implode(', ', $individual_codes) .'))';
			}
			
			//Ranges get a "http_code BETWEEN min AND max" clause each
			if ( !empty($ranges) ){
				$range_strings = array();
				foreach($ranges as $range){
					$range_strings[] = "(links.http_code BETWEEN $range[0] AND $range[1])";
				}
				$piece[] = '( ' . implode(' OR ', $range_strings) . ' )';
			}
			
			//Finally, generate a composite WHERE clause for both types of response code queries
			if ( !empty($piece) ){
				$pieces[] = implode(' OR ', $piece);
			}
			
		}

		//Dismissed links are included by default, but can explicitly included
		//or filtered out by passing a special param.
		if ( isset($params['s_include_dismissed']) ) {
			$s_include_dismissed = !empty($params['s_include_dismissed']);
			$pieces['filter_dismissed'] = $s_include_dismissed ? '1' : '(dismissed = 0)';
		}

		//Optionally sorting is also possible
		$order_exprs = array();
		if ( !empty($params['orderby']) ) {
			$allowed_columns = array(
				'url' => 'links.url',
			);
			$column = $params['orderby'];

			$direction = !empty($params['order']) ? strtolower($params['order']) : 'asc';
			if ( !in_array($direction, array('asc', 'desc')) ) {
				$direction = 'asc';
			}

			if ( array_key_exists($column, $allowed_columns) ) {
				$order_exprs[] = $allowed_columns[$column] . ' ' . $direction;
			}
		}
			
		//Custom filters can optionally call one of the native filters
		//to narrow down the result set. 
		if ( !empty($params['s_filter']) && isset($this->native_filters[$params['s_filter']]) ){
			$the_filter = $this->native_filters[$params['s_filter']];
			$extra_criteria = $this->compile_search_params($the_filter['params']);
			
			$pieces = array_merge($extra_criteria['where_exprs'], $pieces);
			$join_instances = $join_instances || $extra_criteria['join_instances'];			
		}
		
		return array(
			'where_exprs' => $pieces,
			'join_instances' => $join_instances,
			'order_exprs' => $order_exprs,
		);
	}
	
  /**
   * blcLinkQuery::get_links()
   *
   * @see blc_get_links()
   *
   * @param array $params
   * @return array|int
   */
	function get_links($params = null){
		global $wpdb; /** @var wpdb $wpdb */
		
		if( !is_array($params) ){
			$params = array();
		} 
		
		$defaults = array(
			'offset' => 0,
			'max_results' => 0,
			'load_instances' => false,
			'load_containers' => false,
			'load_wrapped_objects' => false,
			'count_only' => false,
			'purpose' => '',
			'include_invalid' => false,
			'orderby' => '',
			'order' => '',
		);
		
		$params = array_merge($defaults, $params);
		
		//Compile the search-related params into search expressions usable in a WHERE clause
		$criteria = $this->compile_search_params($params);
		
		//Build the WHERE clause
		if ( !empty($criteria['where_exprs']) ){
			$where_expr = "\t( " . implode(" ) AND\n\t( ", $criteria['where_exprs']) . ' ) ';
		} else {
			$where_expr = '1';
		}
		
		//Join the blc_instances table if it's required to perform the search.  
		$joins = "";
		if ( $criteria['join_instances'] ){
			$joins = "JOIN {$wpdb->prefix}blc_instances AS instances ON links.link_id = instances.link_id";
		}

		//Optional sorting
		if ( !empty($criteria['order_exprs']) ) {
			$order_clause = 'ORDER BY ' . implode(', ', $criteria['order_exprs']);
		} else {
			$order_clause = '';
		}
		
		if ( $params['count_only'] ){
			//Only get the number of matching links.
			$q = "
				SELECT COUNT(*)
				FROM (	
					SELECT 0
					
					FROM 
						{$wpdb->prefix}blc_links AS links 
						$joins
					
					WHERE
						$where_expr
					
				   GROUP BY links.link_id) AS foo";
			
			return $wpdb->get_var($q);
		}
		 
		//Select the required links.
		$q = "SELECT 
				 links.*
				
			  FROM 
				 {$wpdb->prefix}blc_links AS links
				 $joins
				
			  WHERE
				 $where_expr
				 
			   GROUP BY links.link_id

			   {$order_clause}"; //Note: would be a lot faster without GROUP BY
			   
		//Add the LIMIT clause
		if ( $params['max_results'] || $params['offset'] ){
			$q .= sprintf("\nLIMIT %d, %d", $params['offset'], $params['max_results']);
		}
		
		$results = $wpdb->get_results($q, ARRAY_A);
		if ( empty($results) ){
			return array();
		}
		
		//Create the link objects
		$links = array();
		
		foreach($results as $result){
			$link = new blcLink($result);
			$links[$link->link_id] = $link;
		}
		
		$purpose = $params['purpose'];
		/*
		Preload instances if :
			* It has been requested via the 'load_instances' argument. 
			* The links are going to be displayed or edited, which involves instances. 
		*/
		$load_instances = $params['load_instances'] || in_array($purpose, array(BLC_FOR_DISPLAY, BLC_FOR_EDITING));
		
		if ( $load_instances ){
			$link_ids = array_keys($links);
			$all_instances = blc_get_instances($link_ids, $purpose, $params['load_containers'], $params['load_wrapped_objects']);
			//Assign each batch of instances to the right link
			foreach($all_instances as $link_id => $instances){
				foreach($instances as $instance) { /** @var blcLinkInstance $instance */
					$instance->_link = $links[$link_id];
				}
				$links[$link_id]->_instances = $instances;
			}
		}

		return $links;
	}
	
  /**
   * Calculate the number of results for all known filters
   *
   * @return void
   */
	function count_filter_results(){
		foreach($this->native_filters as $filter_id => $filter){
			$this->native_filters[$filter_id]['count'] = $this->get_filter_links(
				$filter, array('count_only' => true)
			);
		}
		
		foreach($this->custom_filters as $filter_id => $filter){
			$this->custom_filters[$filter_id]['count'] = $this->get_filter_links(
				$filter, array('count_only' => true)
			);
		}
		
		$this->search_filter['count'] = $this->get_filter_links($this->search_filter, array('count_only' => true));
	}
	
  /**
   * Retrieve a list of links matching a filter. 
   *
   * @uses blcLinkQuery::get_links()
   *
   * @param string|array $filter Either a filter ID or an array containing filter data.
   * @param array $extra_params Optional extra criteria that will override those set by the filter. See blc_get_links() for details. 
   * @return array|int Either an array of blcLink objects, or an integer indicating the number of links that match the filter. 
   */
	function get_filter_links($filter, $extra_params = null){
		if ( is_string($filter) ){
			$filter = $this->get_filter($filter);
		}
		
		$params = $this->get_search_params($filter);
		

		if ( !empty($extra_params) ){
			$params = array_merge($params, $extra_params);
		}
		
		return $this->get_links($params);		
	}
	
	/**
	 * Print a menu of available filters, both native and user-created.
	 * 
	 * @param string $current Current filter ID.
	 * @return void
	 */
	function print_filter_menu($current = ''){
		$filters = $this->get_filters();
		
		echo '<ul class="subsubsub">';
    	
		//Construct a submenu of filter types
		$items = array();
		foreach ($filters as $filter => $data){
			if ( !empty($data['hidden']) ) continue; //skip hidden filters
															
			$class = '';
			$number_class = 'filter-' . $filter . '-link-count';
			
			if ( $current == $filter ) {
				$class = 'class="current"';
				$number_class .= ' current-link-count';
			}
			
			$items[] = "<li><a href='tools.php?page=view-broken-links&filter_id=$filter' {$class}>
				{$data['name']}</a> <span class='count'>(<span class='$number_class'>{$data['count']}</span>)</span>";
		}
		echo implode(' |</li>', $items);
		
		echo '</ul>';
	}
	
	/**
	 * Print the appropriate heading for the given filter. 
	 * 
	 * @param array $current_filter
	 * @return void
	 */
	function print_filter_heading($current_filter){
		echo '<h2>';
		//Output a header matching the current filter
		if ( $current_filter['count'] > 0 ){
			echo $current_filter['heading'] . " (<span class='current-link-count'>{$current_filter['count']}</span>)";
		} else {
			echo $current_filter['heading_zero'] . "<span class='current-link-count'></span>";
		}
		echo '</h2>';
	}
	
	/**
	 * Execute a filter.
	 * 
	 * Gathers paging and search parameters from $_GET and executes the specified filter.
	 * The returned array contains standard filter data plus several additional fields :
	 *  'filter_id'     - Which filter was used. May differ from the specified $filter_id due to fallback settings. 
	 * 	'per_page'      - How many results per page the method tried to retrieve.
	 * 	'page'          - Which page of results was retrieved.
	 * 	'max_pages'     - The total number of results pages, calculated using the above 'per_page' value.
	 *  'links'         - An array of retrieved links (blcLink objects).
	 *  'search_params' - An associative array of the current search parameters as extracted either from the current URL or the filter itself.
	 *  'is_broken_filter' - TRUE if the filter was set to retrieve only broken links, FALSE otherwise.
	 * 
	 * @param string $filter_id Filter ID.
	 * @param int $page Optional. Which page of results to retrieve. Defaults to returning the first page of results.
	 * @param int $per_page Optional. The number of results per page. Defaults to 30.
	 * @param string $fallback Optional. Which filter to use if none match the specified $filter_id. Defaults to the native broken link filter.
	 * @param string $orderby Optional. Sort results by this column.
	 * @param string $order Optional. Sort direction ('asc' or 'desc').
	 * @return array Associative array of filter data and the results of its execution.
	 */
	function exec_filter($filter_id, $page = 1, $per_page = 30, $fallback = 'broken', $orderby = '', $order = 'asc'){
		//The only valid sort directions are 'asc' and 'desc'.
		if ( !in_array($order, array('asc', 'desc')) ) {
			$order = 'asc';
		}
		
		//Get the selected filter (defaults to displaying broken links)
		$current_filter = $this->get_filter($filter_id);
		if ( empty($current_filter) ){
			$current_filter = $this->get_filter($fallback);
			$filter_id = $fallback;
		}
		
		//Page number must be > 0 
		if ($page < 1) $page = 1;
		
		//Links per page [1 - 500]
		if ($per_page < 1){
			$per_page = 30;
		} else if ($per_page > 500){
			$per_page = 500;
		}
		
		//Calculate the maximum number of pages.
		$max_pages = ceil($current_filter['count'] / $per_page);
		
		//Select the required links
		$extra_params = array(
			'offset' => ( ($page-1) * $per_page ),
			'max_results' => $per_page,
			'purpose' => BLC_FOR_DISPLAY,
			'orderby' => $orderby,
			'order' => $order,
		);
		$links = $this->get_filter_links($current_filter, $extra_params);
		
		//If the current request is a user-initiated search query (either directly or 
		//via a custom filter), save the search params. They can later be used to pre-fill
		//the search form or build a new/modified custom filter.
		$search_params = array(); 
		if ( !empty($current_filter['custom']) || ($filter_id == 'search') ){
			$search_params = $this->get_search_params($current_filter);
		}
		
		$base_filter = '';
		if ( array_key_exists($filter_id, $this->native_filters) ) {
			$base_filter = $filter_id;
		} else if ( isset($current_filter['params']['s_filter']) && !empty($current_filter['params']['s_filter']) ) {
			$base_filter = $current_filter['params']['s_filter'];
		} else if ( isset($_GET['s_filter']) && !empty($_GET['s_filter']) ) {
			$base_filter = $_GET['s_filter'];
		}

		$is_broken_filter = ($base_filter == 'broken');

		//Save the effective filter data in the filter array. 
		//It can be used later to print the link table.
		$current_filter = array_merge(array(
			'filter_id' => $filter_id,
			'page' => $page,
			'per_page' => $per_page,
			'max_pages' => $max_pages,
			'links' => $links,
			'search_params' => $search_params,
			'is_broken_filter' => $is_broken_filter,
			'base_filter' => $base_filter,
		), $current_filter);
		
		return $current_filter;
	}
}

/**
 * Retrieve a list of links matching some criteria.
 *
 * The function argument should be an associative array describing the criteria.
 * The supported keys are :  
 *     'offset' - Skip the first X results. Default is 0. 
 *     'max_results' - The maximum number of links to return. Defaults to returning all results.
 *     'link_ids' - Retrieve only links with these IDs. This should either be a comma-separated list or an array.
 *     's_link_text' - Link text must match this keyphrase (performs a fulltext search).
 *     's_link_url' - Link URL must contain this string. You can use "*" as a wildcard.
 *     's_parser_type' - Filter links by the type of link parser that was used to find them.
 *     's_container_type' - Filter links by where they were found, e.g. 'post'.
 *     's_container_id' - Find links that belong to a container with this ID (should be used together with s_container_type).
 *     's_link_type' - Either parser type or container type must match this.   
 *     's_http_code' - Filter by HTTP code. Example : 201,400-410,500
 *     's_filter' - Use a built-in filter. Available filters : 'broken', 'redirects', 'all'
 *     'where_expr' - Advanced. Lets you directly specify a part of the WHERE clause.
 *     'load_instances' - Pre-load all link instance data for each link. Default is false. 
 *     'load_containers' - Pre-load container data for each instance. Default is false.
 *     'load_wrapped_objects' - Pre-load wrapped object data (e.g. posts, comments, etc) for each container. Default is false.
 *     'count_only' - Only return the number of results (int), not the whole result set. 'offset' and 'max_results' will be ignored if this is set. Default is false.
 *     'purpose' -  An optional code indicating how the links will be used.
 *     'include_invalid' - Include links that have no instances and links that only have instances that reference not-loaded containers or parsers. Defaults to false. 
 *
 * All keys are optional.
 *
 * @uses blcLinkQuery::get_links();
 *
 * @param array $params
 * @return int|blcLink[] Either an array of blcLink objects, or the number of results for the query.
 */
function blc_get_links($params = null){
	$instance = blcLinkQuery::getInstance();
	return $instance->get_links($params);
}