You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnosygnu_xowa/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func.java

248 lines
13 KiB

/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2020 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.xtns.pagebanners;
import gplx.Bry_;
import gplx.Bry_bfr;
import gplx.Bry_bfr_;
import gplx.Bry_find_;
import gplx.Byte_ascii;
import gplx.Double_;
import gplx.Gfo_usr_dlg_;
import gplx.Hash_adp_bry;
import gplx.List_adp;
import gplx.List_adp_;
import gplx.Ordered_hash;
import gplx.Ordered_hash_;
import gplx.langs.htmls.encoders.Gfo_url_encoder_;
import gplx.langs.mustaches.JsonMustacheNde;
import gplx.langs.mustaches.Mustache_bfr;
import gplx.langs.mustaches.Mustache_render_ctx;
import gplx.xowa.Xoa_ttl;
import gplx.xowa.Xoae_app;
import gplx.xowa.Xoae_page;
import gplx.xowa.Xow_wiki;
import gplx.xowa.Xowe_wiki;
import gplx.xowa.files.Xof_exec_tid;
import gplx.xowa.files.Xof_file_itm;
import gplx.xowa.htmls.core.htmls.Xoh_wtr_ctx;
import gplx.xowa.langs.kwds.Xol_kwd_grp_;
import gplx.xowa.parsers.Xop_ctx;
import gplx.xowa.parsers.lnkis.Xop_lnki_tkn;
import gplx.xowa.parsers.lnkis.files.Xop_file_logger_;
import gplx.xowa.parsers.tmpls.Arg_nde_tkn;
import gplx.xowa.parsers.tmpls.Xop_func_arg_itm;
import gplx.xowa.parsers.tmpls.Xot_invk;
import gplx.xowa.parsers.utils.Xop_sanitizer;
import gplx.xowa.xtns.pfuncs.Pf_func;
import gplx.xowa.xtns.pfuncs.Pf_func_base;
// REF.MW:https://github.com/wikimedia/mediawiki-extensions-WikidataPageBanner/blob/master/includes/WikidataPageBannerFunctions.php
// TODO.DATE:2019-11-11: calculate enableToc and hasPosition;
public class Pgbnr_func extends Pf_func_base {
@Override public int Id() {return Xol_kwd_grp_.Id_pagebanner;}
@Override public Pf_func New(int id, byte[] name) {return new Pgbnr_func().Name_(name);}
@Override public void Func_evaluate(Bry_bfr bfr, Xop_ctx ctx, Xot_invk caller, Xot_invk self, byte[] src) { // {{PAGEBANNER}} appears on page; WikidataPageBanner.hooks.php|addCustomBanner
Xowe_wiki wiki = ctx.Wiki(); Xoae_page page = ctx.Page();
Pgbnr_xtn_mgr xtn_mgr = wiki.Xtn_mgr().Xtn_pgbnr();
Pgbnr_cfg cfg = xtn_mgr.Cfg();
Xoa_ttl ttl = page.Ttl();
if (!cfg.Chk_pgbnr_allowed(ttl, wiki)) return;
byte[] tooltip = ttl.Page_txt(), title = ttl.Page_txt(), toc = Bry_.Empty, origin_x = Bry_.Empty;
boolean bottomtoc = false;;
double data_pos_x = 0, data_pos_y = 0;
List_adp icons_list = null;
Bry_bfr tmp_bfr = Bry_bfr_.New();
int args_len = self.Args_len();
Xop_func_arg_itm func_arg = new Xop_func_arg_itm();
for (int i = 0; i < args_len; ++i) {
Arg_nde_tkn arg = self.Args_get_by_idx(i);
func_arg.Set(tmp_bfr, ctx, src, caller, self, arg);
byte[] key = func_arg.key;
byte[] val = func_arg.val;
if (key == Bry_.Empty) continue; // ignore blank args; EX:{{PAGEBANNER:A.png|\n|toc=yes}}
int tid = arg_hash.Get_as_int_or(key, -1);
if (tid == Arg__pgname)
tooltip = title = val;
if (tid == Arg__tooltip) // note that this overrides pgname above
tooltip = val;
if (tid == Arg__bottomtoc && Bry_.Eq(val, Bry__yes))
bottomtoc = true;
if (tid == Arg__toc && Bry_.Eq(val, Bry__yes)) // REF.MW:addToc
toc = Bry_.Empty; // note that "" will be overwritten later by actual toc html
if ( tid == -1 // note that "icon-*" won't have a tid
&& Bry_.Has_at_bgn(key, Bry__icon) // if (substr($key, 0, 5) === 'icon-')
&& Bry_.Len(key) > 5 // if ( !isset( $iconname) )
&& Bry_.Len_gt_0(val) // if ( !isset( $$value ) )
) { // REF.MW:addIcons
tid = Arg__icon;
if (icons_list == null) icons_list = List_adp_.New();
byte[] icon_key = Bry_.Mid(key, 5);
byte[] icon_name = Xop_sanitizer.Escape_cls(icon_key);
byte[] icon_title = icon_name;
Xoa_ttl icon_ttl = wiki.Ttl_parse(val);
byte[] icon_href = Bry__icon_href_dflt;
if (icon_ttl != null) {
icon_href = Bry_.Add(gplx.xowa.htmls.hrefs.Xoh_href_.Bry__wiki, icon_ttl.Page_db());
icon_title = icon_ttl.Page_txt();
}
icons_list.Add(new Pgbnr_icon(tmp_bfr, icon_name, icon_title, icon_href));
// itm.Add_new_icon(tmp_bfr, icon_name, icon_title, icon_href);
}
if (tid == Arg__origin) { // REF.MW:addFocus
double tmp_data_pos_x = Double_.NaN, tmp_data_pos_y = Double_.NaN;
int comma_pos = Bry_find_.Find_fwd(val, Byte_ascii.Comma);
if (comma_pos != Bry_find_.Not_found) {
tmp_data_pos_x = Bry_.To_double_or(val, 0, comma_pos, Double_.NaN);
if (!Double_.IsNaN(tmp_data_pos_x)) {
if (tmp_data_pos_x >= -1 && tmp_data_pos_x <= 1) {
data_pos_x = tmp_data_pos_x;
origin_x = tmp_data_pos_x <= .25d ? Bry__origin_x__left : Bry__origin_x__right;
}
}
if (!Double_.IsNaN(tmp_data_pos_y)) {
if (tmp_data_pos_y >= -1 && tmp_data_pos_y <= 1)
data_pos_y = tmp_data_pos_y;
}
}
}
if (tid == -1) Gfo_usr_dlg_.Instance.Warn_many("", "", "unknown arg type; page=~{0} key=~{1} val=~{2}", page.Ttl().Full_db(), key, val);
}
byte[] banner_name = Eval_argx(ctx, src, caller, self);
Xoa_ttl banner_ttl = wiki.Ttl_parse(banner_name); // NOTE: MW also creates title to auto-register page and image in imagelinks
if (banner_ttl == null) // if ttl is invalid, get it from wikidata; PAGE:en.v:Diving_the_Cape_Peninsula_and_False_Bay/Whale_Rock; DATE:2016-07-12
banner_ttl = Get_wikidata_banner(wiki.Appe(), wiki, wiki.Xtn_mgr().Xtn_pgbnr().Cfg(), page.Ttl());
if (banner_ttl == null) { // if ttl is still invalid, exit now else will fail with nullref below; PAGE:en.v:Peterborough (New Hampshire); DATE:2016-07-12
Gfo_usr_dlg_.Instance.Warn_many("", "", "banner file is invalid; page=~{0} banner=~{1}", page.Url_bry_safe(), banner_name);
return;
}
Xof_file_itm banner_file_itm = File__make_tkn(ctx, Xop_file_logger_.Tid__pgbnr_main, banner_ttl, Xop_lnki_tkn.Width_null, Xop_lnki_tkn.Height_null);
Pgbnr_itm itm = new Pgbnr_itm();
itm.Init_from_wtxt(banner_ttl, banner_file_itm, tooltip, title, bottomtoc, toc, data_pos_x, data_pos_y, origin_x, icons_list == null ? Pgbnr_icon.Ary_empty : (Pgbnr_icon[])icons_list.To_ary_and_clear(Pgbnr_icon.class));
page.Html_data().Xtn_pgbnr_(itm);
page.Html_data().Head_mgr().Itm__pgbnr().Enabled_y_(); // register css / js during parse stage
page.Wtxt().Toc().Flag__toc_(true); // NOTE: must mark toc_manual else will show 2nd TOC in edit mode; DATE:2016-07-10
}
public static void Add_banner(Bry_bfr bfr, Xoae_page wpg, Xop_ctx ctx, Xoh_wtr_ctx hctx, Pgbnr_itm itm) {
Xowe_wiki wiki = ctx.Wiki(); Xoae_app app = wiki.Appe();
Pgbnr_cfg cfg = wiki.Xtn_mgr().Xtn_pgbnr().Cfg(); if (!cfg.enabled) return;
Xoa_ttl ttl = wpg.Ttl();
Xoa_ttl banner_ttl = null; byte[] banner_html = null;
if (itm != null) { // {{PAGEBANNER}} exists in wikitext
itm.Init_hdump(hctx.Mode_is_hdump());
banner_ttl = itm.banner_ttl;
banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm);
if (banner_html == null) { // no banner; try again using title from wikidata; note that this should only happen if no banner_ttl or banner_ttl is invalid; EX:{{PAGEBANNER:|toc=yes}}
banner_ttl = Get_wikidata_banner(app, wiki, cfg, ttl);
banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm);
}
}
else if ( ttl.Ns().Id_is_main() // {{PAGEBANNER}} missing, but wiki is marked as enable_default_banner
&& cfg.enable_default_banner
&& cfg.Chk_pgbnr_allowed(ttl, wiki)
) {
banner_ttl = Get_wikidata_banner(app, wiki, cfg, ttl);
if (banner_ttl == null)
banner_ttl = wiki.Ttl_parse(cfg.dflt_img_title);
Xof_file_itm banner_file_itm = File__make_tkn(ctx, Xop_file_logger_.Tid__pgbnr_main, banner_ttl, Xop_lnki_tkn.Width_null, Xop_lnki_tkn.Height_null);
itm = new Pgbnr_itm();
itm.Init_from_wtxt(banner_ttl, banner_file_itm, Bry_.Empty, Bry_.Empty, false, Bry_.Empty, 0, 0, Bry_.Empty, Pgbnr_icon.Ary_empty);
itm.Init_hdump(hctx.Mode_is_hdump());
banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm);
}
if (banner_html != null) {
bfr.Add(banner_html);
}
}
public static byte[] Get_banner_html(Xowe_wiki wiki, Xop_ctx ctx, Xoh_wtr_ctx hctx, Pgbnr_cfg cfg, Xoa_ttl banner_ttl, Pgbnr_itm itm) {
byte[][] urls = Get_standard_size_urls(wiki, cfg, banner_ttl); if (urls == null) return null;
Bry_bfr tmp_bfr = Bry_bfr_.New();
int urls_len = urls.length;
int[] sizes = cfg.standard_sizes;
for (int i = 0; i < urls_len; ++i) {
int size = sizes[i];
if (i != 0) tmp_bfr.Add_byte_comma();
tmp_bfr.Add(urls[i]).Add_byte_space().Add_int_variable(size).Add_byte(Byte_ascii.Ltr_w); // REF.MW: $srcset[] = "$url {$size}w";
}
byte[] srcset = tmp_bfr.To_bry_and_clear();
byte[] banner_url = itm.banner_img_src != null ? itm.banner_img_src : urls.length == 0 ? Bry_.Empty : urls[urls_len - 1]; // gets largest url
Xof_file_itm banner_file_itm = itm.banner_file_itm;
int max_width = banner_file_itm.Orig_w(); // $file = wfFindFile( banner_file ); $options['max_width'] = $file->getWidth();
// Provide information to the logic-less template about whether it is a panorama or not.
boolean isPanorama = banner_file_itm.Orig_w() > (banner_file_itm.Orig_h() * 2);
byte[] banner_file = null; // $bannerfile->getLocalUrl();
byte[] toc_html = null;
if (hctx.Mode_is_hdump()) {
gplx.xowa.htmls.core.wkrs.tocs.Xoh_toc_wtr.Write_tag(tmp_bfr, true);
toc_html = tmp_bfr.To_bry_and_clear();
banner_file = Bry_.Add(gplx.xowa.htmls.hrefs.Xoh_href_.Bry__wiki, gplx.xowa.wikis.nss.Xow_ns_.Bry__file, Byte_ascii.Colon_bry
, Gfo_url_encoder_.Href.Encode(banner_ttl.Full_db())); // NOTE: must encode so "'" becomes "%27", not "&#39;"; PAGE:en.v:'s-Hertogenbosch; DATE:2016-07-12
}
else {
ctx.Page().Html_data().Toc_mgr().To_html(tmp_bfr, Xoh_wtr_ctx.Basic, true);
toc_html = tmp_bfr.To_bry_and_clear();
}
itm.Init_from_html(max_width, banner_file, banner_url, srcset, cfg.enable_heading_override, toc_html, isPanorama);
Mustache_render_ctx mctx = new Mustache_render_ctx().Init(new JsonMustacheNde(itm.Mustache__json()));
// Mustache_render_ctx mctx = new Mustache_render_ctx().Init(itm);
Mustache_bfr mbfr = Mustache_bfr.New_bfr(tmp_bfr);
wiki.Xtn_mgr().Xtn_pgbnr().Template_root().Render(mbfr, mctx);
return mbfr.To_bry_and_clear();
}
private static byte[][] Get_standard_size_urls(Xow_wiki wiki, Pgbnr_cfg cfg, Xoa_ttl banner_ttl) {
Ordered_hash hash = Ordered_hash_.New_bry();
int[] sizes = cfg.standard_sizes;
int sizes_len = sizes.length;
for (int i = 0; i < sizes_len; ++i) {
byte[] url = Get_image_url(wiki, banner_ttl, sizes[i]);
if (url != null)
hash.Add_if_dupe_use_1st(url, url);
}
return (byte[][])hash.To_ary_and_clear(byte[].class);
}
private static byte[] Get_image_url(Xow_wiki wiki, Xoa_ttl banner_ttl, int width) {
// Object file = new Object(); // $file = wfFindFile( file_ttl );
// if (file == null) return null;
if (width >= 0 && width <= 3000) {
// $mto = $file->transform( array( 'width' => $imagewidth ) );
byte[] url = new byte[0]; // $url = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT );
return url;
}
else
return new byte[0]; // $file->getFullUrl();
}
private static Xoa_ttl Get_wikidata_banner(Xoae_app app, Xow_wiki wiki, Pgbnr_cfg cfg, Xoa_ttl ttl) {
byte[] rv = app.Wiki_mgr().Wdata_mgr().Get_claim_or(wiki.Domain_itm(), ttl, cfg.dflt_img_wdata_prop, null); // don't log misses; wikivoyage pages will default to show pagebanner, and many pages may not have wikidata definitions
if (rv == null) return null;
return wiki.Ttl_parse(rv);
}
private static Xof_file_itm File__make_tkn(Xop_ctx ctx, byte tid, Xoa_ttl file_ttl, int file_w, int file_h) {
Xop_lnki_tkn lnki = ctx.Tkn_mkr().Lnki(file_w, file_h).Ttl_(file_ttl);
ctx.Page().Lnki_list().Add(lnki);
ctx.Lnki().File_logger().Log_file(tid, ctx, lnki); // NOTE: do not set file_wkr ref early (as member var); parse_all sets late
Xof_file_itm file_itm = ctx.Wiki().Html_mgr().Html_wtr().Lnki_wtr().File_wtr().Lnki_eval(Xof_exec_tid.Tid_wiki_page, ctx, ctx.Page(), lnki);
return file_itm;
}
private static final byte[] Bry__yes = Bry_.new_a7("yes"), Bry__icon = Bry_.new_a7("icon-"), Bry__icon_href_dflt = Bry_.new_a7("#"), Bry__origin_x__left = Bry_.new_a7("wpb-left"), Bry__origin_x__right = Bry_.new_a7("wpb-right");
private static final int Arg__pgname = 0, Arg__tooltip = 1, Arg__bottomtoc = 2, Arg__toc = 3, Arg__icon = 4, Arg__origin = 5;
private static final Hash_adp_bry arg_hash = Hash_adp_bry.cs().Add_str_int("pgname", Arg__pgname)
.Add_str_int("tooltip", Arg__tooltip).Add_str_int("bottomtoc", Arg__bottomtoc).Add_str_int("toc", Arg__toc).Add_str_int("origin", Arg__origin);
}