feat: Add button to publish to Greasy Fork#2458
Conversation
add9633 to
6f7b191
Compare
|
This is great, but I have some issues. Instead of opening a new document I'd like to show the inputs in the current tab and use fetch() to submit a FormData object. I'd also like to avoid showMessage and instead show the hint as text alongside those inputs. It would also make sense to show the Publish button alongside the Save button if the script is connected. Could you address these? |
Due to security concerns, Greasy Fork refused to allow this and requires user interaction before an update can be pushed. Userstyles.world only refuses this on the first upload i.e. publish.
I can move the Publish button to the top bar as requested. Not sure how squished "text alongside those inputs" will be.
That was a temporary workaround. I'll investigate how to detect when the publish is successful in Greasy Fork, and how to make the updateURL reload automatically without closing the editor. |
|
I don't see where GreasyFork doesn't allow fetch(), but if it's indeed so, you can embed the form element in the current page and set its target=_blank or use an iframe.
The UI with the inputs will be shown as a part of the current editor page, it can be a panel between the top panel and the code or it can overlay the code. There'll be plenty of space, no need to squish.
Probably use a temporarily added webRequest.onCompleted listener for the form action's URL. |
|
...or form's |
Greasy Fork has anti-CSRF measures that prohibit POST requests coming from off-site. The "prefill" URL is exempted. |
|
Extensions can modify the "coming from" header, which I guess is |
|
But even without patching, it might be possible to use a visible iframe embedded in the editor page. |
How is ViolentMonkey going to push a repo stored on disk? I edit inside ViolentMonkey so that I can edit-then-refresh 6 times per minute.
The point is that userscript writers want to upload images and select their preferred privacy radio button in the form that opens.
I will attempt to remove the hint that's no longer necessary once I make the reload automatic.
The form we submit does not contain the updateURL. We need to wait for the userscript writer to submit another form on greasyfork.org to have an updateURL. I think I'll listen for the "Reinstall" button.
The iframe would be smaller than desired. I prefer a new tab or at least a popup window like in Stylus to have lots of screen space to write the userscript description.
I will do this. I'm a bit new to Vue.js |
|
Ah so if it's similar to Stylus integration with userstyles.world then it might make sense to specify the window as a popup: const {availWidth: w, availHeight: h} = screen;
window.open('', '_blank', `popup=1,left=${w / 4},top=${h / 4},width=${w / 2},height=${h / 2}`); |
The anti-CSRF check is not using the referer or origin headers. It is based on a value stored in session and a hidden input on the page. The anti-CSRF check is intentional and I would not appreciate efforts to circumvent it. The intended behaviour here is to prefill the form with the new code, and for the user to press a button on greasyfork.org to actually submit it. |
6f7b191 to
aa96741
Compare
aa96741 to
b32d166
Compare
|
@tophf Your idea of a "form element in the current page" simplified my code. I added automatic detection of the "Reinstall version" button after going through "Publish userscript", so showMessage and hints aren't needed anymore. I added screenshots at the top of the 4 cases I tested working. This PR is now ready for others to test. |
b32d166 to
3f4030f
Compare
3f4030f to
ad6c438
Compare
|
I really want it to open inside an iframe inside the editor tab and judging by the HTTP response headers greasyfork doesn't forbid opening itself inside an iframe. Assuming it's added to the Vue template and declared as const iframe = document.createElement('iframe');
const form = document.createElement('form');
const textarea = document.createElement('textarea');
form.hidden = true;
form.method = 'post';
textarea.name = 'script_version[code]';
textarea.value = $codeComp.getRealContent();
document.body.append(iframe);
iframe.contentDocument.body.append(form);
form.submit();
I think the label doesn't need to change to I also have a lot of trivial suggestions for the actual code but we can deal with that later or I can do it myself after we establish the fundamental UX. For example, in diff --git a/src/injected/content/index.js b/src/injected/content/index.js
@@ -7 +7 @@ import './tabs';
-import { sendCmd } from './util';
+import { getAttribute, querySelector, sendCmd } from './util';
@@ -44 +44,12 @@ async function init() {
- addHandlers({ GetScriptVer: true });
+ addHandlers({
+ GetScriptVer({ meta }) {
+ setPrototypeOf(meta, null);
+ const el = document::querySelector('a.install-link');
+ if (el && el::getAttribute('data-script-name') === meta.name
+ && el::getAttribute('data-script-namespace') === meta.namespace) {
+ meta.publishVersion = el::getAttribute('data-script-version');
+ meta.publishDownloadURL = el::getAttribute('href');
+ }
+ return sendCmd('GetScriptVer', { meta });
+ },
+ });
diff --git a/src/injected/content/inject.js b/src/injected/content/inject.js
@@ -2 +2,3 @@ import bridge, { addHandlers, grantless } from './bridge';
-import { elemByTag, makeElem, nextTask, onElement, sendCmd } from './util';
+import {
+ elemByTag, getAttribute, makeElem, nextTask, onElement, querySelector, sendCmd,
+} from './util';
@@ -19,2 +20,0 @@ let nonce;
-let getAttribute;
-let querySelector;
@@ -160,4 +159,0 @@ export async function injectScripts(data, info, isXml) {
- if (wasInjectableFF) {
- getAttribute = Element[PROTO].getAttribute;
- querySelector = document.querySelector;
- }
diff --git a/src/injected/content/util.js b/src/injected/content/util.js
@@ -10,0 +11,2 @@ export const elemByTag = (tag, i) => getOwnProp(document::getElementsByTagName(t
+export const getAttribute = Element[PROTO].getAttribute;
+export const querySelector = document.querySelector; |
ad6c438 to
e3569aa
Compare
Co-authored-by: tophf <tophff@gmail.com>
|
Yeah an iframe won't work because the site sets X-Frame-Options header to 'sameorigin', and I guess Jason won't be too keen on us stripping it. What about using const wnd = window.open() with the size parameter set to something like a half of screen size in each dimension, putting the form inside via wnd.document, and submitting it?
That's a big claim, but I don't think that having different text will meaningfully reduce the possibility of a mistake. The most reliable solution is to not put sensitive info into publishable scripts in the first place e.g. maybe use GM storage instead. We can also add a confirmation on the first publish attempt within this editing session reminding about sensitive info. |
If there's a change I can make that'll make your life easier without leaving the door wide open for bad actors, I'll do it. I'm not sure what origin I would need to allow for this to work for you. |
|
Yeah looks fine.
If we are still in the context of sensitive things that shouldn't be put into a greasyfork-connected script, such BTW, I wonder if authorization should be performed via browser.identity.launchWebAuthFlow the way we do it in Stylus... @JasonBarnabe, a separate window seems fine as it's usually how it's done anyway. |



See #2425
Screenshots
Before publish: Publish userscript button appears
After successful publish: Push update button appears
3rd-party read-only: No button added
3rd-party modified: No button added
Anything that didn't go through my workflow is considered 3rd-party.
If you want to have it recognize a userscript as 1st-party:
@updateURLand@downloadURLThis way, upon reopening, the "Push update" button should appear