From 6f787f2422a4b1e82ba4279b3e264be4af436419 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 4 May 2019 10:05:21 +0200 Subject: [PATCH 01/18] Move mod/friendica to src/Module/Friendica --- mod/friendica.php | 141 ------------------------------ src/App/Router.php | 3 +- src/Model/User.php | 15 ++++ src/Module/Friendica.php | 162 +++++++++++++++++++++++++++++++++++ view/templates/friendica.tpl | 45 ++++++++++ 5 files changed, 224 insertions(+), 142 deletions(-) delete mode 100644 mod/friendica.php create mode 100644 src/Module/Friendica.php create mode 100644 view/templates/friendica.tpl diff --git a/mod/friendica.php b/mod/friendica.php deleted file mode 100644 index 4942e4c8f5..0000000000 --- a/mod/friendica.php +++ /dev/null @@ -1,141 +0,0 @@ -argv[1]) && ($a->argv[1] == "json")) { - $register_policies = [ - Register::CLOSED => 'REGISTER_CLOSED', - Register::APPROVE => 'REGISTER_APPROVE', - Register::OPEN => 'REGISTER_OPEN' - ]; - - $register_policy_int = intval(Config::get('config', 'register_policy')); - if ($register_policy_int !== Register::CLOSED && Config::get('config', 'invitation_only')) { - $register_policy = 'REGISTER_INVITATION'; - } else { - $register_policy = $register_policies[$register_policy_int]; - } - - $condition = []; - $admin = false; - if (!empty(Config::get('config', 'admin_nickname'))) { - $condition['nickname'] = Config::get('config', 'admin_nickname'); - } - if (!empty(Config::get('config', 'admin_email'))) { - $adminlist = explode(",", str_replace(" ", "", Config::get('config', 'admin_email'))); - $condition['email'] = $adminlist[0]; - $administrator = DBA::selectFirst('user', ['username', 'nickname'], $condition); - if (DBA::isResult($administrator)) { - $admin = [ - 'name' => $administrator['username'], - 'profile'=> System::baseUrl() . '/profile/' . $administrator['nickname'], - ]; - } - } - - $visible_addons = Addon::getVisibleList(); - - Config::load('feature_lock'); - $locked_features = []; - $featureLock = Config::get('config', 'feature_lock'); - if (isset($featureLock)) { - foreach ($featureLock as $k => $v) { - if ($k === 'config_loaded') { - continue; - } - - $locked_features[$k] = intval($v); - } - } - - $data = [ - 'version' => FRIENDICA_VERSION, - 'url' => System::baseUrl(), - 'addons' => $visible_addons, - 'locked_features' => $locked_features, - 'explicit_content' => (int)Config::get('system', 'explicit_content', false), - 'language' => Config::get('system','language'), - 'register_policy' => $register_policy, - 'admin' => $admin, - 'site_name' => Config::get('config', 'sitename'), - 'platform' => FRIENDICA_PLATFORM, - 'info' => Config::get('config', 'info'), - 'no_scrape_url' => System::baseUrl().'/noscrape' - ]; - - header('Content-type: application/json; charset=utf-8'); - echo json_encode($data); - exit(); - } -} - -function friendica_content(App $a) -{ - $o = '

Friendica

' . PHP_EOL; - $o .= '

'; - $o .= L10n::t('This is Friendica, version %s that is running at the web location %s. The database version is %s, the post update version is %s.', - '' . FRIENDICA_VERSION . '', System::baseUrl(), '' . DB_UPDATE_VERSION . '', - '' . Config::get("system", "post_update_version") . ''); - $o .= '

' . PHP_EOL; - - $o .= '

'; - $o .= L10n::t('Please visit Friendi.ca to learn more about the Friendica project.') . PHP_EOL; - $o .= '

' . PHP_EOL; - - $o .= '

'; - $o .= L10n::t('Bug reports and issues: please visit') . ' ' . ''.L10n::t('the bugtracker at github').''; - $o .= '

' . PHP_EOL; - $o .= '

'; - $o .= L10n::t('Suggestions, praise, etc. - please email "info" at "friendi - dot - ca'); - $o .= '

' . PHP_EOL; - - $visible_addons = Addon::getVisibleList(); - if (count($visible_addons)) { - $o .= '

' . L10n::t('Installed addons/apps:') . '

' . PHP_EOL; - $sorted = $visible_addons; - $s = ''; - sort($sorted); - foreach ($sorted as $p) { - if (strlen($p)) { - if (strlen($s)) { - $s .= ', '; - } - $s .= $p; - } - } - $o .= '
' . $s . '
' . PHP_EOL; - } else { - $o .= '

' . L10n::t('No installed addons/apps') . '

' . PHP_EOL; - } - - if (Config::get('system', 'tosdisplay')) - { - $o .= '

'.L10n::t('Read about the Terms of Service of this node.', System::baseurl()).'

'; - } - - $blocklist = Config::get('system', 'blocklist', []); - if (!empty($blocklist)) { - $o .= '

' . L10n::t('On this server the following remote servers are blocked.') . '

' . PHP_EOL; - $o .= '' . PHP_EOL; - foreach ($blocklist as $b) { - $o .= '' . PHP_EOL; - } - $o .= '
' . L10n::t('Blocked domain') . '' . L10n::t('Reason for the block') . '
' . $b['domain'] .'' . $b['reason'] . '
' . PHP_EOL; - } - - Hook::callAll('about_hook', $o); - - return $o; -} diff --git a/src/App/Router.php b/src/App/Router.php index 6612936e1b..b1d8d3ade2 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -94,6 +94,7 @@ class Router $collector->addRoute(['GET'], '/{id:\d+}[/posts|conversations]', Module\Contact::class); }); $this->routeCollector->addRoute(['GET'], '/credits', Module\Credits::class); + $this->routeCollector->addRoute(['GET'], '/directory', Module\Directory::class); $this->routeCollector->addGroup('/feed', function (RouteCollector $collector) { $collector->addRoute(['GET'], '/{nickname}', Module\Feed::class); $collector->addRoute(['GET'], '/{nickname}/posts', Module\Feed::class); @@ -101,11 +102,11 @@ class Router $collector->addRoute(['GET'], '/{nickname}/replies', Module\Feed::class); $collector->addRoute(['GET'], '/{nickname}/activity', Module\Feed::class); }); - $this->routeCollector->addRoute(['GET'], '/directory', Module\Directory::class); $this->routeCollector->addRoute(['GET'], '/feedtest', Module\Feedtest::class); $this->routeCollector->addRoute(['GET'], '/filer[/{id:\d+}]', Module\Filer::class); $this->routeCollector->addRoute(['GET'], '/followers/{owner}', Module\Followers::class); $this->routeCollector->addRoute(['GET'], '/following/{owner}', Module\Following::class); + $this->routeCollector->addRoute(['GET'], '/friendica[/json]', Module\Friendica::class); $this->routeCollector->addGroup('/group', function (RouteCollector $collector) { $collector->addRoute(['GET', 'POST'], '[/]', Module\Group::class); $collector->addRoute(['GET', 'POST'], '/{group:\d+}', Module\Group::class); diff --git a/src/Model/User.php b/src/Model/User.php index c575b44d38..8945faeaef 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -129,6 +129,21 @@ class User } } + /** + * Get a user based on it's email + * + * @param string $email + * @param array $fields + * + * @return array|boolean User record if it exists, false otherwise + * + * @throws Exception + */ + public static function getByEmail($email, array $fields = []) + { + return DBA::selectFirst('user', $fields, ['email' => $email]); + } + /** * @brief Get owner data by user id * diff --git a/src/Module/Friendica.php b/src/Module/Friendica.php new file mode 100644 index 0000000000..067a986a27 --- /dev/null +++ b/src/Module/Friendica.php @@ -0,0 +1,162 @@ +getConfig(); + + $visibleAddonList = Addon::getVisibleList(); + if (!empty($visibleAddonList)) { + + $sorted = $visibleAddonList; + sort($sorted); + + $sortedAddonList = ''; + + foreach ($sorted as $addon) { + if (strlen($addon)) { + if (strlen($sortedAddonList)) { + $sortedAddonList .= ', '; + } + $sortedAddonList .= $addon; + } + } + $addon = [ + 'title' => L10n::t('Installed addons/apps:'), + 'list' => $sortedAddonList, + ]; + } else { + $addon = [ + 'title' => L10n::t('No installed addons/apps'), + ]; + } + + $tos = ($config->get('system', 'tosdisplay')) ? + L10n::t('Read about the Terms of Service of this node.', $app->getBaseURL()) : + ''; + + $blockList = $config->get('system', 'blocklist'); + + if (!empty($blockList)) { + $blocked = [ + 'title' => L10n::t('On this server the following remote servers are blocked.'), + 'header' => [ + L10n::t('Blocked domain'), + L10n::t('Reason for the block'), + ], + 'list' => $blockList, + ]; + } else { + $blocked = null; + } + + $hooked = ''; + + Hook::callAll('about_hook', $hooked); + + $tpl = Renderer::getMarkupTemplate('friendica.tpl'); + + return Renderer::replaceMacros($tpl, [ + 'about' => L10n::t('This is Friendica, version %s that is running at the web location %s. The database version is %s, the post update version is %s.', + '' . FRIENDICA_VERSION . '', + $app->getBaseURL(), + '' . DB_UPDATE_VERSION . '', + '' . $config->get("system", "post_update_version") . ''), + 'friendica' => L10n::t('Please visit Friendi.ca to learn more about the Friendica project.'), + 'bugs' => L10n::t('Bug reports and issues: please visit') . ' ' . '' . L10n::t('the bugtracker at github') . '', + 'info' => L10n::t('Suggestions, praise, etc. - please email "info" at "friendi - dot - ca'), + + 'visible_addons' => $addon, + 'tos' => $tos, + 'block_list' => $blocked, + 'hooked' => $hooked, + ]); + } + + public static function rawContent() + { + $app = self::getApp(); + + // @TODO: Replace with parameter from router + if ($app->argc <= 1 || ($app->argv[1] !== 'json')) { + return; + } + + $config = $app->getConfig(); + + $register_policies = [ + Register::CLOSED => 'REGISTER_CLOSED', + Register::APPROVE => 'REGISTER_APPROVE', + Register::OPEN => 'REGISTER_OPEN' + ]; + + $register_policy_int = intval($config->get('config', 'register_policy')); + if ($register_policy_int !== Register::CLOSED && $config->get('config', 'invitation_only')) { + $register_policy = 'REGISTER_INVITATION'; + } else { + $register_policy = $register_policies[$register_policy_int]; + } + + $condition = []; + $admin = false; + if (!empty($config->get('config', 'admin_nickname'))) { + $condition['nickname'] = $config->get('config', 'admin_nickname'); + } + if (!empty($config->get('config', 'admin_email'))) { + $adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email'))); + $condition['email'] = $adminList[0]; + $administrator = User::getByEmail($adminList[0], ['username', 'nickname']); + if (!empty($administrator)) { + $admin = [ + 'name' => $administrator['username'], + 'profile' => $app->getBaseURL() . '/profile/' . $administrator['nickname'], + ]; + } + } + + $visible_addons = Addon::getVisibleList(); + + $config->load('feature_lock'); + $locked_features = []; + $featureLocks = $config->get('config', 'feature_lock'); + if (isset($featureLocks)) { + foreach ($featureLocks as $feature => $lock) { + if ($feature === 'config_loaded') { + continue; + } + + $locked_features[$feature] = intval($lock); + } + } + + $data = [ + 'version' => FRIENDICA_VERSION, + 'url' => $app->getBaseURL(), + 'addons' => $visible_addons, + 'locked_features' => $locked_features, + 'explicit_content' => intval($config->get('system', 'explicit_content', 0)), + 'language' => $config->get('system', 'language'), + 'register_policy' => $register_policy, + 'admin' => $admin, + 'site_name' => $config->get('config', 'sitename'), + 'platform' => FRIENDICA_PLATFORM, + 'info' => $config->get('config', 'info'), + 'no_scrape_url' => $app->getBaseURL() . '/noscrape', + ]; + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($data); + exit(); + } +} diff --git a/view/templates/friendica.tpl b/view/templates/friendica.tpl new file mode 100644 index 0000000000..0fa9eac5b3 --- /dev/null +++ b/view/templates/friendica.tpl @@ -0,0 +1,45 @@ +

Friendica

+
+

{{$about nofilter}}

+
+

{{$friendica nofilter}}

+
+

{{$bugs nofilter}}

+
+

{{$info nofilter}}

+
+ +

{{$visible_addons.title nofilter}}

+{{if $visible_addons.list}} +
{{$visible_addons.list nofilter}}
+{{/if}} + +{{if $tos}} +

{{$tos}}

+{{/if}} + +{{if $block_list}} +
+

{{$block_list.title nofilter}}

+
+ + + + + + + + + {{foreach $block_list.list as $blocked}} + + + + + {{/foreach}} + +
{{$block_list.header[0] nofilter}}{{$block_list.header[1] nofilter}}
{{$blocked.domain nofilter}}{{$blocked.reason nofilter}}
+
+ +{{/if}} + +{{$hooked}} From 3fca00d1d5d2f764fe2b1c9e2267b5ade22298f4 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 4 May 2019 10:17:04 +0200 Subject: [PATCH 02/18] forgot nofilter --- view/templates/friendica.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/templates/friendica.tpl b/view/templates/friendica.tpl index 0fa9eac5b3..3beabda367 100644 --- a/view/templates/friendica.tpl +++ b/view/templates/friendica.tpl @@ -15,7 +15,7 @@ {{/if}} {{if $tos}} -

{{$tos}}

+

{{$tos nofilter}}

{{/if}} {{if $block_list}} @@ -42,4 +42,4 @@ {{/if}} -{{$hooked}} +{{$hooked nofilter}} From 28d6441d695bfe74947605b1099ba4fb483a38a5 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 4 May 2019 10:18:41 +0200 Subject: [PATCH 03/18] add PHP doc --- src/Module/Friendica.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Module/Friendica.php b/src/Module/Friendica.php index 067a986a27..3901a0bc77 100644 --- a/src/Module/Friendica.php +++ b/src/Module/Friendica.php @@ -9,6 +9,10 @@ use Friendica\Core\L10n; use Friendica\Core\Renderer; use Friendica\Model\User; +/** + * Prints information about the current node + * Either in human readable form or in JSON + */ class Friendica extends BaseModule { public static function content() From c969635bbcfba969033690b801594aae59ace3ef Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 4 May 2019 21:54:05 -0400 Subject: [PATCH 04/18] Add style to exception page --- images/friendica-404_svg_flexy-o-hare.png | Bin 0 -> 14553 bytes ...ndica-404_svg_hare-bottom-light-inside.png | Bin 0 -> 6031 bytes src/Module/Special/HTTPException.php | 6 ++--- view/global.css | 22 ++++++++++++++++-- view/templates/exception.tpl | 2 ++ view/theme/frio/css/style.css | 7 ++++++ 6 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 images/friendica-404_svg_flexy-o-hare.png create mode 100644 images/friendica-404_svg_hare-bottom-light-inside.png diff --git a/images/friendica-404_svg_flexy-o-hare.png b/images/friendica-404_svg_flexy-o-hare.png new file mode 100644 index 0000000000000000000000000000000000000000..36d6b5ca3d0e50db24ab6b1c3fe2acb66c1d8291 GIT binary patch literal 14553 zcmV;~I3~x5P)Z7OD7Vzjf*S-k1wf&sx73jmZRweYb^&o13wZ2dZ zKA=|=qKG^c8BCBjnS>EwISt<6+4bViE*p8@QO2lA$U|gZsIH zZp+YBL|FzGOHM3cV5OTN!&prL^JDWuxX|?z6N^<+z?!g$AzWrawKPp!KRtkzqKlZh z@(PA(F^OdUN;2@?LZ-7GEEqO3v|MPZ#Aq3&>Jcs;P*u^FVAZn5gG#J&%pRiU(h5}>vsPtz8#QSWS}(L*TEJS&Vykoz z#6pd9+sJ+f7Z--HR0)V3OELCB%M}kQAA_pK;n&^3U}{Lv*q5+Ml2sfb17KHRvqQ_J z2G#}#yMYkzz%Y?IJsrE8ue*sJyBwRHc8z*)lTW_)M}9P`G40@D@f9>Mp98<2I)Eyb zigjkeHlS-L6rc(Q(QMYv%91=NC0CvT*Wg&V91IpfwTxForuQMt>AaTEb6SBwZ7N}I z@`4n-0(pe^YP(rBR~mxF6)MHO*g*L7<3XHPoxv6gfLbIdynyd~8HKFQ)^M>{W1oY- zYGhVHNM91YAFZ731{4#RY)loL_g9tyVAV3uXQ_qWj}+F>b6L(nH&tF0u&UV8 z#_rb2%sDIO3HZ_*(-bOr5b^9Al9Y8;CteMZh_2qvvRv;fS(aQ-*q%P9Ea*olju`pbEU|A=$pp zesZ`cL_TWoBmX?nOODW=O%TK=)fUXKR3l)YJ;TFB zt{9m^9-ldk+)|t`;v69pSZNVhH3SEK#sf=!7}q%T669ouPbgf}YC!|4N_A(qjLjn} zFDQ!ZP8x6nE3K+#P!uVPkk8>@EoNXj{lOy?Fxhfd@!DZ&ftUPZYGJB?fypklcm~_W z0wE}KC{&QNs3++&+(;;3GF%II?oaw|Jo74y}jqTRp^JRijA4wzfilN12E zqohFZpF!W%5*ys?ETIo}4l~ zvlWRCz1AYY?x~+dmZ1gcn)Ife9iC=SRnL&zY=( z{UOrX(L;K=`$#X{`ujqpi~b+f9_X^-^8DoVTpV5 zL~1sxY?RCatFbUqEXn%YL{{CW6o1KZfnUAK1j}X1wX0Yq#;SPk-HBrzr1fY=y1&d5CP={F(HMVA&rm=>ZU*RB2YJ z-t1x2crKUA<)?sEvZr~fgUTBJaj1KVofjYIJlVbUa7zH8vO$c+spGazV1i>6(YcUe z+$sTP#o$D13U=6m3^Y^p0?;-cn@rYxelh)dtktJq znriJ{g72I6B(8?6mkYsio&cnq$89Dz(aop;vWIqEE4^AEZo@h(at$@bUam9;OJwHA z5~%K<@`CirO90mowk{yeU1yke8DOo-Mb5fpQW%ibdb!gWteSKI3*5iDul#}3U;2n< zu-ZEaX*m?u1nldT52j=V4OlHR3JMwp??36#Bv+b(C3K9M+13icN)%54NKd~UB%8N| z$mXVy^#530T;?WMU*;xrE_IW!XR3egFC8o+i>MV7nD~GsMx}}EfY-`2@sw~-rI)0q z47&ra_G84gcPnunX_kIgDqJ{%ghv&V@Wh$Y&!Pp^=Y8bc%JlX1z3;N1u0nlhW8I(tzDT-rcD~$1ls!Y$v57zR%*m#&-XYS~4+Pux#DuRNg{tbxB<`=8R^k)AHozyg==>f3u$ z0!z7{wJKH*T+rued+@(E54LKi5fs#JYcaEgtNH7hn)$ zB?9ZlMZILlr(sfDd^TDBn_rTTKixuFj_#7SS2i?|d;k3&Qdp#Y&d4Rd?3dg{HNfh= z@_Q+n!a;SS`m~bBjADnHjBpFbfA`mGPmR6s22Eg5ivwVQ=HFaRF23Z;WGrp4Qhc)M zU9$G6deYo{K=ZM~Bgc{c8~=ll9OLulLqmSO^5;^OMIx{OpnLDXC+>cBe6)+)Salm| zZ_`%o4=&=XPh_hmIv`bFO21K{4yjmdPYsr4A!*s&m(_- zc@4Rd!UaDUk2_o10JJ;4vp}){jc>fA%}wlTBX0jkBy@rCWY`ZTzamvzsOi@G`^eUh z!=(1-KP2D#!JUa4)Tj~U=#gV&>-!&QI&M!dk)WMFcW`q6(A#MY_I|73Wt_okt+uLN z6kKup9Ad&$d7W=Ou_kTZ!muanuKuB;le=--UX{QCYxT;8`T=XDraQiK8@YP!mD=Ml z8nc0`Y1C@nIq?nE-#z|JP;%YiT;QBE#Z+zj#;C#m*T%^@-Z$k1Rcoq2(4V!;;-+DS zYgnZ=6?R&{HT|oXj|zKpZs{MA7%qsdJdICDRthYD7J((x6@zVV(3KdGb=Sn#Rr|8Q z9h8$7%N~3{)4sqheEFrM0agnDg9kz)D=X_23YDICOR45V;kecd z&QE7@1FV)E=A|@i1jp>Ogo|3OZ-qVCB$Qo{&}D=4Ib_)&rqTkI(MAf#flaIAp(1Kp z`%1Vm9OISd`nib3+me^g} z@s9L5R)d@O_#gjF{^d(oQ1@!5;q#J@r$zDllOV~^%<2Qd)dS!M!AC7CKm{8Bma5>G zu&D_uIP`>(HfbVe!BxgaK@Z6B*k3Y6r0xOj#{{UPc zzfFP_w`fYupI_7*H_4z(P+VPEMXtW)O38}t*uG2ZGEkl?WH%vE)BO1X>K?vk_}pK4 zJ4B{U8k{-QS&a{6N0u)K1ycynMsL>Pz+_tF4h8pnXae19c2NNAX;*=Yq=*yEK3#3f2*#AfdxNf9qm0WsMlzxkyUmx0T* zYQ6?@G3n06rMu(>qpXnm;@^gtw_CO3NmylH&Tf8lE4KM$Ea!SnE0ii zkNoPl(gv}i60nxf57-4P`AP1xNlM=IxXaOSh28FHnm#vjoO-)w88~R5S6_35WThZp zfrJ30wT)Cc7*V`&Q)*+a20s9H3OT1~lhj>8s58_laK{=mG466UT%PQanm(A5FS%y% zhL%7B71c=Nfvprw4g=OJHYI=crEaNWz1`%+R=^!CRPnN55T$_ul$i$CK-}f*PGpz6 zL~|pbIMe*|gFY^eLr`EYK{d^})ck=90JWM7PQ#Xovl~m+%h7PTLj9w(el|4i64TEw zqyy-e4$e@0|K5e>!*$1v9$gOr4KYZjX}06x3T5SMo@@`?Gg0$h@BkWIL$$^W0~qhP z!92k3UFcOEGzsSy?*V{Pb%oP+-a<76ufE;9c#wju7v{(IUJF$lty0fMAP=;A8z2cmJ75s)ESpavKu7an=QD;zAC4NBc*Elb5}Wg=YCd@gAN zm%F!9oAvsad73hKsi^eyRG^E-<8tdgAxri_5v4?Yx08bGaguQ4uWUkO=pdFXW~_Z zRe_nBc+Q$D&;AtRkn#uj@F&}ERFA)CO!UNqS*kGT5m>^tE3;H*-jywufQdmfD(*^~ z>8!mc50hkbXbghk)@6~Ipx9V1qt+@k^J|j3$y1OygldSDGgl9qTLb!`{<$VcngReu zDmNd}Ud0YmS>HIJe$|)8k#R9M`m6X8LuV6#UzowuV&iF zVRFvcX#l|5fE9xchKRrb#~q&xUW5TYH|-pN$)%~v9l_%Gz)zbC={RiMrK2%Y|BX{3 zi!i|a7tOm>OQp zVfX1v@SJKT|F?J|S@Okxa&7BT&HEV|3`(De4XoIACrp)qvomC{)GEv)2v!VcWtv8T zi#O3tv=z*k+&_8YaT8oXKf>ii%jNd&h`N}QhsntyqbXn$$nvwsQww&2TBwucOP%cl z7Au*+t%T2>%*(g^^#d4?^iWn$$Sxx!K*q5o2qY=svtXSpuu%IEE(glp*L5_Ml^S_XqsAD^p5DYDxW$eVrd? zGF+|{;i_jA%Fis243McAqRHR**CRCU8b}LvkGVkFtP*r$Lrj&(dQ?p*=bANyKDs*( zAJE+85KoVsHJ1z=OF6ak<_bW+RR4sgsbfo0NR&6I))msH>yuv(%1E~~*(#QN!( z&@f5=pxXdk@Z~}wui6k4rK>1?mb!)mI`X+RJ8ro6ToM8|F@Mvx9h!uOlSDEC*D($5y$^Ne7mTmv3KPe+4IP7U3{1Te`U&0w!3ieLNLw!%Ghs4siMIW_R6 z-0#(U#zfLJUjIav(#j+{X3IqplGpOS1Xv^n?%TZc#lP8QH3&m9{+>U&fD{+yNUyTq z`MixZ2YN_zTaPp0V(EQ=XdZj@DL`r@k<@-uY(Loash1b@77m>sc6$ei$O<$Q*Vr#5 zUUgCi>geZ@b&Pwuq?jvgvoT$E7F5M#AK!cWjWGG0jZROKsAFk)Rq86juzj;<<>e*!wpWR(=aiI`fj&KHZLpXXHMIp! z(N!(d&)`zC{y?>=mm#05mqsK+{vdQT=h&Xo?Qg!(X@~nV>cLInL_p1*Je=Hg!N@2J zrBwjdq8Ij(m-d{n4_qv6QUWDgC+Htr&0x~qS2pdcdF6eDXAhcfM(R`!yQGyKr6K(A zN*kUu@|ebB5>zG)+vg&HN2kjgn5#q|tCGH_Z zAqD^A-Sv+>XqQ_>;o3lAO6|2`|LDcsj5dmK$XzEfUs zfpz-XRV8HBIR4pFfD0fq6)xuTEr< znhI#LMPqSfEXdFmk!t6-GFb$0oi@{w{d}LVm~>9iv0M~& zf4tBCM%R3XsR}Gl@Zxz%6Dw{8QKEPe&KoLFT+O?pu0bwTHd=`SDR-zJlvD50d;Q&) z?q2ugJ35nAGHiA1q3ag_SIOZYUcAq&@9)CjZIx5?4K7ZN9G+6E6lqUkR!Rk`I<@1R zJMY`;!rt8LV*j>l{4COYhPIAfPvcXP)v`jN@~=sdC2aTwBwtX1AO@TalU#r*yMzdWYtvm163G#E$=DcYRWmf>r$hscjnS7 zty-npSU#t~_3&FQW;5dm|hrk zvo*$F9_kWmok#vm-w5)JSyz%v&MPOQeTipSIvhAoUfK2zd2Z8dq@z1=<$AE<7Ricf zvb6tPM`OcRrRtIpwR#~v9pTjaxs~x6bf(8-R-qVr$e#7ezY5UzzVh)a&+Z{M7G-Nt zI$khb4M%=7tJ>BR)UfnDSgOU$DlX9r@VQmD|03+kd0mF~Do&WsL# zM&ZgHK9ppQ7$Id~NjTK!?(MvG*9-skDzUjJT+n`&n+VrJ3Rj)W#$eU38v=l>U@o8` zS5w*dr57-0q;g!N=KlSA^|z(hRP@kut0YUN8hcUeV`P;|NXx@{OYWY$5{m)I;JUN6 zzfL^cH;^D3W#Bz?4DtGW5AS`u;eO%(QMkbKF(s+-%{4pyPu)DVRQe8V2$j+W1lMn2 zs!5UQU%kAbXXKQ(iAtTBCJUx)s7zOvUqZfn%}tW!;-T+6x@>SPMnQm>E0w-EmvRLc z1CT-DB7gs8DKjYbWgTcH{f7<)*h8<0B_Zk@02s8X8WXaG%ih1|OSQIxi-J|l+(6I@ z`lT)5weQK--wlmnLp;E`N@gWZa!3h)t?Nn6__5@+%ImmZJGh4b_T)oR?jXclygGrw z0)x9K_0?%uQw(4NsFYbja0Q9WeOKqk*Bh9tzKB5vF8gZsods+PmycO5pQvLzzw>zD z&iXwtPh-F~aK)tZ{mfc5@;;6PSUB{mJkMclk^_dPKwnrg4WW1K((Ev~EGtaP=r)AY zT?Fg(N&ZM0Ctbp2h00`*MHXNmocMr@pfr1vq8|&A7oJmrZ|{8ZZ}lqfyWF=arPE0y zTkN+XU_$59c>KLXOYXdA%!+{rl+CPF0|QIg(+^z4-eILLhP^poRShvlO_Ao`&{N5A zW?vU68mXz>><=myja&Vu#OaTLT_6yrSGN0#n@t!HGV=Q8#vG!gU=9`=S~V% zK;!pnd&mUDUvO_qT`pI^a<~{+8x%qDLbD4xLFZlb&FWCjkl%*g-m}zx-#>J;WW~}I zGMK4Ja{s7{*t2&JfnsL>E9cy)#4}=Kz}ai3&l0}Lb$5xP0z-rjTod;Nz z6s$q*H!fUIlSLvu=E@QkxG@k5LD6M{9Jr+bRvoigmIc93$r1KuuMB17Umx!?09nt- zDVpLjyUX3%Nn@&=5?C=Ym6U4l+5NFJgv8}>uMY=9^Hce5SU=@rh1+i{$S7bH46qX3 zV8w-tf>j%HL*fZ;)H(`TgN~cMja|$_JEd@^&Y52s%=7(@TCwxueFogcKHpdgGACIx z01C2xFce?8%#w=y;r7-acWv8x4*UKC?0ZV7@`iPK6NiGU0o&WOQjYGhTTKCQdXd4aDqYwDroF9uBub{t^kabI`L!nxm@812~S|@ zgsdjUQ;P@HN(xnD((fV0V)=?bcv^b2X)SJz0~&lut0`!XTKJ5)!myQPweFvn)&50= zG|5ej`;V$z1PFY<=-6-t4T13C5!SSGVl{=b@|xUzr`82#-r8s+vm3|StYV3YCT4N`h>=0K1OgV-5J)7& z-$qU^j7P}nh4f_iP0Nz5wh?6M05Y(2vO5Q_yx~*EdGNF3W_q&WK34O9N(Hi7aypxs zmM$H4xTemz;XC~WqyC`wK~6CkD#pc>lOL;EF=P7-03xZHN6=VxohE z1!`}ynS9D);-3hO)Qy1&N??9)h$&ExkdFu&YQg%w${APy+BOCpTCQXtnE1bcT;=IH zAx}a>)RG-w$&~#zDBDV!+d;&RStd~)jEad4qN6JQ-0Hi&7k0Uq#!gj+kU2_z3RkJ9 zoySykNH9BL3II{kyoTuUUGHx>Yh>Z-us6p|T(0wp+mjQuqf@QY2v(_FC>$c;tUSyt z+R!~LxZ-wK=NnH5u_!0Ij(5{-U19!+5@x}w=+FDj_ zaBp<&pVb8G=ECxx zYd$<0wF10_G9@*SmMcva_T*e7YTresu~k0HfHIY~bcpMe$TVS`oRVX4ne z``fLyUZscxKe;msF1P2x*qDol0FW(rmy@4eRiX*3L2e_paP{AsCjC7?YLQCVWUwfN z%aAH~?hHVxzm*>p0O@nzni|)YG(n5l1VAW+OS>l2EE@z?DhUlpQoMfq1(Ma_9j{pk zm^@?zg>dP((B7`H!EhOCz>ur}H%^ob2m7@Uuo~7KfLhGr&?F@BYgyS+=NxTCXa^1L4vn8ec`lT#4pp zPvokZt@|z;Yn6Mr9FrReS5m?1C8^GE3oe>fX>!O$S|B*+VnhVtN+{pfzm%w0GR=QK zck*y)vr3SWgjml4En>?mp%5;0^`0@YVlZ6VT`h|&N0cD;vcPg;Y98TAELg5Il}2r( z>VmiAaw!W`MXgr_qJnU#Dwta|+vwlkdC{4+xehoNi{d`CUI>@EU|iQA;54M-A3Mas%NaY9I}AYqX@|Eo#U~fwxFYF*T2H4YphcmR5znB?}8^6UJT^ z0fxy9gezJ`>*Or75J`3O1)~iAt-MN2(FadU(~s*^4JJ1buG5d(t$$HiF_c~4cLn=iQoaYP(ijjXHxMrE3wcMmUE>RFe_dc%4J2HaQA<^; ze8WK8C3)NE9wJeceAK;ZPq3>xIdUc=3KFUUV=&aT?x;np-HV3KZsLlWAuSi`vhQFIvgP6GxLL7kpLvJx9y6PVLRC zz~{f}J!8@xVYl}z_TofK41>zRMO za**}Ay>6ql&)DB3dLV$cO*G$pygA^vzxhVzxm6SHqCdU>|A)8>Qu95drWj3bR3lOu z6m!w|N^|<}{89Q`GO#p8z>1ropf2GPHM6CC!oE4hU5$x;xx3-{pSaifNN1lCJhT@q>z3-&e|J^fXPR3^ctGdZ9u z-s&je{c_>a^8S&F$aVzPLd(VT0(v?gBuvM8}xq)z@OB-vw0?a)mDFOy@*J6YVC8S`t`9z15 zQjgtU-qAvye(y7`$KpA1ngZ6SbPrPiY-uJBOTmPOKmkDUAcD9nUb2HDTpNwz$v;tN_GAw(D2@Qb_)65DD(QzXMY~keV z?}i_1)BpqZo{=;`vxsESpvp`b&viWP%{{}4W6Kp9PPbf2z$V>tU%=hlwO9=>7|Gl< z{^CTt@ImE<&XO#23sE*mXqZrV{OMBI!YCfQn<47{ z?OylfJD%?L^4RIIlK|F8UBU*8xza>oPd22ogvDGi*ToHgcuksMCRUqtj#Pg6qh@Iy z4cMnqXk+Hn5Ssdi8KmgTHB+vunNRO?ZP@KSPYD=E5rcun%BW!|>7OlhxjRMf9)^y* zy5t(N_Px(kVbTNGob!vxjTcVPJ-u2~+?8wofX`je*F8;No<8rIZ&ru03vMJX*E|YZ zz5=j1Fv`?w1rV;Za{u~Hg*{n9j{z{;d)0Jlvzbiow567;p1#0FC}?K~^L*z}u>6@i zGO8SEWnURP~EQ>k!)GX5px+2kY^vw)UX~?@hB9+Wl*84 z{ExJJPHp}PCn`4P!exFCE(>C@=7IvwXuMycTZnK8 za|;dmc{R`Mc2vi5g02LF3*i#x79LT1A7(2`=4O54uOCaVwzpaz9+lkLIP&3Yj|i8T zi|p-aOC^p0z!EfETf|yFc`a#-h;SiX{1wU`vQ72kd$hTH;2K`>#D}(Z5dka>*0lng zJHo|3=IZa(ozRG9!Cu;P!glT;Y#M|aLxhXJvRnOe=gQMF#TozxNe;WD>A{lz;04XO z8cb>+T>Qn>no^aK$w47aZ?&5U#|BHPLnWBhK)A#x9QuTJ;|9PUeyhdwY$CXU2D6xG zy$~)DftUM$+S_VFPxJ^1eE~9!aZgH;vVznl1y|4@&B~3xIvB{gKOj@67Kd+c88Dx+eziIkNa?=GPQ(7DWtkcVTS|#hqsh_O^o4kq6 zQl?z!OQA(55kqtriiH0e0!Dq+WkVfS+<4<~ca9WJe2nQRR$h~=QB5ejexrTr^2 zWqmBPTo${9o~~A*2L+1*uAJ=70bFV;KsE<@%&G!`xC`MzxWvQ7V<{oMuAapckV4$G z0-HW2G`MNcvyaA2Xt;YjGwD0WxC`Mj&wA-8gGvMWaEfb$%RIPxIy3EiN9%=faaR*s zu7S7Z3I8^&B{xezV^3g!<-%Y|_l!X-l9_AgXxlU)71nfTqK^+LG#Y4^G(-%*bDc!2kayisf%Crl1 zh5FwMd$KQ5vtG&t$c>5u)}#h7jdA4046%;d-0c8jfc<^!-vVq^0^+U;tTRNo_`wzI z{e;3LBJOHR0$rm5BE4EC04~{jAzbJd@`9`X9kusUjk_p30fDF8AZWky5CMdXpQ-LR zv_nA5ayDr1!g%B=og2`%J{oThoTQ0u|r@l_mek%|Igo~SK%(c~N=S-~^ zxP~HJPjn3tE}n5$@1Th&QkZ4z4u_uiY61H!!BQxMi=X8Rhg4&v z6ymNXlU&0lLB~Stg>Z3-x#G-Wm_o)dVB+bEuwGaSg>dmvcB@}?5{ncDO_~Xph`6f? zQ9!u3X#1OQY*)P*28W)gl`IK#4Tbc1(YOl%L%2A_U7`Lij&7mZ64B>{DS3p8SKt*o z7W%^)4vbOI2vSf=z#2zyo4$Hs+WE&}L4Tx3-!xi@A>{GoV z9f!G$NRE5laJYm-MkANSu?`y(SPMD618V{Qheo_SfrZg6SYZWLWicT6nikV*l&30K zoyk@$bK!D(cuiK=qyZ>sbPGtNv$ljs+{3Iz;LiFzd|{p1waNM=ygLStesTZzc?g_SE|fjwS=C5~VLJ^_M(plZSEL-#ee1-Z#D~-Li=YSDFZ_Dme5{Sz)=9 zTAof84B?8sAr4-`l_y*RQ}S9YSPjCJj&M16Aw1r$p+(k9wqRulSBA@?r@JYxp-Cxu zHAzVhge$|<-V;n)*U-e2yqd~br3=D^N>A4is^l%yQZ|afkxGlIcveOOK6(XvOF0jp z>JX0qSGVTNkG=3_+QB8ehP+%lPF3=$2;9R;gv)%OYDi=#N21`jLha4VE}BW=!37=3 zY76A4sX5R?$T)l58>rUc1=pti?N(n-1=MUhaa@?&fl!q|(I@>8Qd!H1fKFUJB~Z<6 zJ+`|?JS`1epAD{Y0mfYBK(&uqEL0ppts&20C^oQOHBOAZ+ztV&mPCef*bXW?rQ$_d zFAGYc)KrCV@rA04Rh*kTK1z?;dirg7?|4?p+rV-;(lvD37_1s*t%UT{56$uLiAe%I z_JnI3Ke!;~var8PR+-ZVSGsKq7Q|LUd+5Dx7q^CIM?p#+rU2lj9Y?zCa9I@ymvN|I z{)u`sVynEoT&_>{bhlnMaSh85F5{{wqbGA_<+R{2^PvhaIx8GO>qYgA3s5E zTe^a@cb*cGG_V6)YqlRnurQ%9u#8TK$0tI-0{!gD5?i=R?cL%>MDt1jAzWew zV40Ry&CAUdvfp1{JBj;L(ncj3aNZRk2GxXcCAL~Z#$YAVlt~k~?hiU=U%&kV+kgdv za}nA2!9v?W0fZ|)ST)3gIn~DwA4+b&?s9GC7$;K*Pi|4d!k!2+miuuY&iX|>Z%*o9!B93n@#gdn#7uxb!26lc{r4J_`K zizP2MU|T?ORw;$c@3fP4a|8>rf%Vu9P@L7^M6h_fg;06n!?u9ptR+r8aV~H%cM#(& z6mKnas@38ab5Xb;&Z@w+fZ`~Cs@#c*3~rW-SuF$$#aq=*28)+lxB%M$inA6{u$r8H z5h=NaEJN6eZ2`qm(5JO99WEqQ%TTT@{hS15iZB6QMN1$3Is!tQccBg z#9Qb%t;Ad40Jo62hOGoUlpPamIc?V3OtxI0I=qdJ(Gt7`&Vmcw3}7Y{;$b7$vghv;kZ=?hR$CC98psXO0cHp6%18a=cX*g0&dGag4dF zgsnmm-1|uK5zY|U`&n?`m@JlIz-=JrkDN}+dC*Y~f-8|5Nh0$*fc&Xs$SeR1!9r!6 zJG#vTSFjfEHY!sg6P3B#r;!LG;^68q}OXMx%MD8m$aQu0-ba^oofa%)0y;-P954mdQGQe zF4H_N=@pZWYPsN3IL9&SVO8zx@Tkz=6wq=F)_t{o}Qk8nd#Z6 zzcm>2kZ;Lj-{%^0AU8LMd^VHs4XJVgX#S6B3OpC3*UK5SA>#w2zGGr4hwv6a3rnQfE}P@^(rVSv9a%E zkk>i?Kf2<;bHQ6fGFtBQ`CxEh1iHK30rQ<{L6=ZcvI;6H?dZXV0FQDlZw=(-nPoi(Oi5dsud^GzmjJ&(F8I1kLFX_2+qRo& z%eTe&^>CS5Sd!dXq-cKvoxYy5wNnw`r9#IGz3|o5v{&2WMe%VvY0>JGof8JL*cKA% z6TI1XDna8L#6rh34KGwFRX0%`JIkEX{iF4}dbOPmZY9EZWDGo)r;94VVz0h%;i?qB zrU=Q-&Q8m5Fu27IAOZeK&f2CIJQrvj6boI#MQK(fwi8;}`m}=Qd`!KXpquBWS0em_ zTuD6j;Dbil3e}|PrumuZ9YW8*kt#F1w%GzzCVj-;SLf)I4qtMlpXe1G6%)*10H<{G zj?iDLwZQKc=fF0L0sgKgGUYQ&j%Q5svCbh8(?0@{v&++i`$F?v^;6wXY9e15*t z6r$xNM7zVaew}VEW9N%?4w0#zgHuRRVvq>EP>f$^(~~Rhij3Z~7JVTm95p>$N^o~Biq_3(vwzl)s{5DhvnuEJ9A zT5Rlltni@~6=U)Vt7CHdctI17C)bw7+IvD8Kfj#PZOhM!`kSt)Wiwyw^~idB92P!Z zb)V?^e_u&O;60YOMV7f=5(@NmBDK8^V&799kZV`l6pWb7mui;30v2Em?8(vDP2lagDCr#w*s2g{Z5`9KWN)8m%qxEBfV zc3ZjR{})~^<^yMug&jx2;JLD_Lnw;x?6?JL5p#?}4r@+l7X$C_(R*QXGWj4C%{%&Q zxdhF=X?rukGl|#ZL8#Cb6o9A`4?NwO%K} zw>yObItkS!g_!$M4sv8?<@5JPNq*na;YvDsPX;o50dZGLH-Wnr3vo@FAga&`Gcr?@BpKSXh^oaQqhNOJE z3bj3urcDg7{uj+X4$h#Q>h}kIyQT-=MiN0C0>_G6u<*bUesCS(Z?DH50J;w*NTxI@ z$Uwx+8p!g5Sn&IWB6&4xJ1FK{eg>jmIDvGN)`FW(Tu%^el$xtsgxZ`L*kYV!H!-POu!)ZaJv?;`cM1Du+~5*fDt5aE z#b`Y)Ak`wI@!u0XSnq(UfS;MP!0)E=V0uy7NHqTb>uD3aS@jF>SpGQd$c^p>Fpz6m zT@zy7V?hsgE#c7rdd(tSJ$+g9Ibz`qHDBNsZB*tXLcv{JGw*i(=-TATXt>*R2lUj%NM%@M;#AoPzuNw>!nG6$#dMz9frU8*L`fa*@af^_`(s z<8hlFe&oFgt}nh!D2>FGLBynX@l3toRB4Y2YVz-m+F0|PNcz1th2>O|Q>^x4t{yH}D0=bASO7Zu|CcS3u)5_l=>E1tw(}Sq5M}P`A~c?>&Bqnrys`&l z-XF6?e5Y&u9q7)V0%m*|mMlfESocF%tiDIq;|}+Y!TQZs;@?BVqmPGX;SFNiQRC6~ zKeGxTr>$9ZswPEHDY3pHFg*)nxJF%+FXKuAD>|gr#G}Rs3v;^-^dJ2P3!VK;UiBaR z3+S4EBm40iKEDmO#%2Si{qJu~LVcItLVmy40MAXfgQw|?>{GcyQ#-+_;CrG)^>>Te zd5TtgOj^S_0@o82jI0}-y+84bKc%+7I@N2Q5p!4RSC;Zj~+23xjm3TS>RuE& z{xE9{p1spbfS-l-E7y}A=@Z=!!WmZ~4!{|t1a`a>m)I0H%?it~(PVMUZq~=MDc-v$ z1K%&L-ITI(Ip1^{%1Mfsi6N}**Tk&vFjC4V7prU3PWDfj{Ao#+h z^0LaxQg(P&2_;+vZ(f*&DSc$+>_)B$p4{YwJ>wqOIPHbJg@uHVgMlK}1p=QrSHi{- zK{Akyg(NR1Rg#K8(APV+Z&u93JfiE3`}cDeE7ATk_BC8eiAAUpv4W+(=^gfU%ht_c zDX;`O1D`pB!0*q@oCSPPgOJ7tgHNa=hl2vRMynF!LAwzueTn7gM{jq*TkTii=Uvy? zP7qUO{hj0e#y8>q2X-x+|M5GDQ2?J$(4sjL_VK}j5q@Q*T@e@kgMtyT%qD)LRb)#xH*Q*&?Be{gNRh<^FW82_I@vGj|GiVRhIIiiJ8IaN zAeMyta#nIo#Lw?m6?i77fP*Vj)+M_`ytPLO`Mqbv1HT$JL3@66{00~whBX9qUzYgs zd;C9G>&LplQgH<=-t$<%67XW27=)iWCeyY?Ua;U(GVa~EHGX_1_d$7opK173n-%^v zZw}6Ndek9 zA<)f=)%ajje@;&LX!=tvz1`R0PS?9|tK&S(Oh@L;sBMqcmcdVqT~IX;eW`c*ogUb? z{Q>yP%C(ReXMv2DEB&))e>mLFb^@#k#FI_=OWN@9kvYDd?EtA7!tHbS!M*lhLP1s= zyz(Prc1KRI-$fILy%-MFT43{q{{*J@M#SGIXT0#MjvLUoDt18%{r;a-ZD#8M|75R( zNxdOv^so2L!Zv;8o+B(`k))^{M;8NG!(Bs(%o40HZ=_LQvJ_)xg?<3Kf518TG045786|MC*)Ue-IPs*gEXkA`# z7G(@PR#dC2<1WI`WJpKv!_}H_6$d$&DKB5+hdoYctzF}Xf4`F<+hy|IWjNow2;L)HkK-Abf=n{k~kL z-()F;0s@|g9xT`)_C2J7dEX$MviuSBm+XkP1GWP!kyoa(+20A06g2U>`aXjVj{n4+W#=Hpao>{myn+qQE8kn+G#075cy#fzs55xAYH5tjsW%P{_h}+RxDq9fzbfZ?<|EszN;9@3v$>=k-xk$ z1h1TSMeJ|w#rNT-k8THJt}##u!oe2Tlz*1bFk^!8*#%e&6OhX+!Uk(Llvk94IZqim zI}VpW^zgkxKieA)UY6>e^eWpJTk%UY@<;f2^S_42zB2|-z4HWgJC<+u-MZQgC%(Ut z1s*O4{*4QvDWI z(f${(>$OKhpg&$?ga3ZEHY#+K=XXVdUCJ@RosE0ocyB&@{N?4Sg&`$Nz9moSC#;zZ zZ{=??dD0mC;Y3bcbfMejmgN86`J@XL?hl2InjSYL4C@f=lxuEMCeF+I4QzrlUtfYP zq^)+Zv%tC0qdi5MFcvLc_fu zUv~$9VIYT^j;~UMnTW`q96OkfsRmsV?Ls+X1i?86E@l-5G;k)3UGr zgBoj0pB^q(2c{%hgGJK0jJdgL)^^|@Vh|1dIVE0{|2gdC@bI1;Y!Jw=_tJW6D8xKJ zeV6#jAlC-OPlm)}n7=#;584(KtnsUae$p6lsWJXouhfe}jmIDmH}wB+dWmmO@NTXU zjVES)%j|6!>7Iu^UltqPBWw)#a)-Ow6|Z(bRHN7w56iRyiywt3wIPZraFKi-eZ3PD zF6jIJ^}CDkcIyOu&@l<_NoiHU4{t0Nj&!%%;JbCZz^eD}qibRTn4wR>m~VvAjmppG zMb3GQtMR;_S3G#Or)TTn1!n=fA^f+R`{02Jizw*)z3gl!S{NhHm)%5FzMkxSof?{e z)pQZ_9a~VT106lakB);1<;nGV@Uxxy(A_@_3vp)3V#zkRT#M#>x5EZ%6_L~1e-Zpa z7bEg>DIup%RI(Q5U|?VXMn^{@Isig{>aErgZATxzxx84l&_%9N(ijpe#rYv>eFLai zyk0N6>2x|U8jWzVe+K^MOfTFX4;{^Ww5otzx>SwOMNVmN3^wzum5cRDZkQGyDOCt7 zOt1qq^-ROr9xvQoWPoRi##OaiCtur;upZF?0=`iccn&^l{6Wx&;QT;b=>t0fE+V9~ z$E~obti#UUtSVtJ%SE6^M5#jH9h}x{QqTfmC%_KDcZy7Dcm+3rmB8rb5d!{oN#Hs7 zxNyvVpc1eO{PLlAS1SKrZq3F8rf{txoTesX4qYt4ahG+tl4Zm(MEvLD6ns$KAm9IR z-|ztubPE&Q!b2mT<%JipbHuxQmG{#IgB~nozc}+pLOVmg{5sl}yew4PUDn!siiw4h`jaO-#Dqb4Hi#17^`XG~^ zktx_zLVr+b zi?+EU8uzn7exPp@> zwGp=NEs%Sryokij+u0vJ%NumU}A0GL0O8H zLj-!tQe-AwSHazxgMOb5?8Fxr_-pSey?2pG_f?S&a2WhmK5F`-n0D~j-cvsQ!DQm< zxI1)E`|BucRz^9~-k7cfGMT(us{O3sAj_-i&~n9q5Ayx>8qE2dIJ2ISbqE-xv;t@T z>opMkxj5jBnLG(bX{|y!0Q&uxR$vEkaVGtQ5>AFuYAbOT;HcGV-LKc{3x|h?)e5|c z1Ky;hlVX(KYD-H?X#Nko-ELJZ!4{<5Xv&X^o$j1&F{q#s*+qBlcKaa$AK(6%a{~?q zrep~FRHps+O3`@q`FQuvUWQ?s!dwA%fIVD2AQLGeo)4}T*_f&5d?kZNz_=XC5d!}8 zI5B~fa|L&EuXQq?N(;B)BPN@a=CfCvsVA^q1U?99G^cSbQECKyToBw53ZlddE)Hri zXf)q0rTXa=FH3$oMq5HY(a|QLqS)Y$)A%~t8dT${XaKx3DEOkHA@G%abWd?NS?bH{(WILPHwQ4bITOPJ`Nhn5)=1X=`W; z{7u|wOK1x^U!OlrTSHsm8#wUvHY*K5cW}@fXlrN-{4pBTX%BiK**s)BOIt&m+FnOn zLwmlRYP-h3pQOHByLmf}HfRYddgetCode(), ''); } - return ['$title' => $title, '$description' => $message]; + return ['$title' => $title, '$message' => $message, '$back' => L10n::t('Go back')]; } /** * Displays a bare message page with no theming at all. * * @param \Friendica\Network\HTTPException $e - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \Exception */ public static function rawContent(\Friendica\Network\HTTPException $e) { @@ -78,7 +78,7 @@ class HTTPException * * @param \Friendica\Network\HTTPException $e * @return string - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \Exception */ public static function content(\Friendica\Network\HTTPException $e) { diff --git a/view/global.css b/view/global.css index b48fa1a24a..b729f1fa8e 100644 --- a/view/global.css +++ b/view/global.css @@ -5,7 +5,7 @@ details > summary { cursor: pointer; } -/* General designing elements */ +/* General design elements */ .btn { outline: none; -moz-box-shadow: inset 0px 1px 0px 0px #ffffff; @@ -87,7 +87,6 @@ span.connector { .wall-item-container .wall-item-content .type-link img.attachment-image, .type-link img.attachment-image, .type-video img.attachment-image { - /* max-width: 640px; */ max-width: 100%; max-height: initial; float: initial; @@ -621,3 +620,22 @@ span.emoji.mastodon img { height: 1.2em; vertical-align: middle; } + +/* Exception page */ + +#exception { + overflow: hidden; + background-image: url('../images/friendica-404_svg_hare-bottom-light-inside.png'); + background-position: 50px bottom; + background-repeat: no-repeat; +} + +#exception .hare { + float: right; +} + +@media screen and (max-width: 600px) { + #exception .hare { + display: none; + } +} \ No newline at end of file diff --git a/view/templates/exception.tpl b/view/templates/exception.tpl index 02b7648e3a..6c26168908 100644 --- a/view/templates/exception.tpl +++ b/view/templates/exception.tpl @@ -1,4 +1,6 @@
+

{{$title}}

{{$message}}

+

diff --git a/view/theme/frio/css/style.css b/view/theme/frio/css/style.css index e6b42e52ea..4a72628352 100644 --- a/view/theme/frio/css/style.css +++ b/view/theme/frio/css/style.css @@ -105,6 +105,9 @@ blockquote { * mobile aside */ @media screen and (max-width: 990px) { + body { + padding-top: 105px; + } aside{ position: fixed!important; top: 0!important; @@ -616,6 +619,10 @@ nav.navbar a, nav.navbar .btn-link { display: flex; } +#friendica-logo-mask { + display: block; +} + /* Notification Menu */ #topbar-first #nav-notifications-menu { From 9123361d61258a5a62bd5a2b17964f5b70eddaf1 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 10:00:28 +0200 Subject: [PATCH 05/18] added feedback --- src/Model/User.php | 2 +- src/Module/Friendica.php | 2 +- view/templates/feedtest.tpl | 50 ++++++++++----------- view/templates/friendica.tpl | 84 ++++++++++++++++++------------------ 4 files changed, 71 insertions(+), 67 deletions(-) diff --git a/src/Model/User.php b/src/Model/User.php index 8945faeaef..64253946fd 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -130,7 +130,7 @@ class User } /** - * Get a user based on it's email + * Get a user based on its email * * @param string $email * @param array $fields diff --git a/src/Module/Friendica.php b/src/Module/Friendica.php index 3901a0bc77..ae8ea14968 100644 --- a/src/Module/Friendica.php +++ b/src/Module/Friendica.php @@ -76,7 +76,7 @@ class Friendica extends BaseModule '' . FRIENDICA_VERSION . '', $app->getBaseURL(), '' . DB_UPDATE_VERSION . '', - '' . $config->get("system", "post_update_version") . ''), + '' . $config->get('system', 'post_update_version') . ''), 'friendica' => L10n::t('Please visit Friendi.ca to learn more about the Friendica project.'), 'bugs' => L10n::t('Bug reports and issues: please visit') . ' ' . '' . L10n::t('the bugtracker at github') . '', 'info' => L10n::t('Suggestions, praise, etc. - please email "info" at "friendi - dot - ca'), diff --git a/view/templates/feedtest.tpl b/view/templates/feedtest.tpl index 2147198f1d..f1edf44a94 100644 --- a/view/templates/feedtest.tpl +++ b/view/templates/feedtest.tpl @@ -1,30 +1,32 @@ -

Feed Test

-
-
-
- {{include file="field_input.tpl" field=$url}} +
+

Feed Test

+ +
+
+ {{include file="field_input.tpl" field=$url}} +
+

-

-
- + -{{if $result}} -
-
-
-

Output Items

+ {{if $result}} +
+
+
+

Output Items

+
+
+
{{$result.output}}
+
-
-
{{$result.output}}
-
-
-
-
-

Input Feed XML

-
-
- {{$result.input}} +
+
+

Input Feed XML

+
+
+ {{$result.input}} +
+ {{/if}}
-{{/if}} \ No newline at end of file diff --git a/view/templates/friendica.tpl b/view/templates/friendica.tpl index 3beabda367..f5e1830cac 100644 --- a/view/templates/friendica.tpl +++ b/view/templates/friendica.tpl @@ -1,45 +1,47 @@ -

Friendica

-
-

{{$about nofilter}}

-
-

{{$friendica nofilter}}

-
-

{{$bugs nofilter}}

-
-

{{$info nofilter}}

-
- -

{{$visible_addons.title nofilter}}

-{{if $visible_addons.list}} -
{{$visible_addons.list nofilter}}
-{{/if}} - -{{if $tos}} -

{{$tos nofilter}}

-{{/if}} - -{{if $block_list}} -
-

{{$block_list.title nofilter}}

+
+

Friendica

+
+

{{$about nofilter}}

+
+

{{$friendica nofilter}}

+
+

{{$bugs nofilter}}

+
+

{{$info nofilter}}


- - - - - - - - - {{foreach $block_list.list as $blocked}} - - - - - {{/foreach}} - -
{{$block_list.header[0] nofilter}}{{$block_list.header[1] nofilter}}
{{$blocked.domain nofilter}}{{$blocked.reason nofilter}}
-
-{{/if}} +

{{$visible_addons.title nofilter}}

+ {{if $visible_addons.list}} +
{{$visible_addons.list nofilter}}
+ {{/if}} + + {{if $tos}} +

{{$tos nofilter}}

+ {{/if}} + + {{if $block_list}} +
+

{{$block_list.title nofilter}}

+
+ + + + + + + + + {{foreach $block_list.list as $blocked}} + + + + + {{/foreach}} + +
{{$block_list.header[0] nofilter}}{{$block_list.header[1] nofilter}}
{{$blocked.domain nofilter}}{{$blocked.reason nofilter}}
+
+ + {{/if}} {{$hooked nofilter}} +
\ No newline at end of file From a052b098fc9daa7380cc4c660d46187662f8ec65 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 4 May 2019 10:30:05 +0200 Subject: [PATCH 06/18] Move mod/filerm to src/Module/FilerM --- mod/filerm.php | 40 ---------------- src/App/Router.php | 3 +- src/Module/Filer/RemoveTag.php | 51 +++++++++++++++++++++ src/Module/{Filer.php => Filer/SaveTag.php} | 4 +- 4 files changed, 55 insertions(+), 43 deletions(-) delete mode 100644 mod/filerm.php create mode 100644 src/Module/Filer/RemoveTag.php rename src/Module/{Filer.php => Filer/SaveTag.php} (95%) diff --git a/mod/filerm.php b/mod/filerm.php deleted file mode 100644 index 9013dd62b4..0000000000 --- a/mod/filerm.php +++ /dev/null @@ -1,40 +0,0 @@ -argc > 1) ? intval($a->argv[1]) : 0); - - Logger::log('filerm: tag ' . $term . ' item ' . $item_id . ' category ' . ($category ? 'true' : 'false')); - - if ($item_id && strlen($term)) { - if (FileTag::unsaveFile(local_user(), $item_id, $term, $category)) { - info('Item removed'); - } - } - else { - info('Item was not deleted'); - } - - $a->internalRedirect('/network?f=&file=' . rawurlencode($term)); - exit(); -} diff --git a/src/App/Router.php b/src/App/Router.php index b94c0bb58d..48d63224ed 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -109,7 +109,8 @@ class Router $collector->addRoute(['GET'], '/{guid}/status_message', Module\Diaspora\Fetch::class); $collector->addRoute(['GET'], '/{guid}/reshare', Module\Diaspora\Fetch::class); }); - $this->routeCollector->addRoute(['GET'], '/filer[/{id:\d+}]', Module\Filer::class); + $this->routeCollector->addRoute(['GET'], '/filer[/{id:\d+}]', Module\Filer\SaveTag::class); + $this->routeCollector->addRoute(['GET'], '/filerm/{id:\d+}', Module\Filer\RemoveTag::class); $this->routeCollector->addRoute(['GET'], '/followers/{owner}', Module\Followers::class); $this->routeCollector->addRoute(['GET'], '/following/{owner}', Module\Following::class); $this->routeCollector->addGroup('/group', function (RouteCollector $collector) { diff --git a/src/Module/Filer/RemoveTag.php b/src/Module/Filer/RemoveTag.php new file mode 100644 index 0000000000..bddaaf9dc2 --- /dev/null +++ b/src/Module/Filer/RemoveTag.php @@ -0,0 +1,51 @@ +getLogger(); + + $item_id = (($app->argc > 1) ? intval($app->argv[1]) : 0); + + $term = XML::unescape(trim(defaults($_GET, 'term', ''))); + $cat = XML::unescape(trim(defaults($_GET, 'cat', ''))); + + $category = (($cat) ? true : false); + + if ($category) { + $term = $cat; + } + + $logger->info('Filer - Remove Tag', [ + 'term' => $term, + 'item' => $item_id, + 'category' => ($category ? 'true' : 'false') + ]); + + if ($item_id && strlen($term)) { + if (FileTag::unsaveFile(local_user(), $item_id, $term, $category)) { + info('Item removed'); + } + } else { + info('Item was not deleted'); + } + + $app->internalRedirect('/network?f=&file=' . rawurlencode($term)); + } +} diff --git a/src/Module/Filer.php b/src/Module/Filer/SaveTag.php similarity index 95% rename from src/Module/Filer.php rename to src/Module/Filer/SaveTag.php index da59084da0..f5d6115199 100644 --- a/src/Module/Filer.php +++ b/src/Module/Filer/SaveTag.php @@ -1,6 +1,6 @@ Date: Sat, 4 May 2019 13:42:26 +0200 Subject: [PATCH 07/18] Move mod/maintenance to src/Module/Maintenance --- mod/maintenance.php | 29 ----------------------------- src/App.php | 8 ++++---- src/App/Router.php | 1 + src/Core/System.php | 3 +++ src/Module/Maintenance.php | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 33 deletions(-) delete mode 100644 mod/maintenance.php create mode 100644 src/Module/Maintenance.php diff --git a/mod/maintenance.php b/mod/maintenance.php deleted file mode 100644 index 8e0197b868..0000000000 --- a/mod/maintenance.php +++ /dev/null @@ -1,29 +0,0 @@ - L10n::t('System down for maintenance'), - '$reason' => $reason - ]); -} diff --git a/src/App.php b/src/App.php index 017661c4ca..32e9fa8763 100644 --- a/src/App.php +++ b/src/App.php @@ -1077,10 +1077,10 @@ class App // in install mode, any url loads install module // but we need "view" module for stylesheet - if ($this->getMode()->isInstall() && $this->module != 'view') { - $this->module = 'install'; - } elseif (!$this->getMode()->has(App\Mode::MAINTENANCEDISABLED) && $this->module != 'view') { - $this->module = 'maintenance'; + if ($this->getMode()->isInstall() && $this->module !== 'install') { + $this->internalRedirect('install'); + } elseif (!$this->getMode()->has(App\Mode::MAINTENANCEDISABLED) && $this->module !== 'maintenance') { + $this->internalRedirect('maintenance'); } else { $this->checkURL(); Core\Update::check($this->getBasePath(), false, $this->getMode()); diff --git a/src/App/Router.php b/src/App/Router.php index b94c0bb58d..ac701a0786 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -138,6 +138,7 @@ class Router $this->routeCollector->addRoute(['GET', 'POST'], '/login', Module\Login::class); $this->routeCollector->addRoute(['GET', 'POST'], '/logout', Module\Logout::class); $this->routeCollector->addRoute(['GET'], '/magic', Module\Magic::class); + $this->routeCollector->addRoute(['GET'], '/maintenance', Module\Maintenance::class); $this->routeCollector->addRoute(['GET'], '/manifest', Module\Manifest::class); $this->routeCollector->addRoute(['GET'], '/nodeinfo/1.0', Module\NodeInfo::class); $this->routeCollector->addRoute(['GET'], '/nogroup', Module\Group::class); diff --git a/src/Core/System.php b/src/Core/System.php index 31934af5a7..42587577da 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -242,6 +242,9 @@ class System extends BaseObject case 301: header('HTTP/1.1 301 Moved Permanently'); break; + case 307: + header('HTTP/1.1 307 Temporary Redirect'); + break; } header("Location: $url"); diff --git a/src/Module/Maintenance.php b/src/Module/Maintenance.php new file mode 100644 index 0000000000..e7dc5a075d --- /dev/null +++ b/src/Module/Maintenance.php @@ -0,0 +1,37 @@ +getConfig(); + + $reason = $config->get('system', 'maintenance_reason'); + + if ((substr(Strings::normaliseLink($reason), 0, 7) === 'http://') || + (substr(Strings::normaliseLink($reason), 0, 8) === 'https://')) { + System::externalRedirect($reason, 307); + } + + header('HTTP/1.1 503 Service Temporarily Unavailable'); + header('Status: 503 Service Temporarily Unavailable'); + header('Retry-After: 600'); + + return Renderer::replaceMacros(Renderer::getMarkupTemplate('maintenance.tpl'), [ + '$sysdown' => L10n::t('System down for maintenance'), + '$reason' => $reason + ]); + } +} From 9fb111bca2cc7ab0817d4ddcb294faa9c144c650 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 4 May 2019 16:22:47 +0200 Subject: [PATCH 08/18] Rename ServiceUnavailableException & alter maintenance --- src/App.php | 2 +- src/Module/Maintenance.php | 13 ++++--------- ...xception.php => ServiceUnavailableException.php} | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) rename src/Network/HTTPException/{ServiceUnavaiableException.php => ServiceUnavailableException.php} (67%) diff --git a/src/App.php b/src/App.php index 32e9fa8763..0f0c0537e4 100644 --- a/src/App.php +++ b/src/App.php @@ -988,7 +988,7 @@ class App header('Refresh: 120; url=' . $this->getBaseURL() . "/" . $this->query_string); Module\Special\HTTPException::rawContent( - new HTTPException\ServiceUnavaiableException('The node is currently overloaded. Please try again later.') + new HTTPException\ServiceUnavailableException('The node is currently overloaded. Please try again later.') ); } diff --git a/src/Module/Maintenance.php b/src/Module/Maintenance.php index e7dc5a075d..24140bb351 100644 --- a/src/Module/Maintenance.php +++ b/src/Module/Maintenance.php @@ -4,8 +4,8 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\L10n; -use Friendica\Core\Renderer; use Friendica\Core\System; +use Friendica\Network\HTTPException; use Friendica\Util\Strings; /** @@ -25,13 +25,8 @@ class Maintenance extends BaseModule System::externalRedirect($reason, 307); } - header('HTTP/1.1 503 Service Temporarily Unavailable'); - header('Status: 503 Service Temporarily Unavailable'); - header('Retry-After: 600'); - - return Renderer::replaceMacros(Renderer::getMarkupTemplate('maintenance.tpl'), [ - '$sysdown' => L10n::t('System down for maintenance'), - '$reason' => $reason - ]); + $exception = new HTTPException\ServiceUnavailableException($reason); + $exception->httpdesc = L10n::t('System down for maintenance'); + throw $exception; } } diff --git a/src/Network/HTTPException/ServiceUnavaiableException.php b/src/Network/HTTPException/ServiceUnavailableException.php similarity index 67% rename from src/Network/HTTPException/ServiceUnavaiableException.php rename to src/Network/HTTPException/ServiceUnavailableException.php index 6c0e6595d8..257b8c8585 100644 --- a/src/Network/HTTPException/ServiceUnavaiableException.php +++ b/src/Network/HTTPException/ServiceUnavailableException.php @@ -4,7 +4,7 @@ namespace Friendica\Network\HTTPException; use Friendica\Network\HTTPException; -class ServiceUnavaiableException extends HTTPException +class ServiceUnavailableException extends HTTPException { protected $code = 503; } From 1e2d9e0bd4f7f7a53b874e0c4cad62a023fb3572 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 5 May 2019 08:14:55 -0400 Subject: [PATCH 09/18] Add EOF EOL to view/global.css --- view/global.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/global.css b/view/global.css index b729f1fa8e..0eaf4482dc 100644 --- a/view/global.css +++ b/view/global.css @@ -638,4 +638,4 @@ span.emoji.mastodon img { #exception .hare { display: none; } -} \ No newline at end of file +} From 06ce74c7c929eeea219ba8bbf70026b4b04178b6 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 17:01:51 +0200 Subject: [PATCH 10/18] add feedback --- view/templates/friendica.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/templates/friendica.tpl b/view/templates/friendica.tpl index f5e1830cac..f890a58a95 100644 --- a/view/templates/friendica.tpl +++ b/view/templates/friendica.tpl @@ -1,4 +1,4 @@ -
+

