Add extra copyright, links in the copyright owner and year range

This commit is contained in:
Richie Bendall 2019-05-31 23:44:35 +12:00
parent 549dbb9627
commit cd4dcaaf34
No known key found for this signature in database
GPG key ID: 1C6A99DFA9D306FC
19 changed files with 169 additions and 93 deletions

View file

@ -10,4 +10,7 @@ before_install: npm i -g yarn
install: yarn install install: yarn install
script: yarn test script:
- yarn test
- yarn lint
- yarn build

View file

@ -40,7 +40,6 @@ Available fields:
- email - email
- format - format
- gravatar - gravatar
- version
- theme - theme
### copyright ### copyright
@ -59,13 +58,29 @@ You can also use an array to hold multiple copyright holders:
```json ```json
{ {
"copyright": ["Remy Sharp (http://remysharp.com)", "Richie Bendall (https://richienb.github.io)"] "copyright": ["Remy Sharp", "Richie Bendall"]
} }
``` ```
Which will be formatted as: Which will be formatted as:
Remy Sharp (http://remysharp.com) and Richie Bendall (https://richienb.github.io) Remy Sharp and Richie Bendall
If you additionally want to include a URL and email with each copyright holder, use objects in the array:
```json
{
"copyright": [{
"name": "Remy Sharp, https://remysharp.com",
"url": "https://remysharp.com",
"email": "remy@remysharp.com"
}, {
"name": "Richie Bendall, https://www.richie-bendall.ml",
"url": "https://www.richie-bendall.ml",
"email": "richiebendall@gmail.com"
}]
}
```
### url ### url
@ -117,28 +132,6 @@ And if you want to show your gravatar, just add the `gravatar` boolean property:
Note that the gravatar requires the email property. You also need to check the compatibility of the chosen theme. Currently, only the default theme supports Gravatar. Note that the gravatar requires the email property. You also need to check the compatibility of the chosen theme. Currently, only the default theme supports Gravatar.
### License version targeting
License version targeting allows you to link your MIT license to a specific revision in this project - therefore fixing it permanently to a specific license text.
Though I don't expect the license text to change ever, this is just some extra assurance for you.
Targeting requires the [sha from the license commit](https://github.com/remy/mit-license/commits/master/LICENSE.html). This can be specified on the URL (in your permalink) or in the JSON file.
For example: <http://rem.mit-license.org/a526bf7ad1> (make sure to view-source) shows an older version of the LICENSE.html file (compared to the [latest version](http://rem.mit-license.org) - the older version didn't have the new themes).
This can also be targeted in my JSON file:
```json
{
"copyright": "Remy Sharp, http://remysharp.com",
"url": "http://remysharp.com",
"version": "a526rbf7"
}
```
Note that if no version is supplied, the latest copy of the LICENSE.html will be displayed with your information included.
### Themes ### Themes
If you've got an eye for design (or like me: not): you can contribute a theme by adding a CSS file to the `themes` directory. You can use the latest CSS technologies since they are automatically polyfilled. The default theme is simple and clean, but you can add your own as you like. If you've got an eye for design (or like me: not): you can contribute a theme by adding a CSS file to the `themes` directory. You can use the latest CSS technologies since they are automatically polyfilled. The default theme is simple and clean, but you can add your own as you like.
@ -198,13 +191,6 @@ The following types of requests can be made to this project:
the json file (currently none specified on `rem`) the json file (currently none specified on `rem`)
- <http://rem.mit-license.org/license.html> HTML - <http://rem.mit-license.org/license.html> HTML
- <http://rem.mit-license.org/license.txt> Text - <http://rem.mit-license.org/license.txt> Text
- <http://rem.mit-license.org/a526bf7ad1> a526bf7ad1 version, HTML, or the
default format specified in the json file (again, none specified for
`rem` so defaults to HTML)
- <http://rem.mit-license.org/a526bf7ad1/license.html> a526bf7ad1 version,
HTML
- <http://rem.mit-license.org/a526bf7ad1/license.txt> a526bf7ad1 version,
text
The url also supports including a start year: The url also supports including a start year:
@ -212,7 +198,7 @@ The url also supports including a start year:
show a license year range of 2009-2016 (2016 being the current year) show a license year range of 2009-2016 (2016 being the current year)
- [http://rem.mit-license.org/2009-2010](http://rem.mit-license.org/2009-2010/) - [http://rem.mit-license.org/2009-2010](http://rem.mit-license.org/2009-2010/)
allows me to force the year range allows me to force the year range
- <http://rem.mit-license.org/a526bf7ad1/2009-2010/license.txt> a526bf7ad1 version, with year range of 2009-2010 in plain text - <http://rem.mit-license.org/2009-2010/license.txt> year range of 2009-2010 in plain text
Finally, the url also supports pinning the year Finally, the url also supports pinning the year

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>BSD Zero Clause License (0BSD)</h1> <h1>BSD Zero Clause License (0BSD)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>Permission to use, copy, modify, and/or distribute this software for any <p>Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.</p> purpose with or without fee is hereby granted.</p>

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>Academic Free License v3.0 (AFL-3.0)</h1> <h1>Academic Free License v3.0 (AFL-3.0)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>This Academic Free License (the "License") applies to any original work of <p>This Academic Free License (the "License") applies to any original work of
authorship (the "Original Work") whose owner (the "Licensor") has placed the authorship (the "Original Work") whose owner (the "Licensor") has placed the

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>GNU Affero General Public License (AGPL-3.0)</h1> <h1>GNU Affero General Public License (AGPL-3.0)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.</p> <p> Preamble</p> <p>Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.</p> <p> Preamble</p>
@ -634,7 +634,7 @@
the "copyright" line and a pointer to where the full notice is found.</p> the "copyright" line and a pointer to where the full notice is found.</p>
<p> &lt;one line to give the program's name and a brief idea of what it does.&gt; <p> &lt;one line to give the program's name and a brief idea of what it does.&gt;
Copyright © <%= info %></p> Copyright © <%- info %></p>
<p> This program is free software: you can redistribute it and/or modify <p> This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published it under the terms of the GNU Affero General Public License as published

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>The ISC License (ISC)</h1> <h1>The ISC License (ISC)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>Permission to use, copy, modify, and/or distribute this software for any <p>Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>The MIT License (MIT)</h1> <h1>The MIT License (MIT)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>Permission is hereby granted, free of charge, to any person obtaining a copy <p>Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal of this software and associated documentation files (the “Software”), to deal

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>NCSA Open Source License (NCSA)</h1> <h1>NCSA Open Source License (NCSA)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>Permission is hereby granted, free of charge, to any person <p>Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>The SIL Open Font License (OFL)</h1> <h1>The SIL Open Font License (OFL)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>This Font Software is licensed under the SIL Open Font License, Version 1.1. <p>This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: This license is copied below, and is also available with a FAQ at:

View file

@ -7,7 +7,7 @@
<article> <article>
<%- gravatar %> <%- gravatar %>
<h1>The Universal Permissive License (UPL)</h1> <h1>The Universal Permissive License (UPL)</h1>
<p>Copyright © <%= info %></p> <p>Copyright © <%- info %></p>
<p>Subject to the condition set forth below, permission is hereby granted to any <p>Subject to the condition set forth below, permission is hereby granted to any
person obtaining a copy of this software, associated documentation and/or data person obtaining a copy of this software, associated documentation and/or data

View file

@ -1,3 +1,3 @@
<footer> <footer>
<p><a href="https://github.com/remy/mit-license">Fork this project to create your own MIT license that you can always link to.</a></p> <p><a target="_blank" rel="noopener" href="https://github.com/remy/mit-license">Fork this project to create your own MIT license that you can always link to.</a></p>
</footer> </footer>

View file

@ -1,7 +1,10 @@
<head> <head>
<title>MIT License</title> <title>MIT License</title>
<meta charset="utf-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.7"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="description"
content="The MIT License is a permissive free software license originating at the Massachusetts Institute of Technology. As a permissive license, it puts only very limited restriction on reuse and has, therefore, an excellent license compatibility.">
<!-- <!--
Welcome fellow open source developer. This project is here for you to Welcome fellow open source developer. This project is here for you to
link to if you're like me and keep forgetting to include the link to if you're like me and keep forgetting to include the

View file

@ -2,21 +2,23 @@
"author": "Remy Sharp <remy@leftlogic.com> (http://remysharp.com/)", "author": "Remy Sharp <remy@leftlogic.com> (http://remysharp.com/)",
"name": "mit-licence", "name": "mit-licence",
"description": "Hosted MIT License with details controlled through this repo", "description": "Hosted MIT License with details controlled through this repo",
"private": true,
"version": "1.0.0", "version": "1.0.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git@github.com:remy/mit-license.git" "url": "git@github.com:remy/mit-license.git"
}, },
"scripts": { "scripts": {
"build": "tsc server.ts",
"start": "ts-node-dev --respawn --transpileOnly --no-notify server.ts", "start": "ts-node-dev --respawn --transpileOnly --no-notify server.ts",
"serve": "tsc server.ts && node server.js", "serve": "yarn build && node server.js",
"test": "node test.js", "test": "node test.js",
"lint": "eslint server.ts util.ts test.js --color --fix" "lint": "eslint server.ts util.ts test.js --color"
}, },
"bugs": { "bugs": {
"url": "https://github.com/remy/mit-license/issues" "url": "https://github.com/remy/mit-license/issues"
}, },
"license": "SEE LICENSE IN LICENSE", "license": "MIT",
"dependencies": { "dependencies": {
"@octokit/rest": "^16.27.3", "@octokit/rest": "^16.27.3",
"btoa": "^1.2.1", "btoa": "^1.2.1",
@ -25,6 +27,7 @@
"ejs": "^2.6.1", "ejs": "^2.6.1",
"express": "^4.17.1", "express": "^4.17.1",
"express-minify": "^1.0.0", "express-minify": "^1.0.0",
"git-pull": "^0.1.0",
"humanize-list": "^1.0.1", "humanize-list": "^1.0.1",
"md5": "^2.2.1", "md5": "^2.2.1",
"node-html-parser": "^1.1.15", "node-html-parser": "^1.1.15",
@ -34,8 +37,10 @@
"typescript": "^3.4.5" "typescript": "^3.4.5"
}, },
"devDependencies": { "devDependencies": {
"@types/btoa": "^1.2.3",
"@types/compression": "^0.0.36", "@types/compression": "^0.0.36",
"@types/css": "^0.0.31", "@types/css": "^0.0.31",
"@types/ejs": "^2.6.3",
"@types/express": "^4.16.1", "@types/express": "^4.16.1",
"@types/express-minify": "^0.1.34", "@types/express-minify": "^0.1.34",
"@types/md5": "^2.1.33", "@types/md5": "^2.1.33",

2
robots.txt Normal file
View file

@ -0,0 +1,2 @@
User-agent: *
Disallow:

144
server.ts
View file

@ -1,18 +1,33 @@
import express = require('express') /*
IMPORTANT: Set the `github_token` environment variable to a personal access token
with at least the `public_repo` scope for the API.
Server port: The `PORT` environment variable can also be set to control the port the server
should be hosted on.
*/
// Core
import * as path from 'path' import * as path from 'path'
import * as fs from 'fs' import * as fs from 'fs'
// Server
const PORT = process.env.PORT || 80 const PORT = process.env.PORT || 80
import express = require('express')
import compression = require('compression') import compression = require('compression')
import minify = require('express-minify')
import postcssMiddleware = require('postcss-middleware')
// License viewing
import * as ejs from 'ejs'
import {yearNow, stripTags, trimArray} from './util'
import * as HTML from 'node-html-parser'
import md5 = require('md5'); import md5 = require('md5');
import humanizeList from 'humanize-list' import humanizeList from 'humanize-list'
import minify = require('express-minify')
import ejs = require('ejs')
import { yearNow, stripTags, trimArray } from './util'
import HTML = require('node-html-parser')
import postcssMiddleware = require('postcss-middleware')
import btoa = require('btoa')
const github = require("@octokit/rest")({ // License creation
import btoa = require('btoa')
import gitpull = require('git-pull')
const github = require('@octokit/rest')({
// GitHub personal access token // GitHub personal access token
auth: process.env.github_token, auth: process.env.github_token,
@ -29,10 +44,11 @@ app.use(minify({
app.set('view engine', 'ejs') app.set('view engine', 'ejs')
// Setup static files // Setup static files
app.use('/robots.txt', express.static('robots.txt'))
app.use('/users', express.static('users')) app.use('/users', express.static('users'))
app.use('/themes', postcssMiddleware({ app.use('/themes', postcssMiddleware({
plugins: [require('postcss-preset-env')({ browsers: '>= 0%', stage: 0 })], plugins: [require('postcss-preset-env')({browsers: '>= 0%', stage: 0})],
src: req => path.join(__dirname, 'themes', req.path) src: (req) => path.join(__dirname, 'themes', req.path),
})) }))
app.use('/themes', express.static('themes')) app.use('/themes', express.static('themes'))
app.use('/favicon.ico', express.static(__dirname + '/favicon.ico')) app.use('/favicon.ico', express.static(__dirname + '/favicon.ico'))
@ -45,33 +61,36 @@ app.use((_req, res, next) => {
}) })
// Parse URL-encoded bodies (as sent by HTML forms) // Parse URL-encoded bodies (as sent by HTML forms)
// app.use(express.urlencoded({ extended: true })) app.use(express.urlencoded({extended: true}))
//
// Parse JSON bodies (as sent by API clients) // Parse JSON bodies (as sent by API clients)
app.use(express.json()) app.use(express.json())
// HTTP POST API // HTTP POST API
app.post('/', (req, res) => { app.post('/', (req, res) => {
// Get differnet parts of hostname (example: remy.mit-license.org -> ['remy', 'mit-license', 'org']) // Get differnet parts of hostname (example: remy.mit-license.org -> ['remy', 'mit-license', 'org'])
const params = req.hostname.split(".") const params = req.hostname.split('.')
console.log(req.body)
// If there isn't enough part of the hostname // If there isn't enough part of the hostname
if (params.length < 2) res.status(400).send("Please specify a subdomain in the URL.") if (params.length < 2) res.status(400).send('Please specify a subdomain in the URL.')
res.json(req.body)
return
github.repos.createFile({ github.repos.createFile({
owner: "remy", owner: 'remy',
repo: "mit-license", repo: 'mit-license',
path: `users/${params[0]}.json`, path: `users/${params[0]}.json`,
message: `Automated creation of user ${params[0]}.`, message: `Automated creation of user ${params[0]}.`,
content: btoa(), content: btoa(''),
committer: { committer: {
name: "MIT License Bot", name: 'MIT License Bot',
email: "remy@leftlogic.com" email: 'remy@leftlogic.com',
},
})
gitpull(__dirname, (err: any, _consoleOutput: any) => {
if (err) {
res.status(502).end()
} else {
res.status(201).end()
} }
}) })
}) })
@ -91,21 +110,66 @@ app.get('*', (req, res) => {
const user = JSON.parse(data || '{}') const user = JSON.parse(data || '{}')
// If error opening // If error opening
if (err) { if (err) {
if (err.code === 'ENOENT') { if (err.code !== 'ENOENT') {
// File not found // Error is not "File not found"
name = '<copyright holders>'
theme = 'default'
gravatar = ''
} else {
// Other error
res.status(500).end() res.status(500).end()
return return
} }
} else if (!user.locked && user.copyright) {
// No error and valid
name = (() => {
if (typeof user.copyright === 'string') {
// Supports regular format
let template: string
if (user.url) template = `<a href="${user.url}">${user.copyright}</a>`
else template = user.copyright
if (user.email) template += ` &lt;<a href="mailto:${user.email}">${user.email}</a>&gt;`
return template
} else if (user.copyright.every((val: any) => typeof val === 'string')) {
// Supports: ['Remy Sharp', 'Richie Bendall']
return humanizeList(user)
} else { } else {
// No error /*
name = typeof user.copyright === 'string' ? user.copyright : humanizeList(user.copyright) Supports:
theme = user.theme || 'default' [{
gravatar = user.gravatar ? `<img id="gravatar" alt="Profile image" src="https://www.gravatar.com/avatar/${md5(user.email.trim().toLowerCase())}" />` : '' "name": "Remy Sharp, https://remysharp.com",
"url": "https://remysharp.com",
"email": "remy@remysharp.com"
},{
"name": "Richie Bendall, https://www.richie-bendall.ml",
"url": "https://www.richie-bendall.ml",
"email": "richiebendall@gmail.com",
}]
*/
let template: string
return humanizeList(user.copyright.map((val) => {
if (val.url) template = `<a href="${val.url}">${val.name}</a>`
else template = val.copyright
if (val.email) template += ` &lt;<a href="mailto:${val.email}">${val.email}</a>&gt;`
return template
}))
}
})()
theme = user.theme
gravatar = (() => {
if (user.gravatar && user.email) {
// Supports regular format
return `<img id="gravatar" alt="Profile image" src="https://www.gravatar.com/avatar/${md5(user.email.trim().toLowerCase())}" />`
}
else if (typeof user.copyright[0] === 'object' && user.gravatar) {
// Supports mutli-user format
return `<img id="gravatar" alt="Profile image" src="https://www.gravatar.com/avatar/${md5(user.copyright[0].email.trim().toLowerCase())}" />`
}
else return ''
})()
} }
const year = (() => { const year = (() => {
@ -115,9 +179,15 @@ app.get('*', (req, res) => {
// rem.mit-license.org/2019 // rem.mit-license.org/2019
const fromYear = params.find((val) => !isNaN(parseInt(val.replace('-', '')))) const fromYear = params.find((val) => !isNaN(parseInt(val.replace('-', ''))))
// rem.mit-license.org/2018-2019
const rangeYear = params.find((val) => val.split('-').length === 2)
// If current year // If current year
if (customYear) return customYear.replace(/[@-]/g, '') if (customYear) return customYear.replace(/[@-]/g, '')
// If range year
if (rangeYear) return rangeYear
// If from year // If from year
if (fromYear) { if (fromYear) {
// If from year is same as current // If from year is same as current
@ -135,8 +205,8 @@ app.get('*', (req, res) => {
const format = params.find((val) => val === 'license.html') ? 'html' : params.find((val) => val === 'license.txt') ? 'txt' : user.format || 'html' const format = params.find((val) => val === 'license.html') ? 'html' : params.find((val) => val === 'license.txt') ? 'txt' : user.format || 'html'
const args = { const args = {
info: `${year} ${name}`, info: `${year} ${name || '&lt;copyright holders&gt;'}`,
theme, theme: theme || 'default',
gravatar, gravatar,
} }

5
users/example.json Normal file
View file

@ -0,0 +1,5 @@
{
"//1": "Security holding user",
"//2": "This user is being locked to allow for an example.",
"locked": true
}

View file

@ -1,8 +1,5 @@
{ {
"copyright": "Richie Bendall, https://www.richie-bendall.ml", "//1": "Security holding user",
"url": "https://www.richie-bendall.ml", "//2": "This user is being locked to prevent complication with localhost.",
"email": "richiebendall@gmail.com", "locked": true
"format": "html",
"gravatar": true,
"theme": "material-indigo"
} }

5
users/null.json Normal file
View file

@ -0,0 +1,5 @@
{
"//1": "Security holding user",
"//2": "This user is being locked to allow for no copyright information.",
"locked": true
}

BIN
yarn.lock

Binary file not shown.