Skip to content

Commit

Permalink
Merge pull request #64 from PatrickSachs/develop
Browse files Browse the repository at this point in the history
0.3.0
  • Loading branch information
Patrick Sachs authored Mar 24, 2019
2 parents 8389efa + bba00f3 commit e6a7b44
Show file tree
Hide file tree
Showing 63 changed files with 4,921 additions and 2,915 deletions.
36 changes: 36 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# The URI to your database. If you have installed MongoDB on the same machine and are
# using the default installation on of MongoDB you can leave this at its default.
# Otherwise see https://docs.mongodb.com/manual/reference/connection-string/ for details.
DB="mongodb://localhost/helios"
DB_OPTS="{"useNewUrlParser": true}"

# The ports to run Helios on. (Set HTTP to -1 to not not listen to HTTP at all -
# HSTS recommended in this case)
PORT_HTTP=80
PORT_HTTPS=443

# The IP Helios will bind to. Set to [::] for IPv6. You'll know when you need any
# other value.
BIND_IP="0.0.0.0"

# Host on these domains. The first domain will be your primary, canonical URL.
# Helios will only issue a certificate valid for these domains.
BIND_DOMAINS="localhost"

# Do you want HTTPS support? Valid values are:
# * none - No SSL
# * letsEncrypt - Free SSL certificates by Let's Encrypt. Make sure that your
# BIND_DOMAINS are correct and pointing to this server. (In this case also
# set AGREE_GREENLOCK_TOS to true)
# * certificate - Manually issued certificates (not implemented yet)
SSL="none"
AGREE_GREENLOCK_TOS=false

# Your webmaster mail. Required by Let's Encrypt and Web Push service.
MAIL="webmaster@localhost.local"

# TODO: Some sort of canonical port for NAT?

# Leave this at its default value unless you want to join in on developing Helios.
# Enables developer mode if set to development.
ENV=production
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
node_modules
# Ignore compiled Next App
.helios
# Ignore certs
# Ignore config files, certs, ...
config/**/
config/style.sass
config/client.js
config/server.js
.env.local
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
A minimalistic CMS for the modern web.

- **Mobile first** - Responsive by design
- **Modern technologies** - Progressive web app, web push, automatic HTTPs
- **Modern technologies** - Progressive web app, web push, automatic HTTPS, ...
- **Small size** for emerging markets - Webpage size is ~250kb
- Optimized for **privacy** - No user data is stored unless opted in

