diff --git a/controllers/scriptStorage.js b/controllers/scriptStorage.js index 6ba9aff9e..7eb7ba8b2 100644 --- a/controllers/scriptStorage.js +++ b/controllers/scriptStorage.js @@ -1529,7 +1529,7 @@ exports.storeScript = function (aUser, aMeta, aBuf, aUpdate, aCallback) { icon = findMeta(aMeta, 'UserScript.icon.0.value'); if (icon) { - if (!isFQUrl(icon, false, true)) { + if (!isFQUrl(icon, { canDataImg: true })) { // Not a web url... reject aInnerCallback(new statusError({ @@ -1636,7 +1636,7 @@ exports.storeScript = function (aUser, aMeta, aBuf, aUpdate, aCallback) { supportURL = findMeta(aMeta, 'UserScript.supportURL.0.value'); if (supportURL) { - if (!isFQUrl(supportURL, true)) { + if (!isFQUrl(supportURL, { canMailto: true })) { // Not a web url... reject aInnerCallback(new statusError({ @@ -1649,6 +1649,27 @@ exports.storeScript = function (aUser, aMeta, aBuf, aUpdate, aCallback) { aInnerCallback(null); }, + function (aInnerCallback) { + // `@contributionURL` validation + var contributionURL = null; + + contributionURL = findMeta(aMeta, 'UserScript.contributionURL.0.value'); + if (contributionURL) { + if (!isFQUrl(contributionURL, { isSecure: true })) { + + // Not a secure web url... reject + aInnerCallback(new statusError({ + message: '`contributionURL` not a secure web url in the UserScript metadata block.', + code: 400 + }), null); + return; + } + + // TODO: Pre-filter on abuse + } + + aInnerCallback(null); + }, function (aInnerCallback) { // `@homepageURL` validations var homepageURLS = null; diff --git a/libs/helpers.js b/libs/helpers.js index 30a365a43..505bc94b7 100644 --- a/libs/helpers.js +++ b/libs/helpers.js @@ -148,9 +148,11 @@ exports.updateUrlQueryString = function (aBaseUrl, aDict) { return url; }; -exports.isFQUrl = function (aString, aMailto, aDataImg) { +exports.isFQUrl = function (aString, aOptions) { var URL = url.parse(aString); // TODO: Convert to non-legacy + var reTrusty = null; + var protocol = URL.protocol; var username = URL.username; // NOTE: BUG: in current *node* var password = URL.password; // NOTE: BUG: in current *node* @@ -163,7 +165,13 @@ exports.isFQUrl = function (aString, aMailto, aDataImg) { var source = encodeURIComponent(aString); var target = null; - if (protocol && /^https?:$/.test(protocol)) { + if (!aOptions) { + aOptions = {}; + } + + reTrusty = aOptions.isSecure ? new RegExp('^https:$') : new RegExp('^https?:$'); + + if (protocol && reTrusty.test(protocol)) { if (hostname) { target = encodeURIComponent(protocol) + encodeURIComponent('//') @@ -180,9 +188,9 @@ exports.isFQUrl = function (aString, aMailto, aDataImg) { return target === source; } - } else if (aMailto && /^mailto:\S+@\S+/.test(aString)) { + } else if (aOptions.canMailto && /^mailto:\S+@\S+/.test(aString)) { return true; - } else if (aDataImg && /^data:image\//.test(aString)) { + } else if (aOptions.canDataImg && /^data:image\//.test(aString)) { return true; } diff --git a/libs/modelParser.js b/libs/modelParser.js index 69cdd9ee0..e64238350 100644 --- a/libs/modelParser.js +++ b/libs/modelParser.js @@ -219,6 +219,7 @@ var parseScript = function (aScript) { var description = null; var icon = null; var supportURL = null; + var contributionURL = null; var downloadURL = null; var downloadUtf = null; @@ -283,7 +284,7 @@ var parseScript = function (aScript) { // Support Url supportURL = findMeta(script.meta, 'UserScript.supportURL.0.value'); if (supportURL) { - if (isFQUrl(supportURL, true)) { + if (isFQUrl(supportURL, { canMailto: true })) { script.hasSupport = true; @@ -296,6 +297,19 @@ var parseScript = function (aScript) { } } + // Contribution Url + contributionURL = findMeta(script.meta, 'UserScript.contributionURL.0.value'); + if (contributionURL) { + if (isFQUrl(contributionURL, { isSecure: true })) { + script.hasContribution = true; + script.contribution = [{ + url: contributionURL, + text: decode(contributionURL) + }]; + + } + } + // OpenUserJS metadata block checks if (findMeta(script.meta, 'OpenUserJS.unstableMinify.0.value')) { script.hasUnstableMinify = true; diff --git a/public/css/common.css b/public/css/common.css index 1b05693cc..0ba420904 100644 --- a/public/css/common.css +++ b/public/css/common.css @@ -495,6 +495,17 @@ ul.flaggedList { overflow-x: hidden; } +.btn-donate { + color: #000; + background-color: #ffc439; + border-color: #f1b72a; + margin-top: 0.55em; +} + +.btn-donate:hover { + background-color: #eab122; +} + /* Extend Ace */ .ace_gutter-cell:hover { font-weight: bold; @@ -503,3 +514,4 @@ ul.flaggedList { #editor .ace_gutter-cell:hover { cursor: pointer; } + diff --git a/public/pegjs/blockUserScript.pegjs b/public/pegjs/blockUserScript.pegjs index e3365c0a4..6add3276b 100644 --- a/public/pegjs/blockUserScript.pegjs +++ b/public/pegjs/blockUserScript.pegjs @@ -44,6 +44,7 @@ Test the generated parser with some input for peg.js site at https://pegjs.org/o // @match http://example.net/* // @exclude http://example.com/foo // @exclude http://example.org/foo +// @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=your.email@example.org&item_name=OpenUserJS+Author+Donation // ==/UserScript== */ @@ -128,7 +129,8 @@ item1 = 'icon64' / 'icon' / 'downloadURL' / - 'defaulticon' + 'defaulticon' / + 'contributionURL' ) whitespace value: non_newline diff --git a/views/includes/documents/Terms-of-Service.md b/views/includes/documents/Terms-of-Service.md index 518bff8b1..5dadbbcb6 100644 --- a/views/includes/documents/Terms-of-Service.md +++ b/views/includes/documents/Terms-of-Service.md @@ -5,7 +5,7 @@ * Staying on topic is a good thing to remember. Everyone has other duties to attend to at different times. Try to be succinct and don't obsess on a particular topic. Give others a chance to respond. * Intense conversations regarding "filling voids" should be avoided. Advocacy of one site or another should be minimal and gracefully done. * Passive aggressive behavior with abrasive commenting does not encourage teamwork even when it is coupled with genuine constructive feedback. Personal attacks may be eligible for account removal from OpenUserJS.org. -* **"Open Source" is not the same as "the developers must do my bidding"**. A lot of generous time is donated by multiple developers in attempting to resolve any properly presented issues. However this does not mean there is an obligation to do it on any single persons time table. Until the OpenUserJS.org bills are paid by everyone involved it's a wise choice to be courteous instead of snarky. +* **"Open Source" is not the same as "the developers must do my bidding"**. A lot of generous time is donated by multiple developers in attempting to resolve any properly presented issues. However this does not mean there is an obligation to do it on any single persons time table. Until the OpenUserJS.org bills are paid by everyone it is a wise choice to be courteous. Please **do not** be snarky. ### Abstraction diff --git a/views/includes/footer.html b/views/includes/footer.html index d4f6782ce..d8e7d0c47 100644 --- a/views/includes/footer.html +++ b/views/includes/footer.html @@ -3,7 +3,12 @@
+
@@ -215,6 +216,20 @@
+
+
+
+ + @contributionURL url +
+
+
+
+

A url of https protocol only. Used for monetary requests for contributions. @contributionAmount is currently unsupported. Specially formatted on the script issues list and script issue page.

+

Last value shown.

+
+
+
{{/newUserJS}}
diff --git a/views/pages/scriptIssueListPage.html b/views/pages/scriptIssueListPage.html index aeafbba7c..d1754c16c 100644 --- a/views/pages/scriptIssueListPage.html +++ b/views/pages/scriptIssueListPage.html @@ -25,9 +25,8 @@ {{#script.hasSupport}} {{#script.support}} {{^isSameOrigin}} - + {{#script.hasContribution}} + {{#script.contribution}} + + {{/script.contribution}} + {{/script.hasContribution}}
diff --git a/views/pages/scriptIssuePage.html b/views/pages/scriptIssuePage.html index e6d497f2a..34a8bee83 100644 --- a/views/pages/scriptIssuePage.html +++ b/views/pages/scriptIssuePage.html @@ -55,6 +55,19 @@
{{> includes/searchBarPanel.html }} + {{#script.hasContribution}} + {{#script.contribution}} +
+
+ +
+ {{/script.contribution}} + {{/script.hasContribution}} +