Friendica


{{$about nofilter}}

@@ -44,4 +44,4 @@ {{/if}} {{$hooked nofilter}} -
\ No newline at end of file +
From 5d1a12d9b34c7c1011eec7597116fc22d72babcb Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 17:02:17 +0200 Subject: [PATCH 11/18] add feedback --- view/templates/feedtest.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/templates/feedtest.tpl b/view/templates/feedtest.tpl index f1edf44a94..709ad4de43 100644 --- a/view/templates/feedtest.tpl +++ b/view/templates/feedtest.tpl @@ -1,4 +1,4 @@ -
+

Feed Test

From 440d0a3a63ffc24f608504e8df8d2038fd95ca5b Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 17:59:57 +0200 Subject: [PATCH 12/18] Rename BookMarklet to Bookmarklet --- src/App/Router.php | 2 +- src/Module/{BookMarklet.php => Bookmarklet.php} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Module/{BookMarklet.php => Bookmarklet.php} (98%) diff --git a/src/App/Router.php b/src/App/Router.php index 1dffb6b406..11ce686770 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -89,7 +89,7 @@ class Router $this->routeCollector->addRoute(['GET'], '/apps', Module\Apps::class); $this->routeCollector->addRoute(['GET'], '/attach/{item:\d+}', Module\Attach::class); $this->routeCollector->addRoute(['GET'], '/babel', Module\Babel::class); - $this->routeCollector->addRoute(['GET'], '/bookmarklet', Module\BookMarklet::class); + $this->routeCollector->addRoute(['GET'], '/bookmarklet', Module\Bookmarklet::class); $this->routeCollector->addGroup('/contact', function (RouteCollector $collector) { $collector->addRoute(['GET'], '[/]', Module\Contact::class); $collector->addRoute(['GET'], '/{id:\d+}[/posts|conversations]', Module\Contact::class); diff --git a/src/Module/BookMarklet.php b/src/Module/Bookmarklet.php similarity index 98% rename from src/Module/BookMarklet.php rename to src/Module/Bookmarklet.php index 51feb25d7b..1b6ff38459 100644 --- a/src/Module/BookMarklet.php +++ b/src/Module/Bookmarklet.php @@ -12,7 +12,7 @@ use Friendica\Util\Strings; * Creates a bookmarklet * Shows either a editor browser or adds the given bookmarklet to the current user */ -class BookMarklet extends BaseModule +class Bookmarklet extends BaseModule { public static function content() { From f6af90fa328bcf7e731917f011b3e20fefeace1c Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 18:15:39 +0200 Subject: [PATCH 13/18] Create XML output for RSD with XML::fromArray() --- src/Module/ReallySimpleDiscovery.php | 42 +++++++++++++++++++++++++--- view/templates/rsd.tpl | 15 ---------- 2 files changed, 38 insertions(+), 19 deletions(-) delete mode 100644 view/templates/rsd.tpl diff --git a/src/Module/ReallySimpleDiscovery.php b/src/Module/ReallySimpleDiscovery.php index 4c14d3c837..515285dbfc 100644 --- a/src/Module/ReallySimpleDiscovery.php +++ b/src/Module/ReallySimpleDiscovery.php @@ -3,7 +3,7 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Core\Renderer; +use Friendica\Util\XML; /** * Prints the rsd.xml @@ -13,9 +13,43 @@ class ReallySimpleDiscovery extends BaseModule { public static function rawContent() { - header ('Content-Type: text/xml'); - $tpl = Renderer::getMarkupTemplate('rsd.tpl'); - echo Renderer::replaceMacros($tpl); + header('Content-Type: text/xml'); + + $app = self::getApp(); + $xml = null; + echo XML::fromArray([ + 'rsd' => [ + '@attributes' => [ + 'version' => '1.0', + 'xmlns' => 'http://archipelago.phrasewise.com/rsd', + ], + 'service' => [ + 'engineName' => 'Friendica', + 'engineLink' => 'http://friendica.com', + 'apis' => [ + 'api' => [ + '@attributes' => [ + 'name' => 'Twitter', + 'preferred' => 'true', + 'apiLink' => $app->getBaseURL(), + 'blogID' => '', + ], + 'settings' => [ + 'docs' => [ + 'http://status.net/wiki/TwitterCompatibleAPI', + ], + 'setting' => [ + '@attributes' => [ + 'name' => 'OAuth', + ], + 'false', + ], + ], + ] + ], + ], + ], + ], $xml); exit(); } } diff --git a/view/templates/rsd.tpl b/view/templates/rsd.tpl deleted file mode 100644 index c194e8f26b..0000000000 --- a/view/templates/rsd.tpl +++ /dev/null @@ -1,15 +0,0 @@ - - - - Friendica - http://friendica.com/ - - - - http://status.net/wiki/TwitterCompatibleAPI - false - - - - - From 039f9490dd6e8cb6551f58908076dfc267fa185a Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 18:40:36 +0200 Subject: [PATCH 14/18] Optimized Profile::searchProfiles & Bugfix Contact::getProbeDataFromDatabase --- src/Model/Contact.php | 2 +- src/Model/Profile.php | 100 +++++++++++++++++++++++++++++------------- 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index f4c62fea80..3f25fd938a 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1168,7 +1168,7 @@ class Contact extends BaseObject // The link could be provided as http although we stored it as https $ssl_url = str_replace('http://', 'https://', $url); - $fields = ['url', 'addr', 'alias', 'notify', 'poll', 'name', 'nick', + $fields = ['id', 'uid', 'url', 'addr', 'alias', 'notify', 'poll', 'name', 'nick', 'photo', 'keywords', 'location', 'about', 'network', 'priority', 'batch', 'request', 'confirm', 'poco']; diff --git a/src/Model/Profile.php b/src/Model/Profile.php index 9d2600b3da..a854b1c9e0 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -1249,56 +1249,96 @@ class Profile */ public static function searchProfiles($start = 0, $count = 100, $search = null) { - if ($search) { - $search = DBA::escape($search); - - $sql_extra = " AND ((`profile`.`name` LIKE '%$search%') OR - (`user`.`nickname` LIKE '%$search%') OR - (`profile`.`pdesc` LIKE '%$search%') OR - (`profile`.`locality` LIKE '%$search%') OR - (`profile`.`region` LIKE '%$search%') OR - (`profile`.`country-name` LIKE '%$search%') OR - (`profile`.`gender` LIKE '%$search%') OR - (`profile`.`marital` LIKE '%$search%') OR - (`profile`.`sexual` LIKE '%$search%') OR - (`profile`.`about` LIKE '%$search%') OR - (`profile`.`romance` LIKE '%$search%') OR - (`profile`.`work` LIKE '%$search%') OR - (`profile`.`education` LIKE '%$search%') OR - (`profile`.`pub_keywords` LIKE '%$search%') OR - (`profile`.`prv_keywords` LIKE '%$search%'))"; - } else { - $sql_extra = ''; - } - $publish = (Config::get('system', 'publish_all') ? '' : " AND `publish` = 1 "); - $total = 0; - $cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` + + if (!empty($search)) { + $searchTerm = '%' . $search . '%'; + $cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` - WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` $sql_extra"); + WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` + AND ((`profile`.`name` LIKE ?) OR + (`user`.`nickname` LIKE ?) OR + (`profile`.`pdesc` LIKE ?) OR + (`profile`.`locality` LIKE ?) OR + (`profile`.`region` LIKE ?) OR + (`profile`.`country-name` LIKE ?) OR + (`profile`.`gender` LIKE ?) OR + (`profile`.`marital` LIKE ?) OR + (`profile`.`sexual` LIKE ?) OR + (`profile`.`about` LIKE ?) OR + (`profile`.`romance` LIKE ?) OR + (`profile`.`work` LIKE ?) OR + (`profile`.`education` LIKE ?) OR + (`profile`.`pub_keywords` LIKE ?) OR + (`profile`.`prv_keywords` LIKE ?))", + $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, + $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm); + } else { + $cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` + FROM `profile` + LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` + WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed`"); + } + if (DBA::isResult($cnt)) { $total = $cnt['total']; } $order = " ORDER BY `name` ASC "; - $limit = $start . ',' . $count; + $profiles = []; - $profiles = DBA::p("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`, + // If nothing found, don't try to select details + if ($total > 0) { + if (!empty($search)) { + $searchTerm = '%' . $search . '%'; + + $profiles = DBA::p("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`, `contact`.`addr`, `contact`.`url` AS `profile_url` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` LEFT JOIN `contact` ON `contact`.`uid` = `user`.`uid` WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `contact`.`self` - $sql_extra $order LIMIT $limit" - ); + AND ((`profile`.`name` LIKE ?) OR + (`user`.`nickname` LIKE ?) OR + (`profile`.`pdesc` LIKE ?) OR + (`profile`.`locality` LIKE ?) OR + (`profile`.`region` LIKE ?) OR + (`profile`.`country-name` LIKE ?) OR + (`profile`.`gender` LIKE ?) OR + (`profile`.`marital` LIKE ?) OR + (`profile`.`sexual` LIKE ?) OR + (`profile`.`about` LIKE ?) OR + (`profile`.`romance` LIKE ?) OR + (`profile`.`work` LIKE ?) OR + (`profile`.`education` LIKE ?) OR + (`profile`.`pub_keywords` LIKE ?) OR + (`profile`.`prv_keywords` LIKE ?)) + $order LIMIT ?,?", + $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, + $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm, + $start, $count + ); + } else { + $profiles = DBA::p("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`, + `contact`.`addr`, `contact`.`url` AS `profile_url` + FROM `profile` + LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` + LEFT JOIN `contact` ON `contact`.`uid` = `user`.`uid` + WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `contact`.`self` + $order LIMIT ?,?", + $start, $count + ); + } + } - if (DBA::isResult($profiles)) { + if (DBA::isResult($profiles) && $total > 0) { return [ 'total' => $total, 'entries' => DBA::toArray($profiles), ]; + } else { return [ 'total' => $total, From 9f0bbd96be8b4b4995cc81ae7700e38ccb2cfadd Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 19:02:19 +0200 Subject: [PATCH 15/18] Move mod/modexp to src/Module/PublicRSAKey --- mod/modexp.php | 36 ---------------------------- src/App/Router.php | 1 + src/Module/PublicRSAKey.php | 47 +++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 36 deletions(-) delete mode 100644 mod/modexp.php create mode 100644 src/Module/PublicRSAKey.php diff --git a/mod/modexp.php b/mod/modexp.php deleted file mode 100644 index cae91c4648..0000000000 --- a/mod/modexp.php +++ /dev/null @@ -1,36 +0,0 @@ -argc != 2) - exit(); - - $nick = $a->argv[1]; - $r = q("SELECT `spubkey` FROM `user` WHERE `nickname` = '%s' LIMIT 1", - DBA::escape($nick) - ); - - if (! DBA::isResult($r)) { - exit(); - } - - $lines = explode("\n",$r[0]['spubkey']); - unset($lines[0]); - unset($lines[count($lines)]); - $x = base64_decode(implode('',$lines)); - - $r = ASN_BASE::parseASNString($x); - - $m = $r[0]->asnData[1]->asnData[0]->asnData[0]->asnData; - $e = $r[0]->asnData[1]->asnData[0]->asnData[1]->asnData; - - header("Content-type: application/magic-public-key"); - echo 'RSA' . '.' . $m . '.' . $e; - - exit(); - -} - diff --git a/src/App/Router.php b/src/App/Router.php index 1dffb6b406..8826245451 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -139,6 +139,7 @@ class Router $this->routeCollector->addRoute(['GET', 'POST'], '/logout', Module\Logout::class); $this->routeCollector->addRoute(['GET'], '/magic', Module\Magic::class); $this->routeCollector->addRoute(['GET'], '/manifest', Module\Manifest::class); + $this->routeCollector->addRoute(['GET'], '/modexp/{nick}', Module\PublicRSAKey::class); $this->routeCollector->addRoute(['GET'], '/nodeinfo/1.0', Module\NodeInfo::class); $this->routeCollector->addRoute(['GET'], '/nogroup', Module\Group::class); $this->routeCollector->addRoute(['GET'], '/objects/{guid}', Module\Objects::class); diff --git a/src/Module/PublicRSAKey.php b/src/Module/PublicRSAKey.php new file mode 100644 index 0000000000..ed099616ad --- /dev/null +++ b/src/Module/PublicRSAKey.php @@ -0,0 +1,47 @@ +argc !== 2) { + throw new BadRequestException(); + } + + // @TODO: Replace with parameter from router + $nick = $app->argv[1]; + + $user = User::getByNickname($nick, ['spubkey']); + if (empty($user) || empty($user['spubkey'])) { + throw new BadRequestException(); + } + + $lines = explode("\n", $user['spubkey']); + unset($lines[0]); + unset($lines[count($lines)]); + + $asnString = base64_decode(implode('', $lines)); + $asnBase = ASN_BASE::parseASNString($asnString); + + $m = $asnBase[0]->asnData[1]->asnData[0]->asnData[0]->asnData; + $e = $asnBase[0]->asnData[1]->asnData[0]->asnData[1]->asnData; + + header('Content-type: application/magic-public-key'); + echo 'RSA' . '.' . $m . '.' . $e; + + exit(); + } +} From 510c150156f3d89261bda00ac54ed2e5beaa7bab Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 19:06:51 +0200 Subject: [PATCH 16/18] Move mod/robots_txt to src/Module/RobotsTxt --- mod/robots_txt.php | 30 ------------------------------ src/App/Router.php | 1 + src/Module/RobotsTxt.php | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 30 deletions(-) delete mode 100644 mod/robots_txt.php create mode 100644 src/Module/RobotsTxt.php diff --git a/mod/robots_txt.php b/mod/robots_txt.php deleted file mode 100644 index 0575742dd8..0000000000 --- a/mod/robots_txt.php +++ /dev/null @@ -1,30 +0,0 @@ -addRoute(['GET'], '/{sub1}/{sub2}/{url}' , Module\Proxy::class); }); $this->routeCollector->addRoute(['GET', 'POST'], '/register', Module\Register::class); + $this->routeCollector->addRoute(['GET'], '/robots.txt', Module\RobotsTxt::class); $this->routeCollector->addRoute(['GET'], '/rsd.xml', Module\ReallySimpleDiscovery::class); $this->routeCollector->addRoute(['GET'], '/statistics.json', Module\Statistics::class); $this->routeCollector->addRoute(['GET'], '/tos', Module\Tos::class); diff --git a/src/Module/RobotsTxt.php b/src/Module/RobotsTxt.php new file mode 100644 index 0000000000..3648f6f9fb --- /dev/null +++ b/src/Module/RobotsTxt.php @@ -0,0 +1,27 @@ + Date: Sun, 5 May 2019 19:15:33 +0200 Subject: [PATCH 17/18] Move mod/viewsrc to src/Module/ItemBody --- mod/viewsrc.php | 35 --------------------------------- src/App/Router.php | 1 + src/Module/ItemBody.php | 43 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 35 deletions(-) delete mode 100644 mod/viewsrc.php create mode 100644 src/Module/ItemBody.php diff --git a/mod/viewsrc.php b/mod/viewsrc.php deleted file mode 100644 index 55eb0b990c..0000000000 --- a/mod/viewsrc.php +++ /dev/null @@ -1,35 +0,0 @@ -argc > 1) ? intval($a->argv[1]) : 0); - - if (!$item_id) { - throw new \Friendica\Network\HTTPException\NotFoundException(L10n::t('Item not found.')); - } - - $item = Item::selectFirst(['body'], ['uid' => local_user(), 'id' => $item_id]); - - if (DBA::isResult($item)) { - if ($a->isAjax()) { - echo str_replace("\n", '
', $item['body']); - exit(); - } else { - $o .= str_replace("\n", '
', $item['body']); - } - } - return $o; -} diff --git a/src/App/Router.php b/src/App/Router.php index 1dffb6b406..f1d5c09d74 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -167,6 +167,7 @@ class Router $this->routeCollector->addRoute(['GET'], '/rsd.xml', Module\ReallySimpleDiscovery::class); $this->routeCollector->addRoute(['GET'], '/statistics.json', Module\Statistics::class); $this->routeCollector->addRoute(['GET'], '/tos', Module\Tos::class); + $this->routeCollector->addRoute(['GET'], '/viewsrc/{item:\d+}', Module\ItemBody::class); $this->routeCollector->addRoute(['GET'], '/webfinger', Module\WebFinger::class); $this->routeCollector->addRoute(['GET'], '/xrd', Module\Xrd::class); } diff --git a/src/Module/ItemBody.php b/src/Module/ItemBody.php new file mode 100644 index 0000000000..ee50b52b28 --- /dev/null +++ b/src/Module/ItemBody.php @@ -0,0 +1,43 @@ +argc > 1) ? intval($app->argv[1]) : 0); + + if (!$itemId) { + throw new HTTPException\NotFoundException(L10n::t('Item not found.')); + } + + $item = Item::selectFirst(['body'], ['uid' => local_user(), 'id' => $itemId]); + + if (!empty($item)) { + if ($app->isAjax()) { + echo str_replace("\n", '
', $item['body']); + exit(); + } else { + return str_replace("\n", '
', $item['body']); + } + } else { + throw new HTTPException\NotFoundException(L10n::t('Item not found.')); + } + } +} From 3f71a51d210683a0f591d2a507bdfb95064536d3 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 5 May 2019 19:23:03 +0200 Subject: [PATCH 18/18] Move mod/pretheme to src/Module/ThemeDetails --- mod/pretheme.php | 25 ------------------------- src/App/Router.php | 1 + src/Module/ThemeDetails.php | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 25 deletions(-) delete mode 100644 mod/pretheme.php create mode 100644 src/Module/ThemeDetails.php diff --git a/mod/pretheme.php b/mod/pretheme.php deleted file mode 100644 index 14d1f2b9ea..0000000000 --- a/mod/pretheme.php +++ /dev/null @@ -1,25 +0,0 @@ - Theme::getScreenshot($theme), 'desc' => $desc, 'version' => $version, 'credits' => $credits]); - } - - exit(); -} diff --git a/src/App/Router.php b/src/App/Router.php index 1dffb6b406..40f2d5ebcc 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -153,6 +153,7 @@ class Router $collector->addRoute(['GET'], '/{type}/{name}', Module\Photo::class); $collector->addRoute(['GET'], '/{type}/{customize}/{name}', Module\Photo::class); }); + $this->routeCollector->addRoute(['GET'], '/pretheme', Module\ThemeDetails::class); $this->routeCollector->addGroup('/profile', function (RouteCollector $collector) { $collector->addRoute(['GET'], '/{nickname}', Module\Profile::class); $collector->addRoute(['GET'], '/{profile:\d+}/view', Module\Profile::class); diff --git a/src/Module/ThemeDetails.php b/src/Module/ThemeDetails.php new file mode 100644 index 0000000000..7b53d1cfde --- /dev/null +++ b/src/Module/ThemeDetails.php @@ -0,0 +1,33 @@ + Theme::getScreenshot($theme), + 'desc' => $description, + 'version' => $version, + 'credits' => $credits, + ]); + } + exit(); + } +}