@@ -36,22 +36,18 @@ import java.util.HashMap; | |||
import java.util.Map; | |||
import static bubble.ApiConstants.HOME_DIR; | |||
import static bubble.rule.RequestModifierRule.ICON_JS_TEMPLATE; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.util.io.regex.RegexReplacementFilter.DEFAULT_PREFIX_REPLACEMENT_WITH_MATCH; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.util.security.ShaUtil.sha256_hex; | |||
import static org.cobbzilla.util.string.StringUtil.UTF8cs; | |||
public abstract class AbstractAppRuleDriver implements AppRuleDriver { | |||
public static final int RESPONSE_BUFSIZ = (int) (64 * Bytes.KB); | |||
public static final String CTX_JS_PREFIX = "JS_PREFIX"; | |||
public static final String CTX_BUBBLE_REQUEST_ID = "BUBBLE_REQUEST_ID"; | |||
public static final String CTX_BUBBLE_DATA_ID = "BUBBLE_DATA_ID"; | |||
public static final String CTX_BUBBLE_HOME = "BUBBLE_HOME"; | |||
public static final String CTX_SITE = "SITE"; | |||
@Autowired protected BubbleConfiguration configuration; | |||
@Autowired protected AppDataDAO appDataDAO; | |||
@Autowired protected AppSiteDAO appSiteDAO; | |||
@@ -139,11 +135,12 @@ public abstract class AbstractAppRuleDriver implements AppRuleDriver { | |||
Map<String, Object> filterCtx, | |||
String bubbleJsTemplate, | |||
String defaultSiteTemplate, | |||
String siteJsInsertionVar) { | |||
String siteJsInsertionVar, | |||
boolean showIcon) { | |||
final RequestModifierConfig modConfig = requestModConfig(); | |||
final String replacement = DEFAULT_PREFIX_REPLACEMENT_WITH_MATCH | |||
+ scriptOpen(filterRequest, modConfig.getScriptOpenNonce(), modConfig.getScriptOpenNoNonce()) | |||
+ getBubbleJs(filterRequest.getId(), filterCtx, bubbleJsTemplate, defaultSiteTemplate, siteJsInsertionVar) | |||
+ getBubbleJs(filterRequest.getId(), filterCtx, bubbleJsTemplate, defaultSiteTemplate, siteJsInsertionVar, showIcon) | |||
+ getScriptClose(); | |||
final RegexReplacementFilter filter = new RegexReplacementFilter(getInsertionRegex(), replacement); | |||
@@ -162,20 +159,35 @@ public abstract class AbstractAppRuleDriver implements AppRuleDriver { | |||
Map<String, Object> filterCtx, | |||
String bubbleJsTemplate, | |||
String defaultSiteTemplate, | |||
String siteJsInsertionVar) { | |||
String siteJsInsertionVar, | |||
boolean showIcon) { | |||
final Map<String, Object> ctx = getBubbleJsContext(requestId, filterCtx); | |||
if (!empty(siteJsInsertionVar) && !empty(defaultSiteTemplate)) { | |||
final String siteJs = HandlebarsUtil.apply(getHandlebars(), getSiteJsTemplate(defaultSiteTemplate), ctx); | |||
ctx.put(siteJsInsertionVar, siteJs); | |||
} | |||
if (showIcon) { | |||
ctx.put(CTX_ICON_JS, HandlebarsUtil.apply(getHandlebars(), ICON_JS_TEMPLATE, ctx)); | |||
} | |||
return HandlebarsUtil.apply(getHandlebars(), bubbleJsTemplate, ctx); | |||
} | |||
public static final String CTX_JS_PREFIX = "JS_PREFIX"; | |||
public static final String CTX_PAGE_PREFIX = "PAGE_PREFIX"; | |||
public static final String CTX_BUBBLE_REQUEST_ID = "BUBBLE_REQUEST_ID"; | |||
public static final String CTX_BUBBLE_DATA_ID = "BUBBLE_DATA_ID"; | |||
public static final String CTX_BUBBLE_HOME = "BUBBLE_HOME"; | |||
public static final String CTX_SITE = "SITE"; | |||
public static final String CTX_ICON_JS = "ICON_JS"; | |||
private String getPagePrefix(String requestId) { return "__bubble_"+sha256_hex(requestId); } | |||
private String getJsPrefix(String requestId) { return "__bubble_"+sha256_hex(requestId+"_"+getClass().getName()); } | |||
protected Map<String, Object> getBubbleJsContext(String requestId, Map<String, Object> filterCtx) { | |||
final Map<String, Object> ctx = new HashMap<>(); | |||
ctx.put(CTX_JS_PREFIX, AppRuleDriver.getJsPrefix(requestId)); | |||
ctx.put(CTX_PAGE_PREFIX, getPagePrefix(requestId)); | |||
ctx.put(CTX_JS_PREFIX, getJsPrefix(requestId)); | |||
ctx.put(CTX_BUBBLE_REQUEST_ID, requestId); | |||
ctx.put(CTX_BUBBLE_HOME, configuration.getPublicUriBase()); | |||
ctx.put(CTX_SITE, getSiteName(matcher)); | |||
@@ -114,8 +114,6 @@ public interface AppRuleDriver { | |||
default Handlebars getHandlebars() { return null; } | |||
static String getJsPrefix(String requestId) { return "__bubble_"+sha256_hex(requestId)+"_"; } | |||
default String locateResource(String res) { | |||
if (!res.startsWith("@")) return res; | |||
final String prefix = getPackagePath(getClass()) + "/" + getClass().getSimpleName(); | |||
@@ -4,8 +4,14 @@ | |||
*/ | |||
package bubble.rule; | |||
import static org.cobbzilla.util.io.StreamUtil.stream2string; | |||
import static org.cobbzilla.util.string.StringUtil.getPackagePath; | |||
public interface RequestModifierRule { | |||
RequestModifierConfig getRequestModifierConfig (); | |||
Class<RequestModifierRule> RMR = RequestModifierRule.class; | |||
String ICON_JS_TEMPLATE = stream2string(getPackagePath(RMR)+"/"+ RMR.getSimpleName()+"_icon.js.hbs"); | |||
} |
@@ -40,6 +40,7 @@ import static org.cobbzilla.util.http.HttpContentTypes.isHtml; | |||
import static org.cobbzilla.util.io.StreamUtil.stream2string; | |||
import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.util.string.StringUtil.EMPTY; | |||
import static org.cobbzilla.util.string.StringUtil.getPackagePath; | |||
@Slf4j | |||
@@ -286,6 +287,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements | |||
} | |||
public static final String FILTER_CTX_DECISION = "decision"; | |||
public static final String BLOCK_STATS_JS = "BLOCK_STATS_JS"; | |||
@Override public InputStream doFilterResponse(FilterHttpRequest filterRequest, InputStream in) { | |||
@@ -333,24 +335,24 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver implements | |||
return in; | |||
} | |||
if (!bubbleBlockConfig.inPageBlocks() && !bubbleBlockConfig.showStats()) { | |||
final boolean showStats = bubbleBlockConfig.showStats(); | |||
if (!bubbleBlockConfig.inPageBlocks() && !showStats) { | |||
if (log.isInfoEnabled()) log.info(prefix + "SEND: both inPageBlocks and showStats are false, returning as-is"); | |||
return in; | |||
} | |||
if (bubbleBlockConfig.inPageBlocks() && bubbleBlockConfig.showStats()) { | |||
return filterInsertJs(in, filterRequest, filterCtx, BUBBLE_JS_BOTH_TEMPLATE, null, null); | |||
if (bubbleBlockConfig.inPageBlocks() && showStats) { | |||
return filterInsertJs(in, filterRequest, filterCtx, BUBBLE_JS_TEMPLATE, BUBBLE_JS_STATS_TEMPLATE, BLOCK_STATS_JS, showStats); | |||
} | |||
if (bubbleBlockConfig.inPageBlocks()) { | |||
return filterInsertJs(in, filterRequest, filterCtx, BUBBLE_JS_TEMPLATE, null, null); | |||
return filterInsertJs(in, filterRequest, filterCtx, BUBBLE_JS_TEMPLATE, EMPTY, BLOCK_STATS_JS, showStats); | |||
} | |||
log.warn(prefix+"doFilterResponse: inserting JS for stats..."); | |||
return filterInsertJs(in, filterRequest, filterCtx, BUBBLE_JS_STATS_TEMPLATE, null, null); | |||
log.warn(prefix+"inserting JS for stats..."); | |||
return filterInsertJs(in, filterRequest, filterCtx, BUBBLE_JS_STATS_TEMPLATE, null, null, showStats); | |||
} | |||
public static final Class<BubbleBlockRuleDriver> BB = BubbleBlockRuleDriver.class; | |||
public static final String BUBBLE_JS_TEMPLATE = stream2string(getPackagePath(BB)+"/"+ BB.getSimpleName()+".js.hbs"); | |||
public static final String BUBBLE_JS_STATS_TEMPLATE = stream2string(getPackagePath(BB)+"/"+ BB.getSimpleName()+"_stats.js.hbs"); | |||
public static final String BUBBLE_JS_BOTH_TEMPLATE = BUBBLE_JS_TEMPLATE + "\n\n" + BUBBLE_JS_STATS_TEMPLATE; | |||
private static final String CTX_BUBBLE_SELECTORS = "BUBBLE_SELECTORS_JSON"; | |||
private static final String CTX_BUBBLE_BLACKLIST = "BUBBLE_BLACKLIST_JSON"; | |||
@@ -8,7 +8,6 @@ import bubble.resources.stream.FilterHttpRequest; | |||
import bubble.rule.AbstractAppRuleDriver; | |||
import bubble.rule.RequestModifierConfig; | |||
import bubble.rule.RequestModifierRule; | |||
import bubble.rule.bblock.BubbleBlockConfig; | |||
import lombok.Getter; | |||
import lombok.extern.slf4j.Slf4j; | |||
@@ -16,7 +15,6 @@ import java.io.InputStream; | |||
import static org.cobbzilla.util.http.HttpContentTypes.isHtml; | |||
import static org.cobbzilla.util.io.StreamUtil.stream2string; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.util.string.StringUtil.getPackagePath; | |||
@Slf4j | |||
@@ -37,7 +35,7 @@ public class JsUserBlockerRuleDriver extends AbstractAppRuleDriver implements Re | |||
@Override public InputStream doFilterResponse(FilterHttpRequest filterRequest, InputStream in) { | |||
if (!isHtml(filterRequest.getContentType())) return in; | |||
log.warn("doFilterResponse: inserting JS, getRequestModifierConfig()="+json(getRequestModifierConfig())); | |||
return filterInsertJs(in, filterRequest, null, BUBBLE_JS_TEMPLATE, getDefaultSiteJsTemplate(), CTX_APPLY_BLOCKS_JS); | |||
log.warn("doFilterResponse("+filterRequest.getId()+"): inserting JS"); | |||
return filterInsertJs(in, filterRequest, null, BUBBLE_JS_TEMPLATE, getDefaultSiteJsTemplate(), CTX_APPLY_BLOCKS_JS, true); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') { | |||
let {{PAGE_PREFIX}}_doc_ready = false; | |||
const {{PAGE_PREFIX}}_interval = 50; | |||
{{PAGE_PREFIX}}_icon_status = []; | |||
function {{PAGE_PREFIX}}_onReady(callback) { | |||
const intervalId = window.setInterval(function() { | |||
if (document.getElementsByTagName('body')[0] !== undefined) { | |||
{{PAGE_PREFIX}}_doc_ready = true; | |||
window.clearInterval(intervalId); | |||
callback.call(this); | |||
} | |||
}, {{PAGE_PREFIX}}_interval); | |||
} | |||
{{PAGE_PREFIX}}_onReady(function() { | |||
const controlDivId = '{{PAGE_PREFIX}}_controlDiv'; | |||
let bubbleControlDiv = document.getElementById(controlDivId); | |||
if (bubbleControlDiv === null) { | |||
bubbleControlDiv = document.createElement('div'); | |||
bubbleControlDiv.id = controlDivId; | |||
bubbleControlDiv.style.position = 'fixed'; | |||
bubbleControlDiv.style.bottom = '0'; | |||
bubbleControlDiv.style.right = '0'; | |||
document.getElementsByTagName('body')[0].appendChild(bubbleControlDiv); | |||
} | |||
for (let i=0; i<{{PAGE_PREFIX}}_icon_status.length; i++) { | |||
bubbleControlDiv.innerHTML = bubbleControlDiv.innerHTML + {{PAGE_PREFIX}}_icon_status[i].iconHtml; | |||
} | |||
}); | |||
} |
@@ -326,3 +326,5 @@ function {{JS_PREFIX}}_process_filters() { | |||
{{JS_PREFIX}}_process_filters(); | |||
window.setInterval({{JS_PREFIX}}_process_filters, {{JS_PREFIX}}_idle_interval); | |||
}); | |||
{{{BLOCK_STATS_JS}}} |
@@ -1,3 +1,10 @@ | |||
// | |||
// block stats js goes here | |||
// | |||
{{{ICON_JS}}} | |||
{{PAGE_PREFIX}}_icon_status.push({ | |||
jsPrefix: '{{JS_PREFIX}}', | |||
iconHtml: '<br/><a href="{{{BUBBLE_HOME}}}/app/BubbleBlock/view/last_24_hours"><img width="64" src="/__bubble/api/filter/assets/{{BUBBLE_REQUEST_ID}}/BubbleBlock/icon?raw=true"/></a>' | |||
}); | |||
console.log("BubbleBlock pushed icon, {{PAGE_PREFIX}}_icon_status="+JSON.stringify({{PAGE_PREFIX}}_icon_status)); |
@@ -1,19 +1,10 @@ | |||
let {{JS_PREFIX}}_blocked_users = null; | |||
let {{JS_PREFIX}}_doc_ready = false; | |||
const {{JS_PREFIX}}_request_id = '{{BUBBLE_REQUEST_ID}}'; | |||
let {{JS_PREFIX}}_doc_ready = false; | |||
const {{JS_PREFIX}}_interval = 50; | |||
const {{JS_PREFIX}}_idle_interval = 1000; | |||
function {{JS_PREFIX}}_onReady(callback) { | |||
const intervalId = window.setInterval(function() { | |||
if (document.getElementsByTagName('body')[0] !== undefined) { | |||
{{JS_PREFIX}}_doc_ready = true; | |||
window.clearInterval(intervalId); | |||
callback.call(this); | |||
} | |||
}, {{JS_PREFIX}}_interval); | |||
} | |||
function {{JS_PREFIX}}_fetch_blocks (do_apply) { | |||
const requestOptions = { method: 'GET' }; | |||
const blocked_users_url = '/__bubble/api/filter/data/{{BUBBLE_DATA_ID}}/read'; | |||
@@ -60,16 +51,10 @@ function {{JS_PREFIX}}_block_user (author) { | |||
{{{APPLY_BLOCKS_JS}}} | |||
{{JS_PREFIX}}_onReady(function() { | |||
const controlDivId = '{{JS_PREFIX}}_controlDiv'; | |||
let bubbleControlDiv = document.getElementById(controlDivId); | |||
if (bubbleControlDiv === null) { | |||
bubbleControlDiv = document.createElement('div'); | |||
bubbleControlDiv.id = controlDivId; | |||
bubbleControlDiv.style.position = 'fixed'; | |||
bubbleControlDiv.style.bottom = '0'; | |||
bubbleControlDiv.style.right = '0'; | |||
document.getElementsByTagName('body')[0].appendChild(bubbleControlDiv); | |||
} | |||
bubbleControlDiv.innerHTML = bubbleControlDiv.innerHTML + '<br/><a href="{{{BUBBLE_HOME}}}/app/UserBlocker/site/{{SITE}}/view/blocked_users"><img width="64" src="/__bubble/api/filter/assets/{{BUBBLE_REQUEST_ID}}/UserBlocker/icon?raw=true"/></a>'; | |||
{{{ICON_JS}}} | |||
{{PAGE_PREFIX}}_icon_status.push({ | |||
jsPrefix: '{{JS_PREFIX}}', | |||
iconHtml: '<br/><a href="{{{BUBBLE_HOME}}}/app/UserBlocker/site/{{SITE}}/view/blocked_users"><img width="64" src="/__bubble/api/filter/assets/{{BUBBLE_REQUEST_ID}}/UserBlocker/icon?raw=true"/></a>' | |||
}); | |||
console.log("JsUserBlocker pushed icon, {{PAGE_PREFIX}}_icon_status="+JSON.stringify({{PAGE_PREFIX}}_icon_status)); |