Live Demo: [https://patrick-sachs.de/](https://patrick-sachs.de/)
Live Demo: [https://patrick-sachs.dev/](https://patrick-sachs.dev/)

Wiki/Guide: [https://github.com/PatrickSachs/helios/wiki](https://github.com/PatrickSachs/helios/wiki)

Expand All @@ -21,7 +21,7 @@ This is done by only including the features that are **actually required**, all

## Live production websites using Helios

| [<img src="https://patrick-sachs.de/static/content/system/logo.png" width="100px;"/><br /><sub><b>patrick-sachs.de</b></sub>](https://patrick-sachs.de) | [<img src="https://sahnee.de/static/content/system/logo.png" width="100px;"/><br /><sub><b>sahnee.de</b></sub>](https://sahnee.de) |
| [<img src="https://patrick-sachs.dev/static/content/system/logo.png" width="100px;"/><br /><sub><b>patrick-sachs.dev</b></sub>](https://patrick-sachs.dev) | [<img src="https://sahnee.de/static/content/system/logo.png" width="100px;"/><br /><sub><b>sahnee.de</b></sub>](https://sahnee.de) |
| :---: | :---: |

## Feature List
Expand Down Expand Up @@ -51,10 +51,6 @@ $ npm run dev

### Compile/Deploy

**Important**: Make sure to adjust the config files(`/src/config`). These contain your private keys for passwords. If you leave them at their default values, it will be rather trivial to decrypt your senstive user data.

They are named `client.example.js` and `server.example.js`. Make sure to give to omit the `.example` from the file names of the configured files. There is also a `style.example.sass`. You'll want to rename that one too.

```
$ npm install
$ npm run build
Expand Down
20 changes: 15 additions & 5 deletions components/EditorCode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from "react";
import Head from "next/head";

export default class EditorCode extends React.Component {
constructor(p) {
Expand All @@ -15,12 +14,15 @@ export default class EditorCode extends React.Component {
}
}

UNSAFE_componentWillReceiveProps(next) {
if (next.mode !== this.props.mode) {
componentDidUpdate(old) {
if (old.mode !== this.props.mode) {
this.setMode(next.mode);
}
if (next.readOnly !== this.props.readOnly) {
this.setMode(next.readOnly);
if (old.readOnly !== this.props.readOnly) {
this.setMode(this.props.readOnly);
}
if (old.value !== this.props.value) {
this.setValue(this.props.value);
}
}

Expand Down Expand Up @@ -62,6 +64,14 @@ export default class EditorCode extends React.Component {
}
}

setValue(value) {
if (this.ace) {
const pos = this.ace.session.selection.toJSON();
this.ace.setValue(value);
this.ace.session.selection.fromJSON(pos);
}
}

setReadOnly(readOnly) {
if (this.ace) {
this.ace.setReadOnly(!!readOnly);
Expand Down
4 changes: 2 additions & 2 deletions components/EditorRichText.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export default withDynamic({
style.innerHTML = this.props.style;
document.head.appendChild(style);
}

this.jodit = new this.props.jodit.Jodit(this.dom.current, {
// Create the jodit editor
this.jodit = new this.props.jodit(this.dom.current, {
sourceEditorCDNUrlsJS: [
'/node_modules/ace-builds/src-min/ace.js'
],
Expand Down
25 changes: 21 additions & 4 deletions components/Error.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FormattedMessage } from "react-intl";
import Card from "./layout/Card";
import A from "./system/A";

export const FullError = ({ error }) => {
return (<Card title={<p><FormattedMessage id="error" /></p>}>
Expand All @@ -9,9 +10,21 @@ export const FullError = ({ error }) => {

export const SlimError = ({ error }) => {
error = error && error.response ? error.response.data : error;
const str = byString(error);
if (str) return str;
return (<div>todo: handle this error!<code>{JSON.stringify(error)}</code></div>);
let str = null;
if (error.name !== undefined) { str = byName(error); }
if (!str) { str = byString(error); }
if (str) { return str; }
return (<div><code>{JSON.stringify(error)}</code></div>);
}

const byName = ({ name }) => {
if (name === "NotLoggedInError") {
return (<div>
<FormattedMessage id="errorMessages.notLoggedIn" />
<br/>
<A href="/admin/account" class="button is-link"><FormattedMessage id="navigation.admin.signIn" /></A>
</div>);
}
}

const byString = string => {
Expand All @@ -24,7 +37,11 @@ const byString = string => {
}
if (string === "no-data") return (<FormattedMessage id="errorMessages.noData" />);
if (string === "authorization-failure") return (<FormattedMessage id="errorMessages.authorizationFailure" />);
if (string === "not-logged-in") return (<FormattedMessage id="errorMessages.notLoggedIn" />);
if (string === "not-logged-in") return (<div>
<FormattedMessage id="errorMessages.notLoggedIn" />
<br/>
<A href="/admin/account" class="button is-link"><FormattedMessage id="navigation.admin.signIn" /></A>
</div>);
if (string === "already-logged-in") return (<FormattedMessage id="errorMessages.notLoggedIn" />);
if (string === "already-exists") return (<FormattedMessage id="errorMessages.alreadyExists" />);
}
Expand Down
5 changes: 2 additions & 3 deletions components/PWA.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react";
import config from "../config/client";
import withStores from "../store/withStores";
import NotificationStore from "../store/Notification";
import AppsIcon from "mdi-react/AppsIcon";
Expand All @@ -17,7 +16,7 @@ export default withStores(NotificationStore, class PWA extends React.PureCompone
}

componentDidMount() {
if (config.promptForAddToHomeScreenAfter) {
if (this.props.promptForAddToHomeScreenAfter) {
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
Expand All @@ -26,7 +25,7 @@ export default withStores(NotificationStore, class PWA extends React.PureCompone
const didSelect = localStorage.getItem(STORAGE_DID_SELECT);
if (!didSelect) {
this.setState({ promptEvent: e });
window.setTimeout(this.promptForPwa, config.promptForAddToHomeScreenAfter);
window.setTimeout(this.promptForPwa, this.props.promptForAddToHomeScreenAfter);
}
});
}
Expand Down
25 changes: 14 additions & 11 deletions components/WebPush.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react";
import config from "../config/client";
import { get, post } from "axios";
import withStores from "../store/withStores";
import NotificationStore from "../store/Notification";
Expand All @@ -16,26 +15,30 @@ export default withStores(NotificationStore, class WebPush extends React.PureCom
}

componentDidMount() {
if (config.promptForNotificationsAfter && "serviceWorker" in navigator) {
if (this.props.promptForNotificationsAfter && "serviceWorker" in navigator) {
navigator.serviceWorker.ready.then(() => {
const didSelect = localStorage.getItem(STORAGE_DID_SELECT);
if (!didSelect) {
setTimeout(this.promptForNotifications, config.promptForNotificationsAfter);
setTimeout(this.promptForNotifications, this.props.promptForNotificationsAfter);
}
});
}
}

async subscribeToNotifications(key) {
const sw = await navigator.serviceWorker.getRegistration();
const subscription = await sw.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(key)
});
post("/api/subscription/subscribe", {
subscription,
device: navigator.userAgent
});
if (sw) {
const subscription = await sw.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(key)
});
post("/api/subscription/subscribe", {
subscription,
device: navigator.userAgent
});
} else {
console.error("No Service Worker found.");
}
}

/**
Expand Down
54 changes: 54 additions & 0 deletions components/fields/DropdownField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from "react";
import ValidationResult from "@react-formilicious/core/validators/ValidationResult";
import classnames from "classnames"
import MenuDownOutlineIcon from "mdi-react/MenuDownOutlineIcon";
import { FormattedMessage } from "react-intl";

export default class DropdownField extends React.Component {
constructor() {
super();
}

static getDefaultValue() {
return "";
}

render() {
const {
multiple = false, name,
system: { waiting }, value, field,
values = []
} = this.props;
const selected = values.find(({ key }) => key === value);
return (<div className="field" onDragOver={this.onDragOver} onDragLeave={this.onDragLeave} onDrop={this.onDrop} >
<label className="label">{name}</label>
<div className="contol">
<div className="dropdown is-hoverable">
<div className="dropdown-trigger">
<button type="button" className="button" aria-haspopup="true" aria-controls="dropdown-menu2">
<span>{selected ? selected.label : <FormattedMessage id="form.select" />}</span>
<span className="icon is-small">
<MenuDownOutlineIcon />
</span>
</button>
</div>
<div className="dropdown-menu" id="dropdown-menu2" role="menu">
<div className="dropdown-content">
{values.map(({ key, label }, i) => (<a onClick={() => this.props.onChange(key)} key={i + "-" + key} className="dropdown-item">
{label}
</a>))}
</div>
</div>
</div>
</div>
<ValidationResult {...field} />
</div>);
}

renderFileList(files) {
if (!files || !files.length) return (<FormattedMessage id="form.noFilesSelected" />);
if (files.length !== 1) return (<FormattedMessage id="form.filesSelected" values={{ n: files.length }} />);
// todo: display more than one file name if it fits on the screen
return files[0].name;
}
}
Loading

0 comments on commit e6a7b44

Please sign in to comment.