Im trying to redirect from a list of entries to the edit page for these entries when an link.action is clicked.
I can't seem to get the values from the objects using the 'property' tag once I redirect to the editing page.
The action 'edit' is not getting executed on the page I redirect to. Instead it fires the standard action which is just listing all the entries.
public function toEditAction(Personenliste $person) {
$this->redirect('edit', 'Listen', 'testprivateext', ['personenliste' => $person], 43);
}
public function editAction(Personenliste $person) {
$this->view->assign('personenliste',$person);
return $this->htmlResponse();
}
The call is done via link.action. (I also tried directly redirecting the action with the 'pageUid'-tag)
<f:link.action action="toEdit" arguments="{person:'{person}'}" extensionName="testprivateext" controller="Listen" pluginName="pi1">🖉</f:link.action>
This is not an answer to your question (because you already solved it), but a little info for more readable inline-fluid.
This:
arguments="{person:'{person}'}"
can be written:
arguments="{person: person}"
when you directly pass a variable, you can discard the quotes and curly braces.
but you will need it, when passing a VH:
arguments="{person:'{person -> f:format.raw()}'}"
Have a nice day :)
Related
There a few similar questions, but none of them have really gotten at what I'm asking.
I have a browser action popup. In the popup, I want to display settings if you're on a page where the content script has been injected (i.e., any page that matches the matches key within the content_scripts in the `manifest).
If I'm on a page that doesn't match the content_scripts matches pattern (and so wasn't injected), I just want to display a generic message "this plugin activates when you're on so-and-so sites".
What is the cleanest way to do it, without adding any unnecessary permissions?
It seems like one option is sending a message to a content script in the active tab, and seeing if I get a reply, but that seems really.. hacky. I should be able to know just based on a regex if I'm on one of the domains that matches my content script.
I'm looking for something that works in both manifest v2 and v3, btw.
TL;DR;
What's the simplest way to display a "you're on a page that matches your content_script" or "you're not on a page that matches your content_script" in a browser_action popup?
I build chrome extensions full time for an agency and have had projects where I needed to do exactly what you're asking.
The solution can be implemented w/o any permissions whatsoever. I built mine locally with an empty array for permissions. (for mv3)
for popup.html just create 2 divs and have them default to display none.
<div id="unsupported" style="display: none;">Ooops! This is not a supported site.</div>
<div id="supported" style="display: none;">Wohoo! This is a supported site!!!!!</div>
for your script.js, wait till the popup loads then query the active tab in the current window and get that tab's ID to send a message directly to it. If the tab is supported with a content script, it will send a true response (see last code snippet). If it wasn't supported, it will be an 'undefined' response.
async function setUI() {
let tabData = await chrome.tabs.query({ active: true, currentWindow: true })
let tabId = tabData[0].id // tabs.query returns an array, but we filtered to active tab within current window which yields only 1 object in the array
chrome.tabs.sendMessage(tabId, {
'message': 'isSupported'
}, (response) => {
console.log(response)
// response will be true if the message was successfuly sent to the tab and "undefined" if the message was never received (i.e. not supported w/ your content script)
if (response) return showSupportedHTML()
// else
showUnsupportedHTML()
})
}
function showSupportedHTML() {
document.querySelector('#supported').style['display'] = ''
}
function showUnsupportedHTML() {
document.querySelector('#unsupported').style['display'] = ''
}
window.addEventListener('DOMContentLoaded', () => {
setUI()
})
Lastly, in your content script, add a message listener to receive the message 'isSupported' that comes in from your content script. If the content script receives that message, have it send a response back with 'true'.
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.message == 'isSupported') {
console.log('run')
sendResponse(true)
}
})
Now, this of course only works for manifest v3 because as far as I know you can't use chrome.tabs.query for mv2. However, I recommend this solution as I've implemented pretty much this exact same code in other projects for clients and it's never had any issues.
I could look into a solution for mv2, though using the "activeTab" permission would be the right way to do it, I believe. Now, if you really don't want to go that route then you could implement a rather hacky solution. For example, you could use window 'focus' and window 'blur' events to see when a user has entered or left a tab. Then set a local storage variable every time a user enters / leaves a supported page. The order of operations for blur and focus is always blur => focus. So, when the blur event occurs you set a local storage variable to false. However, if you leave a supported tab for another supported tab then the 'focus' event will trigger immediately afterwards so you can set that same storage variable back to true.
Now, your content script will load after the tab has been focused so you'll need to add a function for when the page loads. You can run something like document.hidden and if that returns true, do nothing because the user already left this tab. If it returns false, then the user is still on the tab and you can set your local storage variable to true.
When the user opens the popup, you'll check that local storage variable and if its true or false, you can set the UI accordingly.
Let me know if the mv2 solution made sense or sounds too hacky. Happy to look into it more! :)
edit: Here is the code for mv2, I tested it and it does work and without any permissions, other than storage which is not an invasive permission.
Script.js for the mv2 popup:
async function setUI() {
chrome.storage.local.get(['isSupported'], function (response) {
console.log(response['isSupported'])
// response will be true if the message was successfuly sent to the tab and "undefined" if the message was never received (i.e. not supported w/ your content script)
if (response['isSupported']) return showSupportedHTML()
// else
showUnsupportedHTML()
})
}
function showSupportedHTML() {
document.querySelector('#supported').style['display'] = ''
}
function showUnsupportedHTML() {
document.querySelector('#unsupported').style['display'] = ''
}
window.addEventListener('DOMContentLoaded', () => {
setUI()
})
code for the content script in mv2:
if (!document.hidden) chrome.storage.local.set({'isSupported': true})
window.addEventListener('blur', () => {
console.log('left site')
chrome.storage.local.set({'isSupported': false})
})
window.addEventListener('focus', () => {
console.log('entered site')
chrome.storage.local.set({'isSupported': true})
})
Let me know if you have any additional questions.
Disclaimer: I have no prior browser extension development experience and am just going by the docs. I might be spouting nonsense or giving an answer that is plainly against your requirements, but that would be out of ignorance and not malicious intent. If you find my answer problematic, comment, or cast a vote and move on.
According to MDN, the activeTab permission allows to read the active tab's Tab.url property. One solution could be to request that permission, and then use that API to get the active tab's URL, and then use the same regex from the manifest.json's matches property to test for a match, and then use that information to modify your extension's browser_action UI.
You should be able to read the matches property from the manifest file via the .runtime.getManifest() API. MDN docs, chrome docs.
Snippet to get active tab in a background script: tabs.query({active: true}). (link to MDN docs). A content script should instead use tabs.getCurrent and the Tab.active property of the resolved result.
If you don't want to request the activeTab permission, what you're suggesting with the message-passing between the browser_action scripts and the content scripts might be the right way to go, but I don't know for a fact. The tabs.onActivated event would probably be useful with this approach. Note that to send a message from a background script to a content script, you need to use tabs.sendMessage (MDN docs, chrome docs) instead of runtime.sendMessage.
Another possible (maybe?) approach would be to listen for the tab change in the content script and then send the notification message from the content script to the extension's background scripts via the onfocus event (or similar events), and runtime.sendMessage.
If you go with a messaging-related approach, you might want to put a condition in the content script to only do messaging if the content script is in the top frame of the tab (Ie. iframes don't do messaging), since only one frame of the tab really needs to do this kind of messaging when the active tab changes, and content scripts can be applied to all frames in a browsing context.
Of these possible solutions I can think of, I don't know which is best for you, since you want both minimal permission requirements and a simple/clean approach, and each seems to be a tradeoff.
I am trying to reset a form so that it appears to Drupal 8 that it hasn't been submitted. So far I have been unable to do this, as I cannot find any available methods (setSubmitted() hardcodes it to TRUE without a FALSE option). The reason is this isn't a full submit, but a submit of one field after which I would like the user to be redirected to another page that has another form, and I would like this secondary form to use the value obtained in the first step.
In the submit handler for the first part I use this to redirect:
$form_state->setRedirect('my.route', [], []);
And this works, but when the form reaches the second form (it seems) that the second form thinks it is a submission. As a result any submit buttons I add to the second form seem to make it auto-submit, and this breaks my user journey.
In the submit for the first part I have tried:
$form_state->setRebuild(TRUE);
$form_state = new FormState();
unset($form_state);
Tried the above in various configurations to no avail. They all prevent/ignore the setRedirect call that I make afterwards. The reason I want/need to do it this way is I want to preserve the POST method used.
Do you want to obtain something similar to what core search module does? It has simple SearchBlockForm that sends data to more complex SearchPageForm.
SearchBlockForm uses GET method (though you may use POST):
$form['#method'] = 'get';
and has no id and token fields:
function search_form_search_block_form_alter(&$form, FormStateInterface $form_state) {
$form['form_build_id']['#access'] = FALSE;
$form['form_token']['#access'] = FALSE;
$form['form_id']['#access'] = FALSE;
}
BTW, the last change allows you to avoid running submit callbacks.
Hope this helps.
I want to create an "extension" for a Jacada Interaction (to extend functionality), in my case to parse and assign the numerical part of serialNumber (a letter, followed by digits) to a numeric global ("system") variable, say serialNumeric. What I am lacking is the structure and syntax to make this work, including the way to reference interaction variables from within the extension.
Here is my failed attempt, with lines commented out to make it innocuous after failing; I think I removed "return page;" after crashing, whereupon it still crashed:
initExtensions("serialNumeric", function(app){
app.registerExtension("loaded", function(ctx, page) {
// Place your extension code here
//$('[data-refname="snum"]').val('serialNumber');
// snum = Number(substring(serialNumber,1))
});
});
Here is an example of one that works:
/**
* Description: Add swiping gestures to navigate the next/previous pages
*/
initExtensions("swipe", function(app) {
// Swipe gestures (mobile only)
app.registerExtension('pageRenderer', function(ctx, page) {
page.swipe(function(evt) {
(evt.swipestart.coords[0] - evt.swipestop.coords[0] > 0)
? app.nextButton.trigger('click')
: app.backButton.trigger('click')
});
return page;
});
});
After reading the comment below, I tried the following, unsuccessfully (the modified question variable is not written back to that variable). It rendered poorly in the comment section, so I am putting it here:
initExtensions("serialNumeric", function(app){
app.registerExtension("loaded", function(ctx, page) {
var sernum = new String($('[data-refname="enter"] input'));
var snumeric = new String(sernum.substr(1));
$('[data-refname="enter"] input').val(snumeric);
});
});
I would like to understand when this code will run: it seems logical that it would run when the variable is assigned. Thanks for any insight ~
In your case, you extend loaded event. You don't have to return the page from the extension like in your working example below.
The page argument contains the DOM of the page you have just loaded, the ctx argument contains the data of the page in JSON form. You can inspect the content of both arguments in the browser's inspection tools. I like Chrome. Press F12 on Windows or Shift+Ctrl+I on Mac.
The selector $('[data-refname="snum"] input') will get you the input field from the question with the name snum that you defined in the designer. You can then place the value in the input field with the value from the serialNumber variable.
$('[data-refname="snum"] input').val(serialNumber);
You can also read values in the same way.
You can't (at this point) access interaction variables in the extension, unless you place theses variables inside question fields.
Here is a simple example how to put your own value programmatically into a input field and cause it to read it into the model, so upon next it will be sent to the server. You are welcome to try more sophisticated selectors to accommodate for your own form.
initExtensions("sample", function(app){
app.registerExtension("loaded", function(ctx, page) {
// simple selector
var i = $('input');
// set new value
i.val('some new value');
// cause trigger so we can read into our model
i.trigger('change');
});
});
I've got a view (phase4) with some custom content type content in it, where the users may comment on.
When the users want to comment, the comment form should appear in a modal form. I solved this by using the admin overlay.
Adding following function to my custom module:
function phase2_admin_paths_alter(&$paths) {
$paths['comment/reply/*'] = TRUE;
}
and using following link:
Comment
to open the comment form in a modal way. So far so good... but....
How do I redirect the user back to the page, the user was coming from.
I know that I have to overwrite the #action of the form in the template_form_FORMID_alter, like
$form['#action'] = $lasturl;
but how do I get the last url, so that it is reusable (so hardcoding the url isn't an option)?
My first idea was that I transfer the last url by adding it to the url as a $_GET-parameter, but it looks like this:
www.example.com/phase4#overlay=comment/reply/161%3Furl%3Dphase4
I also tried it with drupal_get_destination(), but either with no success, because of the tranformation of the "?" and the "=" in the url.
Are there other ways to find out where the user was coming from?
Note: phase4 isn't the alias of node 161. Phase 4 is a view, where node 161 is an element of.
Cheers
Tom
You have to use the drupal_get_destination() function with l() function to create such links.
$destination = drupal_get_destination(); // Store current path
Comment
I am using N2CMS to manage the content of my site without using the page routing from N2. Hence when I edit a piece of content, it's quite useless when N2 asks me: "Update links leading to..." "Add permanent redirect at previous URL?". Can I disable this behaviour?
Converting Page into Part is inherently bad idea. It may be temporary fix for the problem you have, but it will bounce back at you in a bad way.
Instead, you can do this
Turn LinkTracker off in web.config
linkTracker enabled="false" permanentRedirectEnabled="false"
Copy CommandFactory.cs from N2 Source into your solution, and rename it to MyCommandFactory.cs.
Add Service replacement attribute
[Service(typeof(ICommandFactory), Replaces = typeof(CommandFactory))]
In a constructor, change this line
updateReferences = new MyUpdateReferencesCommand();
Write your own empty Update reference command class
public class MyUpdateReferencesCommand : UpdateReferencesCommand
{
public override void Process(CommandContext state)
{
}
}
As far as I can see from the source code, N2 expects always to show you the "Update links leading to..." page if the ContentItem is a Page (i.e. [PageDefinition] attribute or .IsPage = true) and the address has been updated. The solution in our case was to make the 'page' in question into a 'part' using [PartDefinition].