FAQ

  1. What is Project Forge?
  2. What modules are available?
  3. What CLI commands are available?
  4. What actions are available for my project from the UI?
  5. What build options are available?
  6. What git actions are available?
  7. How do I make a new HTTP action?
  8. What is provided in the TypeScript application?
  9. How do I use custom modules?
  10. How do I manage more than one Project Forge project at once?
  11. How are static assets served?
  12. How do I work with SVG icons?
  13. What’s this PF_SECTION crap?
  14. What administrative functions are available?
  15. How can I secure my application?
  16. What options are available for the HTML menu and breadcrumbs?
  17. What search facilities are available?
  18. What utility methods are available for my app?
  19. How do I add a new model to the “export” facilities?

What is Project Forge?

Project Forge helps you build and maintain feature-rich applications written in the Go programming language.

It’s a code-generation platform that uses modules, which are self-contained features for your app. All managed projects expose a web and CLI interface, and additional modules are available for everything from OAuth to database access.

What modules are available?

What CLI commands are available?

What actions are available for my project from the UI?

What build options are available?

What git actions are available?

Common git actions like status, fetch, pull and commit do exactly what you’d expect. There’s also a magic action, which is a little crazy: - If there are uncommitted changes, it will stash them - If there are pending upstream commits, it will pull them - If a stash was previously created, it will pop and commit it - If commits exist that haven’t been pushed, it will push them

How do I make a new HTTP action?

Create a new file in app/controller or a child directory, then add your method. The usual form is:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
// All controller actions require an http request/response
func CurrentTime(w http.ResponseWriter, r *http.Request) {
// The Act method wires up the page state for your logic
Act("current.time", w, r, func(as *app.State, ps *cutil.PageState) (string, error) {
// The PageState::Title is used as the HTML title
ps.Title = "The current server time for " + util.AppName
// For example purposes
t := util.TimeCurrent()
// PageState::Data will be rendered as JSON or XML if the Content-Type of the request matches
ps.Data = t
// The Render method will send the template contents if HTML is requested. The final argument refers to the active menu key
return Render(r, as, &views.CurrentTime{Time: t}, ps, "time")
})
}

Add your action to ./app/controller/routes/routes.go like so:

makeRoute(r, http.MethodGet, "/time", controller.CurrentTime)

Then create a menu item in ./app/controller/cmenu/menu.go like so:

1
2
i := &menu.Item{Key: "time", Title: "Current Time", Description: "...", Icon: "star", Route: "/time"}
ret = append(ret, i)

Templates are written in quicktemplate, and generally take this form:

 1
2
3
4
5
6
7
8
9
10
11
{% code type CurrentTime struct {
layout.Basic
Time time.Time
} %}

{% func (p *CurrentTime) Body(as *app.State, ps *cutil.PageState) %}
<div class="card">
<h3>{%= components.SVGRefIcon(`calendar`, ps) %}The Current Time!</h3>
<p>{%s fmt.Sprint(p.Time) %}</p>
</div>
{% endfunc %}

The layout.Basic renders menu and navigation, and is swappable for custom response types.

What is provided in the TypeScript application?

The TypeScript project (available at ./client) is dependency-free, lightweight, and is built with ESBuild. Most code is wired in automatically when the build’s .js output is requested. See the code in ./client/src for details.

CSS is also built by ESBuild, see ./client/src/client.css and the files in ./client/src/style.

How do I use custom modules?

If you want to make your own module, you can load it in Project Forge by adding the following section to the “info” object in ./.projectforge/project.json:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"moduleDefs": [
{
"key": "foo",
"path": "./module_foo",
"url": "https://github.com/me/foo"
},
{
"key": "*",
"path": "../modules",
"url": "https://github.com/me/modules"
}
]
}

How do I manage more than one Project Forge project at once?

Create the file ./projectforge/additional-projects.json, containing a JSON array of string paths to other projects.

How are static assets served?

Assets are embedded into the servier binary at build time using Go’s embed package. This brings a lot of advantages, but you’ll need to rebuild before you see changes to your assets. The script ./bin/dev.sh handles the rebuild/restart automatically.

How do I work with SVG icons?

To see available icons, click the SVG button on your project’s page. To add a new icon, enter the name of the icon from Line Awesome or paste a URL. When added, the icon is rewritten to support themes/styling and references.

To reference an icon, add {%= components.SVG("star") %} in your template. Other helper methods available, see ./views/compnents/SVG.html

What’s this PF_SECTION crap?

Most files are either fully-generated by Project Forge, or completely custom to your application. Some files are managed but also support custom code. The text you place inside a PF_SECTION will be preserved when your project is updated.

As always, adding $PF_IGNORE$ at the top of your file causes it to be skipped entirely.

What administrative functions are available?

Assuming you’ve got access, going to /admin will show you the available actions:

How can I secure my application?

TODO

What options are available for the HTML menu and breadcrumbs?

TODO

What search facilities are available?

TODO

What utility methods are available for my app?

So many. See the files in ./app/util, there’s a ton of juicy stuff.

How do I add a new model to the “export” facilities?

Export configuration is stored in ./.projectforge/export. A rudimentary UI is provided in the “Export” section of your project’s page. To be honest, the easiest way to do this is to copy/paste and modify an existing model definition. There’s some good examples at rituals.dev.