Use a different variable for each domain in a Greasemonkey script - greasemonkey

I have a Greasemonkey script that saves a list of numeric IDs as a delimited string using GM_setValue(). The script uses this list to filter out items on a bulletin board.
I'm trying to figure out the best way to make this script work for multiple domains, so each domain has its own list of IDs. As it stands now, if I filter out ID "12345" on one site, it will also be filtered on every other site.
I realize I could append the domain to each ID and search for a combination of ID + domain, but I'd prefer to save space unless it's my only choice. Ideally I'd have a separate variable for each domain.

You can use location.hostname to get the domain and then generate a key for use with GM_setValue and GM_getValue. After that, the rest of your code is the same.
For example:
// ==UserScript==
// #name _Store and retrieve a comman var that's unique to each domain
// #include http://DOMAIN_1.COM/YOUR_PATH/*
// #include http://DOMAIN_2.COM/YOUR_PATH/*
// #include http://DOMAIN_3.COM/YOUR_PATH/*
// #grant GM_getValue
// #grant GM_setValue
// ==/UserScript==
var storageKey = "IDs_" + location.hostname;
var idList = GM_getValue (storageKey, "");
console.log ("This site's list was: ", idList);
idList += ",7";
GM_setValue (storageKey, idList);
This keeps a separate idList for each domain you visit (That matches the #include, #match, and #exclude directives).

Related

How to check if two URL's lead to the same path?

I'm building a URL Shortener, and I've decided to recyle the short ID's if possible to save space in my database. How can i check if 2 URL's lead to the same path?
For example, let's say a user generates a short URL for https://google.com/.
My app generates the following short id: jkU3
So if this user visits https://tiny.url/jkU3 my express server will redirect the visitor to https://google.com/.
This works like a charm, but know let's imagine another person visits https://tiny.url/ and generates a short URL for https://google.com. And another one comes and generates a short URL for https://www.google.com/, and another one comes and generates one for https://www.google.com. You get the point..
So far my app would have wasted 4 short ID's.
How can i prevent this from happening? Is there a regex for this?
This is the current code I have for generating short URL's:
app.post("/", (req: Request, res: Response) => {
const shortUrl: string = nanoid(4);
const destination: string = req.body.destination;
UrlSchema.create({
_id: mongoose.Types.ObjectId(),
origin: shortUrl,
destination: destination,
}).then(() => {
// Unique Id
res.json(shortUrl);
});
});
Before creating a new entry you can check work destination with
const existing = await UrlSchema.findOne({destination:req.body.destination});
if(!existing){
// create new
} else{
// return same
}
This way you will be creating destination if it does not exist already. You can remove tariling slash(/) if it exists to match URLs better,
You've listed four slightly different URLs:
https://www.google.com
https://google.com
https://www.google.com/
https://google.com/
None of these are technically the same https request, though it sounds like you want to assume that the / at the end is optional and thus does not make it a different target URL.
The last two are not guaranteed to be the same host as the first two. For google.com and www.google.com, they are the same host, but this is not guaranteed to be the case for all possible hosts.
If you want to assume that these four are all to the same host no matter what the domain is, then you just have to normalize the URL before you put it in your database and then before assigning a new shortened ID, you search the database for the normalized version of the URL.
In this case, you would remove the www. and remove any trailing slash to create the normalized version of the URL.
function normalizeUrl(url) {
// remove "www." if at first part of hostname
// remove trailing slash
return url.replace(/\/\/www\./, "//").replace(/\/$/, "");
}
Once you've normalized the URL, you search for the normalized URL in your database. If you find it, you use the existing shortener for it. If you don't find it, you add the normalized version to your database with a newly generated shortId.
Here's a demo:
function normalizeUrl(url) {
// remove "www." if at first part of hostname
// remove trailing slash
return url.replace(/\/\/www\./i, "//").replace(/\/$/, "");
}
const testUrls = [
"https://www.google.com",
"https://www.google.com/",
"https://google.com",
"https://google.com/",
];
for (const url of testUrls) {
console.log(normalizeUrl(url));
}
FYI, since hostnames in DNS are not case sensitive, you may also want to force the hostname to lower case to normalize it. Path names or query parameters could be case sensitive (sometimes they are and sometime they are not).
To include the host case sensitivity normalization, you could use this:
function normalizeUrl(url) {
// remove "www." if at first part of hostname
// remove trailing slash
// lowercase host name
return newUrl = url.replace(/\/\/www\./i, "//").replace(/\/$/, "").replace(/\/\/([^/]+)/, function(match, p1) {
// console.log(match, p1);
return "//" + p1.toLowerCase();
});
}
const testUrls = [
"https://www.google.com",
"https://www.google.com/",
"https://google.com",
"https://google.com/",
"https://WWW.google.com",
"https://www.Google.com/",
"https://GOOGLE.com",
"https://google.COM/",
"https://www.Google.com/xxx", // this should be unique
"https://google.COM/XXX", // this should be unique
];
for (const url of testUrls) {
console.log(normalizeUrl(url));
}

How to override template file item-list.html.twig for field_slider_images in Drupal 8?

I want to override the item listing template file core/themes/classy/templates/dataset/item-list.html.twig for listing the fields field_slider_images as well as field_blog_tags respectively of their's multiple values of the field.
I have selected "Unordered List" in the view.
Please do check the attached image.
I have created following files :
item-list--field-blog-tags.html.twig
item-list--field-slider-images.html.twig
But, this is not rendered for the listing of the fields.
When I have created item-list.html.twig then only it will access.
However, both fields have different data to style and I am not able to get the current field name which is loading it's data in item-list.html.twig.
Had a brief look at this and it doesn't seem that 'item-list' to have suggestions, which is quite unfortunate.
In this situation there are two options:
Create your own suggestion which would accomplish exactly what you need.
You'll have to do something like this:
/
/*add new variable to theme with suggestion name*/
function hook_theme_registry_alter(&$theme_registry) {
$theme_registry['item_list']['variables']['suggestion'] = '';
}
//send a value to newly added variable to use it build the suggestion
function hook_ENTITY_TYPE_view(array &$build, $entity, $display, $view_mode) {
//add condition here if field exists or whatever, do the same for other field
$build['field_slider_images']['#suggestion'] = 'field_slider_images';
}
//use newly added variable to build suggestion
function hook_theme_suggestions_THEME_HOOK(array $variables) {//THEME_HOOK=item_list
$suggestions = array();
if(isset($variables['suggestion'])){
$suggestions[] = 'item_list__' . $variables['suggestion'];
}
return $suggestions;
}
Now you should be able to use item-list--field-slider-images.html.twig
Second option is to do what others in core did: use a new theme
function hook_ENTITY_TYPE_view(array &$build, $entity, $display, $view_mode) {
//add condition here if field exists or whatever, do the same for other field
$build['field_slider_images']['#theme'] = array(
'item_list',
'item_list__field_slider_images',
);
}

in a civicrm webform, create multiple 'groups' fields

In CiviCRM webform, you can 'enable tag and groups'. Configuring those allows you to create option elements in the webform.
This creates one 'widget', one dropdown or set of checkboxes. I have two field instances where I want the user to select a group - say for example
which mailing lists do you want to receive (a,b,c)
what food are you interested in (d,e,f)
a,b,c,d,e and f are all groups. I can not change that.
How could I do that ?
A technical suggestion below, but first, I'd suggest that your real solution is to not use groups for the second question. Groups are set up nicely to handle mailing lists, but if it's to track interests, you'd be better off setting those up as custom fields. It'll solve this immediate issue, and it'll make it easier to deal with tandem searches and so forth (on list b and likes food d).
Now if you must have them as groups, you can create a fake field and move checkboxes into it using jQuery. Create the fake field with one option that you don't care about, but label it "What food are you interested in", or equivalent. Then, edit the Groups field that CiviCRM generated: label it more specifically as "which mailing lists...", and choose Static Options so it doesn't start offering up just any group for someone to choose.
Now, add the following javascript:
// first remove the dummy checkboxes in your fake field
$('#yourdummyfield .form-item').each( function() { $(this).remove(); });
// now move things into the dummy field
$('#yourdummyfield').append( $('#groupsfield .form-item-d');
$('#yourdummyfield').append( $('#groupsfield .form-item-e');
$('#yourdummyfield').append( $('#groupsfield .form-item-f');
From the form processing perspective, they'll all be evaluated as the "groups" field. However, they'll look separate. For better or worse, this will have to be adjusted as you add new groups fields.
After using Andrew Hunts suggestion for a while, I finally solved this on the server side, in a custom module, using webform logic as described here
http://www.webomelette.com/drupal-webform-submission-presave-hook
Basicly, on presave, I look for 2 custom fields containing group ids (mailing and food in the example). Then I add these to the CiviCRM groups field.
I'll add the code below, which has some more logic:
to make it flexible, I use one hidden field to contain the fieldkey
of the civicrm groups selector to add the other fields in. that
field is called 'the_groups_element' (but its not the groups element, it contains the key of the groups element)
there is only one foods group allowed, so before it adds you to a food group, it removes all other food groups from the groups selector.
You could probably make it even more generic, but since I had different logic for the different groups, this was suitable for me.
function getFoodGroups() {
// return foodgroups
}
function getMailGroups() {
// return mailgroups
}
function MYMODULE_webform_submission_presave($node, &$submission) {
$groupselm = '';
$groups_cid = false;
$foods_cid = false;
$mailings_cid = false;
// http://www.webomelette.com/drupal-webform-submission-presave-hook
foreach($node->webform['components'] as $cid=>$comp) {
if ($comp['form_key']=='the_groups_element') {
$groupselm = $comp['value'];
break;
}
}
if ($groupselm) {
foreach($node->webform['components'] as $cid=>$comp) {
if ($comp['form_key']==$groupselm) $groups_cid = $comp['cid'];
if ($comp['form_key']=='the_foods') $foods_cid = $comp['cid'];
if ($comp['form_key']=='the_mailings') $mailings_cid = $comp['cid'];
}
$group_gids = $submission->data[$groups_cid];
if (!$group_gids) $group_gids=array();
if ($foods_cid!==false && $submission->data[$foods_cid]) {
// remove all current foods
foreach ($group_gids as $gidx=>$group_gid) {
foreach (getFoodGroups() as $foodgroup) {
if ($group_gid==$foodgroup['gid']) {
if ($debug) drupal_set_message('removing foodgroup '.$foodgroup['gid']);
unset($group_gids[$gidx]);
}
}
}
// validate and add submitted regions
$foodsgids = $submission->data[$foods_cid];
if (!is_array($foodsgids)) $foodsgids = array($foodsgids);
foreach ($foodsgids as $foodsgid) {
foreach (getFoodGroups() as $foodgroup) {
if ($foodsgid==$foodgroup['gid']) {
$group_gids[]=$foodsgid;
break; // only one food allowed
}
}
}
}
if ($mailings_cid!==false && $submission->data[$mailings_cid]) {
// just add submitted mailings, dont remove any
$mailinggids = $submission->data[$mailings_cid];
if (!is_array($mailinggids)) $mailinggids = array($mailinggids);
foreach ($mailinggids as $mailinggid) {
foreach (getMailGroups() as $mailing) {
if ($mailinggid==$mailing['gid']) {
if ($debug) drupal_set_message('adding mailing '.$mailing['gid']);
$group_gids[]=$mailinggid;
}
}
}
}
$submission->data[$groups_cid] = array_unique($group_gids);
}

How to secure the segment passed to the controller in CI

I am trying to pass a segment to a controller. The url is like base_url/controller/function/seg1. I want to ensure that if the user try to enter the segment in the address bar, the controller would make sure there are not other words to be proceeded except the segment I want to pass.
For example, If the user tries to type base_url/main/function/(change this to other words) in address bar, the controller will filter the segment. I am not sure how to do it and would appreciate if someone can help me out.
Okay, so the best way to "secure" against such things would be to simply create a session at the time the user logs into your site with two values stored in that session;
1) Their database primary key id, and
2) a session item called 'logged_in'
At the time that your user would log into your site, you would store those two values like this;
$this->session->set_userdata('logged_in', true);
$this->session->set_userdata('user_id', $id);
Where $id is pulled from their user record during authentication.
Now that you have those in there, the next part would be that, in your controller, you would put an if statement in that checks if the user is logged in, as such;
function show($id) {
if($this->session->userdata('logged_in')) {
$posts = $this->Model_posts->get_user_posts($id);
}
}
Now, in your model, you would create a function for pulling the record that you want the user to be able to view based on their user_id. We'll say user posts for example.
function get_user_posts($user_id, $post_id) {
$sql = "SELECT * FROM posts WHERE user_id = ? AND id = ?";
$binds = array($user_id, $post_id);
$qry = $this->db->query($sql, $binds);
$result = array();
while($row = $qry->result_array()) {
array_push($result, $row);
}
return $result;
}
Now, when a logged in user or visitor tries to access records that don't belong to them, they will not retrieve any records because the select statement limits what's returned only to that user.
The structure you have there is
base_url/controller/action
So, your controller is already "filtering" it out because if you don't have a method/function in the controller (methods = actions) then your controller will trigger a 404 Page Not Found error. Of coarse, you could then handle your errors however you see fit, but from what you presented, the item you wish to filter is known as a controller action.
So for instance;
http://www.base_url.com/users/add
denotes that you wish to call the add (function) in the users controller.
If you want to pass the add action an argument, then you would do this as;
http://www.base_url.com/users/show/1
Where show would be a controller action and 1 would be the id of the user you wish to show.
I know it seems like I'm giving a basic intro to MVC methodologies, but like I said, the structure you showed plays out like I described.
Hope this helps.

How to have greasemonkey Check if text if found on page

I did do some research on google and the userscripts site but was unsuccessful in finding an answer.
So basically how can I check if specific text is found on a page?
And the text is in no special tags or anything.
A crude but fast way, for FF GM:
if (/Text you are looking for/i.test (document.body.innerHTML) )
{
alert ("Found it!");
}
//--- Looking for one of two different texts...
if (/(Text ONE that you are looking for)|(Text TWO that you are looking for)/i.test (document.body.innerHTML) )
{
alert ("Found one!");
}
For more focused searches use jQuery contains as in this previous question.
for example this script will show if the text specific text is found on this page.
// ==UserScript==
// #name so5059986
// #namespace test
// #description test
// #include http://stackoverflow.com/questions/5059986/how-to-have-greasemonkey-check-if-text-if-found-on-page
// ==/UserScript==
var xpathResult = document.evaluate("(//text()[contains(., 'specific text')])[1]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
var node=xpathResult.singleNodeValue;
if (node==null)
alert("text not found");
else
alert("text found on page");
I don't know what you mean with special tags. Text is always inside some tags.

Resources