2014-04-10 08:10:12 -04:00
// Copyright © 2013-14 Steve Francia <spf@spf13.com>.
2013-07-04 11:32:55 -04:00
//
// Licensed under the Simple Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://opensource.org/licenses/Simple-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugolib
import (
2014-01-29 17:50:31 -05:00
"bytes"
2014-09-14 07:23:03 -04:00
"errors"
2014-01-29 17:50:31 -05:00
"fmt"
"html/template"
"io"
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
"net/url"
2014-01-29 17:50:31 -05:00
"os"
2014-12-27 08:11:19 -05:00
"path/filepath"
2014-08-29 13:40:21 -04:00
"strconv"
2014-01-29 17:50:31 -05:00
"strings"
2014-03-05 19:07:39 -05:00
"sync"
2014-01-29 17:50:31 -05:00
"time"
2014-03-31 13:23:34 -04:00
2015-01-30 14:51:06 -05:00
"sync/atomic"
2014-03-31 13:23:34 -04:00
"bitbucket.org/pkg/inflect"
2014-04-23 02:55:43 -04:00
"github.com/spf13/cast"
2015-01-30 14:51:06 -05:00
bp "github.com/spf13/hugo/bufferpool"
2014-03-31 13:23:34 -04:00
"github.com/spf13/hugo/helpers"
2014-11-01 11:57:29 -04:00
"github.com/spf13/hugo/hugofs"
2015-01-20 17:08:01 -05:00
"github.com/spf13/hugo/parser"
2014-03-31 13:23:34 -04:00
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/target"
2014-11-20 12:32:21 -05:00
"github.com/spf13/hugo/tpl"
2014-03-31 13:23:34 -04:00
"github.com/spf13/hugo/transform"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/nitro"
2014-04-05 01:26:43 -04:00
"github.com/spf13/viper"
2013-07-04 11:32:55 -04:00
)
2013-11-05 00:28:08 -05:00
var _ = transform . AbsURL
2013-10-03 09:44:45 -04:00
var DefaultTimer * nitro . B
2013-08-07 20:21:22 -04:00
2015-03-31 16:33:24 -04:00
var distinctErrorLogger = helpers . NewDistinctErrorLogger ( )
2013-10-17 23:57:25 -04:00
// Site contains all the information relevant for constructing a static
2013-09-01 00:13:04 -04:00
// site. The basic flow of information is as follows:
//
// 1. A list of Files is parsed and then converted into Pages.
//
// 2. Pages contain sections (based on the file they were generated from),
// aliases and slugs (included in a pages frontmatter) which are the
2013-11-18 04:35:56 -05:00
// various targets that will get generated. There will be canonical
// listing. The canonical path can be overruled based on a pattern.
2013-09-01 00:13:04 -04:00
//
2014-04-08 23:15:57 -04:00
// 3. Taxonomies are created via configuration and will present some aspect of
2013-09-01 00:13:04 -04:00
// the final page and typically a perm url.
//
// 4. All Pages are passed through a template based on their desired
2013-11-18 04:35:56 -05:00
// layout based on numerous different elements.
2013-09-01 00:13:04 -04:00
//
// 5. The entire collection of files is written to disk.
2013-07-04 11:32:55 -04:00
type Site struct {
2015-02-20 12:38:35 -05:00
Pages Pages
Files [ ] * source . File
Tmpl tpl . Template
Taxonomies TaxonomyList
Source source . Input
Sections Taxonomy
Info SiteInfo
Menus Menus
timer * nitro . B
Targets targetList
targetListInit sync . Once
Completed chan bool
RunMode runmode
params map [ string ] interface { }
draftCount int
futureCount int
Data map [ string ] interface { }
2013-07-04 11:32:55 -04:00
}
2014-11-04 00:39:37 -05:00
type targetList struct {
Page target . Output
File target . Output
Alias target . AliasPublisher
}
2013-07-04 11:32:55 -04:00
type SiteInfo struct {
2015-03-18 01:16:54 -04:00
BaseURL template . URL
2014-12-27 08:11:19 -05:00
Taxonomies TaxonomyList
Authors AuthorList
Social SiteSocial
Sections Taxonomy
Pages * Pages
Files [ ] * source . File
Menus * Menus
Hugo * HugoInfo
Title string
Author map [ string ] interface { }
LanguageCode string
DisqusShortname string
Copyright string
LastChange time . Time
Permalinks PermalinkOverrides
Params map [ string ] interface { }
BuildDrafts bool
2015-03-11 13:34:57 -04:00
canonifyURLs bool
2014-12-27 08:11:19 -05:00
paginationPageCount uint64
2015-02-08 15:09:14 -05:00
Data * map [ string ] interface { }
2014-04-23 02:52:01 -04:00
}
2014-04-23 02:55:43 -04:00
2014-12-09 13:33:55 -05:00
// SiteSocial is a place to put social details on a site level. These are the
// standard keys that themes will expect to have available, but can be
// expanded to any others on a per site basis
// github
// facebook
// facebook_admin
// twitter
// twitter_domain
// googleplus
// pinterest
// instagram
// youtube
// linkedin
type SiteSocial map [ string ] string
2015-03-18 01:16:54 -04:00
// BaseUrl is deprecated. Will be removed in 0.15.
func ( s * SiteInfo ) BaseUrl ( ) template . URL {
helpers . Deprecated ( "Site" , ".BaseUrl" , ".BaseURL" )
return s . BaseURL
}
// Recent is deprecated. Will be removed in 0.15.
func ( s * SiteInfo ) Recent ( ) * Pages {
helpers . Deprecated ( "Site" , ".Recent" , ".Pages" )
return s . Pages
}
// Indexes is deprecated. Will be removed in 0.15.
func ( s * SiteInfo ) Indexes ( ) * TaxonomyList {
helpers . Deprecated ( "Site" , ".Indexes" , ".Taxonomies" )
return & s . Taxonomies
}
2014-04-23 02:55:43 -04:00
func ( s * SiteInfo ) GetParam ( key string ) interface { } {
v := s . Params [ strings . ToLower ( key ) ]
if v == nil {
return nil
}
switch v . ( type ) {
case bool :
return cast . ToBool ( v )
case string :
return cast . ToString ( v )
case int64 , int32 , int16 , int8 , int :
return cast . ToInt ( v )
case float64 , float32 :
return cast . ToFloat64 ( v )
case time . Time :
return cast . ToTime ( v )
case [ ] string :
return v
}
return nil
2013-07-04 11:32:55 -04:00
}
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
func ( s * SiteInfo ) refLink ( ref string , page * Page , relative bool ) ( string , error ) {
2015-03-11 13:34:57 -04:00
var refURL * url . URL
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
var err error
2015-03-11 13:34:57 -04:00
refURL , err = url . Parse ( ref )
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
if err != nil {
return "" , err
}
2015-03-07 06:53:20 -05:00
var target * Page
var link string
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
2015-03-11 13:34:57 -04:00
if refURL . Path != "" {
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
for _ , page := range [ ] * Page ( * s . Pages ) {
2015-03-17 10:38:48 -04:00
refPath := filepath . FromSlash ( refURL . Path )
if page . Source . Path ( ) == refPath || page . Source . LogicalName ( ) == refPath {
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
target = page
break
}
}
if target == nil {
2015-03-11 13:34:57 -04:00
return "" , fmt . Errorf ( "No page found with path or logical name \"%s\".\n" , refURL . Path )
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
}
if relative {
link , err = target . RelPermalink ( )
} else {
link , err = target . Permalink ( )
}
if err != nil {
return "" , err
}
}
2015-03-11 13:34:57 -04:00
if refURL . Fragment != "" {
link = link + "#" + refURL . Fragment
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
2015-03-11 13:34:57 -04:00
if refURL . Path != "" && target != nil && ! target . getRenderingConfig ( ) . PlainIDAnchors {
link = link + ":" + target . UniqueID ( )
} else if page != nil && ! page . getRenderingConfig ( ) . PlainIDAnchors {
link = link + ":" + page . UniqueID ( )
Provide (relative) reference funcs & shortcodes.
- `.Ref` and `.RelRef` take a reference (the logical filename for a
page, including extension and/or a document fragment ID) and return
a permalink (or relative permalink) to the referenced document.
- If the reference is a page name (such as `about.md`), the page
will be discovered and the permalink will be returned: `/about/`
- If the reference is a page name with a fragment (such as
`about.md#who`), the page will be discovered and used to add the
`page.UniqueID()` to the resulting fragment and permalink:
`/about/#who:deadbeef`.
- If the reference is a fragment and `.*Ref` has been called from
a `Node` or `SiteInfo`, it will be returned as is: `#who`.
- If the reference is a fragment and `.*Ref` has been called from
a `Page`, it will be returned with the page’s unique ID:
`#who:deadbeef`.
- `.*Ref` can be called from either `Node`, `SiteInfo` (e.g.,
`Node.Site`), `Page` objects, or `ShortcodeWithPage` objects in
templates.
- `.*Ref` cannot be used in content, so two shortcodes have been
created to provide the functionality to content: `ref` and `relref`.
These are intended to be used within markup, like `[Who]({{% ref
about.md#who %}})` or `<a href="{{% ref about.md#who %}}">Who</a>`.
- There are also `ref` and `relref` template functions (used to create
the shortcodes) that expect a `Page` or `Node` object and the
reference string (e.g., `{{ relref . "about.md" }}` or `{{
"about.md" | ref . }}`). It actually looks for `.*Ref` as defined on
`Node` or `Page` objects.
- Shortcode handling had to use a *differently unique* wrapper in
`createShortcodePlaceholder` because of the way that the `ref` and
`relref` are intended to be used in content.
2014-11-24 01:15:34 -05:00
}
}
return link , nil
}
func ( s * SiteInfo ) Ref ( ref string , page * Page ) ( string , error ) {
return s . refLink ( ref , page , false )
}
func ( s * SiteInfo ) RelRef ( ref string , page * Page ) ( string , error ) {
return s . refLink ( ref , page , true )
}
2014-12-27 08:11:19 -05:00
func ( s * SiteInfo ) addToPaginationPageCount ( cnt uint64 ) {
atomic . AddUint64 ( & s . paginationPageCount , cnt )
}
2013-10-25 18:03:14 -04:00
type runmode struct {
2014-01-29 17:50:31 -05:00
Watching bool
2013-10-25 18:03:14 -04:00
}
func ( s * Site ) Running ( ) bool {
2014-01-29 17:50:31 -05:00
return s . RunMode . Watching
2013-10-25 18:03:14 -04:00
}
2013-10-03 09:44:45 -04:00
func init ( ) {
2014-01-29 17:50:31 -05:00
DefaultTimer = nitro . Initalize ( )
2013-10-03 09:44:45 -04:00
}
2013-08-07 20:21:22 -04:00
func ( s * Site ) timerStep ( step string ) {
2014-01-29 17:50:31 -05:00
if s . timer == nil {
s . timer = DefaultTimer
}
s . timer . Step ( step )
2013-07-04 11:32:55 -04:00
}
2013-08-31 20:29:19 -04:00
func ( s * Site ) Build ( ) ( err error ) {
2014-01-29 17:50:31 -05:00
if err = s . Process ( ) ; err != nil {
return
}
if err = s . Render ( ) ; err != nil {
2014-03-31 13:23:34 -04:00
jww . ERROR . Printf ( "Error rendering site: %s\nAvailable templates:\n" , err )
2014-01-29 17:50:31 -05:00
for _ , template := range s . Tmpl . Templates ( ) {
2014-03-31 13:23:34 -04:00
jww . ERROR . Printf ( "\t%s\n" , template . Name ( ) )
2014-01-29 17:50:31 -05:00
}
return
}
return nil
2013-07-04 11:32:55 -04:00
}
2013-08-31 20:29:19 -04:00
func ( s * Site ) Analyze ( ) {
2014-01-29 17:50:31 -05:00
s . Process ( )
s . ShowPlan ( os . Stdout )
2013-07-04 11:32:55 -04:00
}
2013-08-31 20:47:21 -04:00
func ( s * Site ) prepTemplates ( ) {
2014-11-24 17:10:38 -05:00
s . Tmpl = tpl . InitializeT ( )
2014-01-29 17:50:31 -05:00
s . Tmpl . LoadTemplates ( s . absLayoutDir ( ) )
2014-04-10 08:10:12 -04:00
if s . hasTheme ( ) {
s . Tmpl . LoadTemplatesWithPrefix ( s . absThemeDir ( ) + "/layouts" , "theme" )
}
2013-08-31 20:47:21 -04:00
}
func ( s * Site ) addTemplate ( name , data string ) error {
2014-01-29 17:50:31 -05:00
return s . Tmpl . AddTemplate ( name , data )
2013-08-31 20:47:21 -04:00
}
2015-02-11 14:24:56 -05:00
func ( s * Site ) loadData ( sources [ ] source . Input ) ( err error ) {
2015-01-20 17:08:01 -05:00
s . Data = make ( map [ string ] interface { } )
2015-02-08 15:09:14 -05:00
var current map [ string ] interface { }
2015-02-11 14:24:56 -05:00
for _ , currentSource := range sources {
for _ , r := range currentSource . Files ( ) {
// Crawl in data tree to insert data
current = s . Data
for _ , key := range strings . Split ( r . Dir ( ) , helpers . FilePathSeparator ) {
if key != "" {
if _ , ok := current [ key ] ; ! ok {
current [ key ] = make ( map [ string ] interface { } )
}
current = current [ key ] . ( map [ string ] interface { } )
2015-01-20 17:08:01 -05:00
}
}
2015-02-11 14:24:56 -05:00
data , err := readData ( r )
if err != nil {
return fmt . Errorf ( "Failed to read data from %s: %s" , filepath . Join ( r . Path ( ) , r . LogicalName ( ) ) , err )
}
2015-01-20 17:08:01 -05:00
2015-04-22 12:36:07 -04:00
if data == nil {
continue
}
2015-02-11 14:24:56 -05:00
// Copy content from current to data when needed
if _ , ok := current [ r . BaseFileName ( ) ] ; ok {
data := data . ( map [ string ] interface { } )
for key , value := range current [ r . BaseFileName ( ) ] . ( map [ string ] interface { } ) {
if _ , override := data [ key ] ; override {
// filepath.Walk walks the files in lexical order, '/' comes before '.'
// this warning could happen if
// 1. A theme uses the same key; the main data folder wins
// 2. A sub folder uses the same key: the sub folder wins
jww . WARN . Printf ( "Data for key '%s' in path '%s' is overridden in subfolder" , key , r . Path ( ) )
}
data [ key ] = value
2015-01-20 17:08:01 -05:00
}
}
2015-02-11 14:24:56 -05:00
// Insert data
current [ r . BaseFileName ( ) ] = data
}
2015-01-20 17:08:01 -05:00
}
return
}
2015-02-08 15:09:14 -05:00
func readData ( f * source . File ) ( interface { } , error ) {
2015-01-20 17:08:01 -05:00
switch f . Extension ( ) {
case "yaml" , "yml" :
2015-03-11 13:34:57 -04:00
return parser . HandleYAMLMetaData ( f . Bytes ( ) )
2015-01-20 17:08:01 -05:00
case "json" :
2015-03-11 13:34:57 -04:00
return parser . HandleJSONMetaData ( f . Bytes ( ) )
2015-01-20 17:08:01 -05:00
case "toml" :
2015-03-11 13:34:57 -04:00
return parser . HandleTOMLMetaData ( f . Bytes ( ) )
2015-01-20 17:08:01 -05:00
default :
2015-04-22 12:36:07 -04:00
jww . WARN . Printf ( "Data not supported for extension '%s'" , f . Extension ( ) )
return nil , nil
2015-01-20 17:08:01 -05:00
}
}
2013-08-31 20:29:19 -04:00
func ( s * Site ) Process ( ) ( err error ) {
2014-01-29 17:50:31 -05:00
if err = s . initialize ( ) ; err != nil {
return
}
s . prepTemplates ( )
2015-01-30 18:56:25 -05:00
s . Tmpl . PrintErrors ( )
2015-02-08 15:09:14 -05:00
s . timerStep ( "initialize & template prep" )
2015-02-11 14:24:56 -05:00
dataSources := make ( [ ] source . Input , 0 , 2 )
dataSources = append ( dataSources , & source . Filesystem { Base : s . absDataDir ( ) } )
// have to be last - duplicate keys in earlier entries will win
themeStaticDir , err := helpers . GetThemeDataDirPath ( )
if err == nil {
dataSources = append ( dataSources , & source . Filesystem { Base : themeStaticDir } )
}
if err = s . loadData ( dataSources ) ; err != nil {
2015-01-20 17:08:01 -05:00
return
}
s . timerStep ( "load data" )
2015-02-08 15:09:14 -05:00
2014-01-29 17:50:31 -05:00
if err = s . CreatePages ( ) ; err != nil {
return
}
s . setupPrevNext ( )
s . timerStep ( "import pages" )
if err = s . BuildSiteMeta ( ) ; err != nil {
return
}
2014-04-08 23:15:57 -04:00
s . timerStep ( "build taxonomies" )
2014-01-29 17:50:31 -05:00
return
2013-07-04 11:32:55 -04:00
}
2013-09-20 20:03:43 -04:00
func ( s * Site ) setupPrevNext ( ) {
2014-01-29 17:50:31 -05:00
for i , page := range s . Pages {
if i < len ( s . Pages ) - 1 {
page . Next = s . Pages [ i + 1 ]
}
2013-09-20 20:03:43 -04:00
2014-01-29 17:50:31 -05:00
if i > 0 {
page . Prev = s . Pages [ i - 1 ]
}
}
2013-09-20 20:03:43 -04:00
}
2013-08-31 20:29:19 -04:00
func ( s * Site ) Render ( ) ( err error ) {
2014-01-29 17:50:31 -05:00
if err = s . RenderAliases ( ) ; err != nil {
return
}
s . timerStep ( "render and write aliases" )
2014-04-08 23:15:57 -04:00
if err = s . RenderTaxonomiesLists ( ) ; err != nil {
2014-01-29 17:50:31 -05:00
return
}
2014-04-08 23:15:57 -04:00
s . timerStep ( "render and write taxonomies" )
s . RenderListsOfTaxonomyTerms ( )
s . timerStep ( "render & write taxonomy lists" )
if err = s . RenderSectionLists ( ) ; err != nil {
2014-01-29 17:50:31 -05:00
return
}
s . timerStep ( "render and write lists" )
if err = s . RenderPages ( ) ; err != nil {
return
}
s . timerStep ( "render and write pages" )
if err = s . RenderHomePage ( ) ; err != nil {
return
}
s . timerStep ( "render and write homepage" )
2014-05-06 06:50:23 -04:00
if err = s . RenderSitemap ( ) ; err != nil {
return
}
s . timerStep ( "render and write Sitemap" )
2014-01-29 17:50:31 -05:00
return
2013-07-04 11:32:55 -04:00
}
2013-08-31 20:29:19 -04:00
func ( s * Site ) checkDescriptions ( ) {
2014-01-29 17:50:31 -05:00
for _ , p := range s . Pages {
if len ( p . Description ) < 60 {
2014-10-16 20:20:09 -04:00
jww . FEEDBACK . Println ( p . Source . Path ( ) + " " )
2014-01-29 17:50:31 -05:00
}
}
2013-07-04 11:32:55 -04:00
}
2014-05-01 13:20:58 -04:00
func ( s * Site ) Initialise ( ) ( err error ) {
return s . initialize ( )
}
2013-09-25 00:24:49 -04:00
func ( s * Site ) initialize ( ) ( err error ) {
2014-01-29 17:50:31 -05:00
if err = s . checkDirectories ( ) ; err != nil {
return err
}
2013-07-04 11:32:55 -04:00
2014-04-05 01:26:43 -04:00
staticDir := helpers . AbsPathify ( viper . GetString ( "StaticDir" ) + "/" )
2013-08-15 14:58:34 -04:00
2014-01-29 17:50:31 -05:00
s . Source = & source . Filesystem {
AvoidPaths : [ ] string { staticDir } ,
Base : s . absContentDir ( ) ,
}
2013-07-04 11:32:55 -04:00
2014-04-23 02:59:19 -04:00
s . Menus = Menus { }
2014-01-29 17:50:31 -05:00
s . initializeSiteInfo ( )
2013-09-12 19:17:53 -04:00
2014-01-29 17:50:31 -05:00
return
2013-09-12 19:17:53 -04:00
}
func ( s * Site ) initializeSiteInfo ( ) {
2014-05-28 18:37:59 -04:00
params := viper . GetStringMap ( "Params" )
2014-04-05 01:26:43 -04:00
2014-05-10 20:27:49 -04:00
permalinks := make ( PermalinkOverrides )
for k , v := range viper . GetStringMapString ( "Permalinks" ) {
permalinks [ k ] = PathPattern ( v )
2014-04-05 01:26:43 -04:00
}
2014-04-24 12:18:47 -04:00
s . Info = SiteInfo {
2015-03-18 01:16:54 -04:00
BaseURL : template . URL ( helpers . SanitizeURLKeepTrailingSlash ( viper . GetString ( "BaseURL" ) ) ) ,
2014-04-23 02:52:01 -04:00
Title : viper . GetString ( "Title" ) ,
2014-10-31 23:58:14 -04:00
Author : viper . GetStringMap ( "author" ) ,
2014-04-23 02:52:01 -04:00
LanguageCode : viper . GetString ( "languagecode" ) ,
Copyright : viper . GetString ( "copyright" ) ,
DisqusShortname : viper . GetString ( "DisqusShortname" ) ,
2014-09-10 22:34:00 -04:00
BuildDrafts : viper . GetBool ( "BuildDrafts" ) ,
2015-03-11 13:34:57 -04:00
canonifyURLs : viper . GetBool ( "CanonifyURLs" ) ,
2014-09-05 06:57:32 -04:00
Pages : & s . Pages ,
2014-04-23 02:59:19 -04:00
Menus : & s . Menus ,
2014-04-23 02:52:01 -04:00
Params : params ,
Permalinks : permalinks ,
2015-01-20 17:08:01 -05:00
Data : & s . Data ,
2014-01-29 17:50:31 -05:00
}
2013-07-04 11:32:55 -04:00
}
2014-04-10 08:10:12 -04:00
func ( s * Site ) hasTheme ( ) bool {
return viper . GetString ( "theme" ) != ""
}
2015-01-20 17:08:01 -05:00
func ( s * Site ) absDataDir ( ) string {
return helpers . AbsPathify ( viper . GetString ( "DataDir" ) )
}
2014-04-10 08:10:12 -04:00
func ( s * Site ) absThemeDir ( ) string {
return helpers . AbsPathify ( "themes/" + viper . GetString ( "theme" ) )
}
2013-08-07 20:21:22 -04:00
func ( s * Site ) absLayoutDir ( ) string {
2014-04-05 01:26:43 -04:00
return helpers . AbsPathify ( viper . GetString ( "LayoutDir" ) )
2013-08-07 20:21:22 -04:00
}
func ( s * Site ) absContentDir ( ) string {
2014-04-05 01:26:43 -04:00
return helpers . AbsPathify ( viper . GetString ( "ContentDir" ) )
2013-08-07 20:21:22 -04:00
}
func ( s * Site ) absPublishDir ( ) string {
2014-04-05 01:26:43 -04:00
return helpers . AbsPathify ( viper . GetString ( "PublishDir" ) )
2013-08-07 20:21:22 -04:00
}
2013-09-25 00:24:49 -04:00
func ( s * Site ) checkDirectories ( ) ( err error ) {
2014-11-01 11:57:29 -04:00
if b , _ := helpers . DirExists ( s . absContentDir ( ) , hugofs . SourceFs ) ; ! b {
2014-01-29 17:50:31 -05:00
return fmt . Errorf ( "No source directory found, expecting to find it at " + s . absContentDir ( ) )
}
return
2013-07-04 11:32:55 -04:00
}
2014-09-05 06:57:32 -04:00
type pageResult struct {
2014-08-29 13:40:21 -04:00
page * Page
err error
}
func ( s * Site ) CreatePages ( ) error {
2014-01-29 17:50:31 -05:00
if s . Source == nil {
panic ( fmt . Sprintf ( "s.Source not set %s" , s . absContentDir ( ) ) )
}
if len ( s . Source . Files ( ) ) < 1 {
2014-08-29 13:40:21 -04:00
return nil
2014-01-29 17:50:31 -05:00
}
2014-08-29 13:40:21 -04:00
files := s . Source . Files ( )
2014-01-29 17:50:31 -05:00
2014-10-20 17:42:16 -04:00
results := make ( chan HandledResult )
2014-09-05 06:57:32 -04:00
filechan := make ( chan * source . File )
2014-01-29 17:50:31 -05:00
2014-08-29 13:40:21 -04:00
procs := getGoMaxProcs ( )
2014-03-05 19:07:39 -05:00
2014-08-29 13:40:21 -04:00
wg := & sync . WaitGroup { }
2014-03-05 19:07:39 -05:00
2014-11-24 17:51:47 -05:00
wg . Add ( procs * 4 )
2014-08-30 01:15:20 -04:00
for i := 0 ; i < procs * 4 ; i ++ {
2014-10-16 20:20:09 -04:00
go sourceReader ( s , filechan , results , wg )
2014-08-29 13:40:21 -04:00
}
2014-03-05 19:07:39 -05:00
2014-08-29 13:40:21 -04:00
errs := make ( chan error )
2014-08-25 13:13:11 -04:00
2014-08-29 13:40:21 -04:00
// we can only have exactly one result collator, since it makes changes that
// must be synchronized.
2014-09-05 06:57:32 -04:00
go readCollator ( s , results , errs )
2014-08-20 11:09:35 -04:00
2014-09-05 06:57:32 -04:00
for _ , file := range files {
filechan <- file
2014-01-29 17:50:31 -05:00
}
2014-09-05 06:57:32 -04:00
close ( filechan )
2014-08-29 13:40:21 -04:00
2014-03-05 19:07:39 -05:00
wg . Wait ( )
2014-08-29 13:40:21 -04:00
close ( results )
2014-09-05 06:57:32 -04:00
readErrs := <- errs
2014-10-20 17:42:16 -04:00
results = make ( chan HandledResult )
2014-11-04 00:36:05 -05:00
pageChan := make ( chan * Page )
fileConvChan := make ( chan * source . File )
2014-09-05 06:57:32 -04:00
wg = & sync . WaitGroup { }
2014-11-24 17:51:47 -05:00
wg . Add ( 2 * procs * 4 )
2014-10-20 20:15:33 -04:00
for i := 0 ; i < procs * 4 ; i ++ {
2014-11-04 00:36:05 -05:00
go fileConverter ( s , fileConvChan , results , wg )
go pageConverter ( s , pageChan , results , wg )
2014-09-05 06:57:32 -04:00
}
2014-09-14 07:01:40 -04:00
go converterCollator ( s , results , errs )
2014-09-05 06:57:32 -04:00
for _ , p := range s . Pages {
2014-11-04 00:36:05 -05:00
pageChan <- p
2014-09-05 06:57:32 -04:00
}
2014-10-20 20:15:33 -04:00
for _ , f := range s . Files {
2014-11-04 00:36:05 -05:00
fileConvChan <- f
2014-10-20 20:15:33 -04:00
}
2014-11-04 00:36:05 -05:00
close ( pageChan )
close ( fileConvChan )
2014-09-05 06:57:32 -04:00
wg . Wait ( )
close ( results )
renderErrs := <- errs
if renderErrs == nil && readErrs == nil {
return nil
}
if renderErrs == nil {
return readErrs
}
if readErrs == nil {
return renderErrs
}
return fmt . Errorf ( "%s\n%s" , readErrs , renderErrs )
2014-08-29 13:40:21 -04:00
}
2014-10-20 17:42:16 -04:00
func sourceReader ( s * Site , files <- chan * source . File , results chan <- HandledResult , wg * sync . WaitGroup ) {
2014-09-14 07:01:40 -04:00
defer wg . Done ( )
2014-09-05 06:57:32 -04:00
for file := range files {
2014-11-20 12:39:09 -05:00
h := NewMetaHandler ( file . Extension ( ) )
2014-10-17 16:57:48 -04:00
if h != nil {
2014-10-20 17:51:53 -04:00
h . Read ( file , s , results )
2014-10-17 16:57:48 -04:00
} else {
jww . ERROR . Println ( "Unsupported File Type" , file . Path ( ) )
}
2014-09-05 06:57:32 -04:00
}
}
2014-08-29 13:40:21 -04:00
2014-10-20 17:42:16 -04:00
func pageConverter ( s * Site , pages <- chan * Page , results HandleResults , wg * sync . WaitGroup ) {
2014-09-14 07:01:40 -04:00
defer wg . Done ( )
2014-09-05 06:57:32 -04:00
for page := range pages {
2014-11-20 12:39:09 -05:00
var h * MetaHandle
2014-11-04 00:44:30 -05:00
if page . Markup != "" {
2014-11-20 12:39:09 -05:00
h = NewMetaHandler ( page . Markup )
2014-11-04 00:44:30 -05:00
} else {
2014-11-20 12:39:09 -05:00
h = NewMetaHandler ( page . File . Extension ( ) )
2014-11-04 00:44:30 -05:00
}
2014-10-20 20:15:33 -04:00
if h != nil {
h . Convert ( page , s , results )
}
}
}
2014-08-29 13:40:21 -04:00
2014-10-20 20:15:33 -04:00
func fileConverter ( s * Site , files <- chan * source . File , results HandleResults , wg * sync . WaitGroup ) {
defer wg . Done ( )
for file := range files {
2014-11-20 12:39:09 -05:00
h := NewMetaHandler ( file . Extension ( ) )
2014-10-20 20:15:33 -04:00
if h != nil {
h . Convert ( file , s , results )
2014-08-29 13:40:21 -04:00
}
}
}
2014-10-20 17:42:16 -04:00
func converterCollator ( s * Site , results <- chan HandledResult , errs chan <- error ) {
2014-09-05 06:57:32 -04:00
errMsgs := [ ] string { }
for r := range results {
if r . err != nil {
errMsgs = append ( errMsgs , r . err . Error ( ) )
continue
}
}
if len ( errMsgs ) == 0 {
errs <- nil
return
}
errs <- fmt . Errorf ( "Errors rendering pages: %s" , strings . Join ( errMsgs , "\n" ) )
}
2014-10-20 17:42:16 -04:00
func readCollator ( s * Site , results <- chan HandledResult , errs chan <- error ) {
2014-08-29 13:40:21 -04:00
errMsgs := [ ] string { }
for r := range results {
if r . err != nil {
2014-11-20 12:39:09 -05:00
errMsgs = append ( errMsgs , r . Error ( ) )
2014-08-29 13:40:21 -04:00
continue
}
2014-10-20 20:15:33 -04:00
// !page == file
if r . page == nil {
s . Files = append ( s . Files , r . file )
} else {
if r . page . ShouldBuild ( ) {
s . Pages = append ( s . Pages , r . page )
}
2014-08-29 13:40:21 -04:00
2014-10-20 20:15:33 -04:00
if r . page . IsDraft ( ) {
2015-03-07 06:53:20 -05:00
s . draftCount ++
2014-10-20 20:15:33 -04:00
}
2014-08-29 13:40:21 -04:00
2014-10-20 20:15:33 -04:00
if r . page . IsFuture ( ) {
2015-03-07 06:53:20 -05:00
s . futureCount ++
2014-10-20 20:15:33 -04:00
}
2014-08-29 13:40:21 -04:00
}
}
2014-10-20 20:15:33 -04:00
2014-01-29 17:50:31 -05:00
s . Pages . Sort ( )
2014-08-29 13:40:21 -04:00
if len ( errMsgs ) == 0 {
errs <- nil
2014-09-05 06:57:32 -04:00
return
2014-08-29 13:40:21 -04:00
}
2014-09-05 06:57:32 -04:00
errs <- fmt . Errorf ( "Errors reading pages: %s" , strings . Join ( errMsgs , "\n" ) )
2013-07-04 11:32:55 -04:00
}
2013-08-01 14:55:18 -04:00
func ( s * Site ) BuildSiteMeta ( ) ( err error ) {
2014-04-23 02:59:19 -04:00
s . assembleMenus ( )
if len ( s . Pages ) == 0 {
return
}
s . assembleTaxonomies ( )
s . assembleSections ( )
s . Info . LastChange = s . Pages [ 0 ] . Date
return
}
2014-04-24 18:11:08 -04:00
func ( s * Site ) getMenusFromConfig ( ) Menus {
ret := Menus { }
if menus := viper . GetStringMap ( "menu" ) ; menus != nil {
for name , menu := range menus {
m , err := cast . ToSliceE ( menu )
if err != nil {
jww . ERROR . Printf ( "unable to process menus in site config\n" )
jww . ERROR . Println ( err )
} else {
for _ , entry := range m {
jww . DEBUG . Printf ( "found menu: %q, in site config\n" , name )
menuEntry := MenuEntry { Menu : name }
ime , err := cast . ToStringMapE ( entry )
if err != nil {
jww . ERROR . Printf ( "unable to process menus in site config\n" )
jww . ERROR . Println ( err )
}
menuEntry . MarshallMap ( ime )
2015-05-09 14:54:11 -04:00
menuEntry . URL = s . Info . createNodeMenuEntryURL ( menuEntry . URL )
2014-12-12 14:28:28 -05:00
2014-04-24 18:11:08 -04:00
if ret [ name ] == nil {
ret [ name ] = & Menu { }
}
* ret [ name ] = ret [ name ] . Add ( & menuEntry )
}
}
}
return ret
}
return ret
}
2015-05-09 14:54:11 -04:00
func ( s * SiteInfo ) createNodeMenuEntryURL ( in string ) string {
if ! strings . HasPrefix ( in , "/" ) {
return in
}
// make it match the nodes
menuEntryURL := in
menuEntryURL = helpers . URLizeAndPrep ( menuEntryURL )
if ! s . canonifyURLs {
menuEntryURL = helpers . AddContextRoot ( string ( s . BaseURL ) , menuEntryURL )
}
return menuEntryURL
}
2014-04-23 02:59:19 -04:00
func ( s * Site ) assembleMenus ( ) {
type twoD struct {
MenuName , EntryName string
}
flat := map [ twoD ] * MenuEntry { }
children := map [ twoD ] Menu { }
2014-04-24 18:11:08 -04:00
menuConfig := s . getMenusFromConfig ( )
for name , menu := range menuConfig {
for _ , me := range * menu {
2014-05-14 18:01:13 -04:00
flat [ twoD { name , me . KeyName ( ) } ] = me
2014-04-24 18:11:08 -04:00
}
}
2015-01-06 12:11:06 -05:00
sectionPagesMenu := viper . GetString ( "SectionPagesMenu" )
sectionPagesMenus := make ( map [ string ] interface { } )
2014-04-23 02:59:19 -04:00
//creating flat hash
for _ , p := range s . Pages {
2015-01-06 12:11:06 -05:00
if sectionPagesMenu != "" {
if _ , ok := sectionPagesMenus [ p . Section ( ) ] ; ! ok {
if p . Section ( ) != "" {
2015-05-09 14:54:11 -04:00
me := MenuEntry { Identifier : p . Section ( ) , Name : helpers . MakeTitle ( p . Section ( ) ) , URL : s . Info . createNodeMenuEntryURL ( "/" + p . Section ( ) ) }
2015-01-06 12:11:06 -05:00
if _ , ok := flat [ twoD { sectionPagesMenu , me . KeyName ( ) } ] ; ok {
// menu with same id defined in config, let that one win
continue
}
flat [ twoD { sectionPagesMenu , me . KeyName ( ) } ] = & me
sectionPagesMenus [ p . Section ( ) ] = true
}
}
}
2014-04-23 02:59:19 -04:00
for name , me := range p . Menus ( ) {
2014-05-14 18:01:13 -04:00
if _ , ok := flat [ twoD { name , me . KeyName ( ) } ] ; ok {
jww . ERROR . Printf ( "Two or more menu items have the same name/identifier in %q Menu. Identified as %q.\n Rename or set a unique identifier. \n" , name , me . KeyName ( ) )
2015-01-22 11:23:01 -05:00
continue
2014-05-14 18:01:13 -04:00
}
flat [ twoD { name , me . KeyName ( ) } ] = me
2014-04-23 02:59:19 -04:00
}
}
// Create Children Menus First
for _ , e := range flat {
if e . Parent != "" {
children [ twoD { e . Menu , e . Parent } ] = children [ twoD { e . Menu , e . Parent } ] . Add ( e )
}
}
// Placing Children in Parents (in flat)
for p , childmenu := range children {
_ , ok := flat [ twoD { p . MenuName , p . EntryName } ]
if ! ok {
2015-03-18 01:16:54 -04:00
// if parent does not exist, create one without a URL
flat [ twoD { p . MenuName , p . EntryName } ] = & MenuEntry { Name : p . EntryName , URL : "" }
2014-04-23 02:59:19 -04:00
}
flat [ twoD { p . MenuName , p . EntryName } ] . Children = childmenu
}
// Assembling Top Level of Tree
for menu , e := range flat {
if e . Parent == "" {
_ , ok := s . Menus [ menu . MenuName ]
if ! ok {
s . Menus [ menu . MenuName ] = & Menu { }
}
* s . Menus [ menu . MenuName ] = s . Menus [ menu . MenuName ] . Add ( e )
}
}
}
func ( s * Site ) assembleTaxonomies ( ) {
2014-04-08 23:15:57 -04:00
s . Taxonomies = make ( TaxonomyList )
s . Sections = make ( Taxonomy )
2014-01-29 17:50:31 -05:00
2014-04-08 23:15:57 -04:00
taxonomies := viper . GetStringMapString ( "Taxonomies" )
jww . INFO . Printf ( "found taxonomies: %#v\n" , taxonomies )
2014-04-07 23:29:35 -04:00
2014-04-08 23:15:57 -04:00
for _ , plural := range taxonomies {
s . Taxonomies [ plural ] = make ( Taxonomy )
2014-01-29 17:50:31 -05:00
for _ , p := range s . Pages {
vals := p . GetParam ( plural )
weight := p . GetParam ( plural + "_weight" )
if weight == nil {
weight = 0
}
if vals != nil {
2014-09-05 09:29:01 -04:00
if v , ok := vals . ( [ ] string ) ; ok {
2014-01-29 17:50:31 -05:00
for _ , idx := range v {
x := WeightedPage { weight . ( int ) , p }
2014-04-08 23:15:57 -04:00
s . Taxonomies [ plural ] . Add ( idx , x )
2014-01-29 17:50:31 -05:00
}
2014-09-05 09:29:01 -04:00
} else if v , ok := vals . ( string ) ; ok {
x := WeightedPage { weight . ( int ) , p }
s . Taxonomies [ plural ] . Add ( v , x )
2014-01-29 17:50:31 -05:00
} else {
2014-10-16 20:20:09 -04:00
jww . ERROR . Printf ( "Invalid %s in %s\n" , plural , p . File . Path ( ) )
2014-01-29 17:50:31 -05:00
}
}
}
2014-04-08 23:15:57 -04:00
for k := range s . Taxonomies [ plural ] {
s . Taxonomies [ plural ] [ k ] . Sort ( )
2014-01-29 17:50:31 -05:00
}
}
2014-04-23 02:59:19 -04:00
s . Info . Taxonomies = s . Taxonomies
2014-05-13 10:46:04 -04:00
s . Info . Sections = s . Sections
2014-04-23 02:59:19 -04:00
}
func ( s * Site ) assembleSections ( ) {
2014-01-29 17:50:31 -05:00
for i , p := range s . Pages {
2014-10-16 20:20:09 -04:00
s . Sections . Add ( p . Section ( ) , WeightedPage { s . Pages [ i ] . Weight , s . Pages [ i ] } )
2014-01-29 17:50:31 -05:00
}
for k := range s . Sections {
s . Sections [ k ] . Sort ( )
2014-04-18 03:23:13 -04:00
for i , wp := range s . Sections [ k ] {
if i > 0 {
2015-01-18 20:40:34 -05:00
wp . Page . NextInSection = s . Sections [ k ] [ i - 1 ] . Page
2014-04-18 03:23:13 -04:00
}
2015-01-18 20:40:34 -05:00
if i < len ( s . Sections [ k ] ) - 1 {
wp . Page . PrevInSection = s . Sections [ k ] [ i + 1 ] . Page
2014-04-18 03:23:13 -04:00
}
}
2014-01-29 17:50:31 -05:00
}
2013-07-04 11:32:55 -04:00
}
2014-04-08 23:15:57 -04:00
func ( s * Site ) possibleTaxonomies ( ) ( taxonomies [ ] string ) {
2014-01-29 17:50:31 -05:00
for _ , p := range s . Pages {
for k := range p . Params {
2014-10-20 20:15:33 -04:00
if ! helpers . InStringArray ( taxonomies , k ) {
2014-04-08 23:15:57 -04:00
taxonomies = append ( taxonomies , k )
2014-01-29 17:50:31 -05:00
}
}
}
return
2013-08-13 13:46:05 -04:00
}
2015-03-07 06:53:20 -05:00
// RenderAliases renders shell pages that simply have a redirect in the header
2013-08-10 10:35:34 -04:00
func ( s * Site ) RenderAliases ( ) error {
2014-01-29 17:50:31 -05:00
for _ , p := range s . Pages {
for _ , a := range p . Aliases {
plink , err := p . Permalink ( )
if err != nil {
return err
}
2014-11-04 00:39:37 -05:00
if err := s . WriteDestAlias ( a , template . HTML ( plink ) ) ; err != nil {
2014-01-29 17:50:31 -05:00
return err
}
}
}
return nil
2013-08-10 10:35:34 -04:00
}
2015-03-07 06:53:20 -05:00
// RenderPages renders pages each corresponding to a markdown file
2014-09-14 07:23:03 -04:00
func ( s * Site ) RenderPages ( ) error {
2014-09-14 07:01:40 -04:00
results := make ( chan error )
pages := make ( chan * Page )
procs := getGoMaxProcs ( )
wg := & sync . WaitGroup { }
for i := 0 ; i < procs * 4 ; i ++ {
2014-03-05 19:07:39 -05:00
wg . Add ( 1 )
2014-09-14 07:01:40 -04:00
go pageRenderer ( s , pages , results , wg )
}
errs := make ( chan error )
2014-09-14 07:23:03 -04:00
go errorCollator ( results , errs )
2014-01-29 17:50:31 -05:00
2014-09-14 07:01:40 -04:00
for _ , page := range s . Pages {
pages <- page
2014-03-05 19:07:39 -05:00
}
2014-09-14 07:01:40 -04:00
close ( pages )
2014-03-05 19:07:39 -05:00
wg . Wait ( )
2014-09-14 07:01:40 -04:00
close ( results )
2014-09-14 07:23:03 -04:00
err := <- errs
if err != nil {
return fmt . Errorf ( "Error(s) rendering pages: %s" , err )
}
return nil
2014-09-14 07:01:40 -04:00
}
func pageRenderer ( s * Site , pages <- chan * Page , results chan <- error , wg * sync . WaitGroup ) {
defer wg . Done ( )
for p := range pages {
var layouts [ ] string
if ! p . IsRenderable ( ) {
self := "__" + p . TargetPath ( )
_ , err := s . Tmpl . New ( self ) . Parse ( string ( p . Content ) )
if err != nil {
results <- err
continue
}
layouts = append ( layouts , self )
} else {
layouts = append ( layouts , p . Layout ( ) ... )
layouts = append ( layouts , "_default/single.html" )
}
2015-01-30 15:05:05 -05:00
err := s . renderAndWritePage ( "page " + p . FullFilePath ( ) , p . TargetPath ( ) , p , s . appendThemeTemplates ( layouts ) ... )
2014-11-04 00:39:37 -05:00
if err != nil {
results <- err
}
2014-01-29 17:50:31 -05:00
}
2014-09-14 07:01:40 -04:00
}
2014-09-14 07:23:03 -04:00
func errorCollator ( results <- chan error , errs chan <- error ) {
2014-09-14 07:01:40 -04:00
errMsgs := [ ] string { }
for err := range results {
if err != nil {
errMsgs = append ( errMsgs , err . Error ( ) )
}
}
if len ( errMsgs ) == 0 {
errs <- nil
2014-09-14 07:23:03 -04:00
} else {
errs <- errors . New ( strings . Join ( errMsgs , "\n" ) )
2014-09-14 07:01:40 -04:00
}
2014-09-14 07:23:03 -04:00
close ( errs )
2013-07-04 11:32:55 -04:00
}
2014-04-10 08:10:12 -04:00
func ( s * Site ) appendThemeTemplates ( in [ ] string ) [ ] string {
if s . hasTheme ( ) {
out := [ ] string { }
// First place all non internal templates
for _ , t := range in {
2014-11-14 12:14:52 -05:00
if ! strings . HasPrefix ( t , "_internal/" ) {
2014-04-10 08:10:12 -04:00
out = append ( out , t )
}
}
// Then place theme templates with the same names
for _ , t := range in {
2014-11-14 12:14:52 -05:00
if ! strings . HasPrefix ( t , "_internal/" ) {
2014-04-10 08:10:12 -04:00
out = append ( out , "theme/" + t )
}
}
2014-11-14 12:14:52 -05:00
2014-04-10 08:10:12 -04:00
// Lastly place internal templates
for _ , t := range in {
2014-11-14 12:14:52 -05:00
if strings . HasPrefix ( t , "_internal/" ) {
out = append ( out , t )
2014-04-10 08:10:12 -04:00
}
}
return out
}
2015-03-07 06:53:20 -05:00
return in
2014-04-10 08:10:12 -04:00
}
2014-09-14 07:23:03 -04:00
type taxRenderInfo struct {
key string
pages WeightedPages
singular string
plural string
}
2015-03-07 06:53:20 -05:00
// RenderTaxonomiesLists renders the listing pages based on the meta data
2014-04-08 23:15:57 -04:00
// each unique term within a taxonomy will have a page created
2014-09-14 07:23:03 -04:00
func ( s * Site ) RenderTaxonomiesLists ( ) error {
wg := & sync . WaitGroup { }
2014-03-05 19:07:39 -05:00
2014-09-14 07:23:03 -04:00
taxes := make ( chan taxRenderInfo )
results := make ( chan error )
2014-04-07 23:29:35 -04:00
2014-09-14 07:23:03 -04:00
procs := getGoMaxProcs ( )
for i := 0 ; i < procs * 4 ; i ++ {
wg . Add ( 1 )
go taxonomyRenderer ( s , taxes , results , wg )
}
errs := make ( chan error )
go errorCollator ( results , errs )
taxonomies := viper . GetStringMapString ( "Taxonomies" )
for singular , plural := range taxonomies {
for key , pages := range s . Taxonomies [ plural ] {
taxes <- taxRenderInfo { key , pages , singular , plural }
2014-01-29 17:50:31 -05:00
}
}
2014-09-14 07:23:03 -04:00
close ( taxes )
2014-03-05 19:07:39 -05:00
wg . Wait ( )
2014-09-14 07:23:03 -04:00
close ( results )
err := <- errs
if err != nil {
return fmt . Errorf ( "Error(s) rendering taxonomies: %s" , err )
}
2014-01-29 17:50:31 -05:00
return nil
2013-07-04 11:32:55 -04:00
}
2014-10-18 14:25:10 -04:00
func ( s * Site ) newTaxonomyNode ( t taxRenderInfo ) ( * Node , string ) {
base := t . plural + "/" + t . key
n := s . NewNode ( )
n . Title = strings . Replace ( strings . Title ( t . key ) , "-" , " " , - 1 )
2015-03-18 01:16:54 -04:00
s . setURLs ( n , base )
2014-10-18 14:25:10 -04:00
if len ( t . pages ) > 0 {
n . Date = t . pages [ 0 ] . Page . Date
}
n . Data [ t . singular ] = t . pages
2014-11-01 23:44:21 -04:00
n . Data [ "Singular" ] = t . singular
n . Data [ "Plural" ] = t . plural
2014-10-18 14:25:10 -04:00
n . Data [ "Pages" ] = t . pages . Pages ( )
return n , base
}
2014-09-14 07:23:03 -04:00
func taxonomyRenderer ( s * Site , taxes <- chan taxRenderInfo , results chan <- error , wg * sync . WaitGroup ) {
defer wg . Done ( )
2014-12-27 08:11:19 -05:00
var n * Node
2014-09-14 07:23:03 -04:00
for t := range taxes {
2014-12-27 08:11:19 -05:00
var base string
layouts := s . appendThemeTemplates (
[ ] string { "taxonomy/" + t . singular + ".html" , "indexes/" + t . singular + ".html" , "_default/taxonomy.html" , "_default/list.html" } )
n , base = s . newTaxonomyNode ( t )
2015-01-30 15:05:05 -05:00
if err := s . renderAndWritePage ( "taxononomy " + t . singular , base , n , layouts ... ) ; err != nil {
2014-12-27 08:11:19 -05:00
results <- err
continue
}
if n . paginator != nil {
paginatePath := viper . GetString ( "paginatePath" )
// write alias for page 1
s . WriteDestAlias ( fmt . Sprintf ( "%s/%s/%d/index.html" , base , paginatePath , 1 ) , s . permalink ( base ) )
pagers := n . paginator . Pagers ( )
for i , pager := range pagers {
if i == 0 {
// already created
continue
}
taxonomyPagerNode , _ := s . newTaxonomyNode ( t )
taxonomyPagerNode . paginator = pager
if pager . TotalPages ( ) > 0 {
taxonomyPagerNode . Date = pager . Pages ( ) [ 0 ] . Date
}
pageNumber := i + 1
htmlBase := fmt . Sprintf ( "/%s/%s/%d" , base , paginatePath , pageNumber )
2015-03-31 16:33:24 -04:00
if err := s . renderAndWritePage ( fmt . Sprintf ( "taxononomy %s" , t . singular ) , htmlBase , taxonomyPagerNode , layouts ... ) ; err != nil {
2014-12-27 08:11:19 -05:00
results <- err
continue
}
2014-11-04 00:39:37 -05:00
}
2014-09-14 07:23:03 -04:00
}
if ! viper . GetBool ( "DisableRSS" ) {
// XML Feed
2015-03-18 01:16:54 -04:00
n . URL = s . permalinkStr ( base + "/index.xml" )
2014-11-16 09:41:07 -05:00
n . Permalink = s . permalink ( base )
2014-09-14 07:23:03 -04:00
rssLayouts := [ ] string { "taxonomy/" + t . singular + ".rss.xml" , "_default/rss.xml" , "rss.xml" , "_internal/_default/rss.xml" }
2015-01-30 14:51:06 -05:00
if err := s . renderAndWriteXML ( "taxonomy " + t . singular + " rss" , base + "/index.xml" , n , s . appendThemeTemplates ( rssLayouts ) ... ) ; err != nil {
2014-09-14 07:23:03 -04:00
results <- err
continue
}
}
}
}
2015-03-07 06:53:20 -05:00
// RenderListsOfTaxonomyTerms renders a page per taxonomy that lists the terms for that taxonomy
2014-04-08 23:15:57 -04:00
func ( s * Site ) RenderListsOfTaxonomyTerms ( ) ( err error ) {
2014-04-10 08:10:12 -04:00
taxonomies := viper . GetStringMapString ( "Taxonomies" )
for singular , plural := range taxonomies {
n := s . NewNode ( )
n . Title = strings . Title ( plural )
2015-03-18 01:16:54 -04:00
s . setURLs ( n , plural )
2014-04-10 08:10:12 -04:00
n . Data [ "Singular" ] = singular
n . Data [ "Plural" ] = plural
n . Data [ "Terms" ] = s . Taxonomies [ plural ]
// keep the following just for legacy reasons
n . Data [ "OrderedIndex" ] = n . Data [ "Terms" ]
n . Data [ "Index" ] = n . Data [ "Terms" ]
layouts := [ ] string { "taxonomy/" + singular + ".terms.html" , "_default/terms.html" , "indexes/indexes.html" }
layouts = s . appendThemeTemplates ( layouts )
if s . layoutExists ( layouts ... ) {
2015-01-30 15:05:05 -05:00
if err := s . renderAndWritePage ( "taxonomy terms for " + singular , plural + "/index.html" , n , layouts ... ) ; err != nil {
2014-11-04 00:39:37 -05:00
return err
}
2014-01-29 17:50:31 -05:00
}
}
2014-04-10 08:10:12 -04:00
2014-01-29 17:50:31 -05:00
return
2013-08-03 03:09:28 -04:00
}
2014-12-27 08:11:19 -05:00
func ( s * Site ) newSectionListNode ( section string , data WeightedPages ) * Node {
n := s . NewNode ( )
if viper . GetBool ( "PluralizeListTitles" ) {
n . Title = strings . Title ( inflect . Pluralize ( section ) )
} else {
n . Title = strings . Title ( section )
}
2015-03-18 01:16:54 -04:00
s . setURLs ( n , section )
2014-12-27 08:11:19 -05:00
n . Date = data [ 0 ] . Page . Date
n . Data [ "Pages" ] = data . Pages ( )
return n
}
2015-03-07 06:53:20 -05:00
// RenderSectionLists renders a page for each section
2014-04-08 23:15:57 -04:00
func ( s * Site ) RenderSectionLists ( ) error {
2014-01-29 17:50:31 -05:00
for section , data := range s . Sections {
2014-12-27 08:11:19 -05:00
layouts := s . appendThemeTemplates (
[ ] string { "section/" + section + ".html" , "_default/section.html" , "_default/list.html" , "indexes/" + section + ".html" , "_default/indexes.html" } )
n := s . newSectionListNode ( section , data )
2015-03-31 16:33:24 -04:00
if err := s . renderAndWritePage ( fmt . Sprintf ( "section %s" , section ) , fmt . Sprintf ( "/%s" , section ) , n , s . appendThemeTemplates ( layouts ) ... ) ; err != nil {
2014-11-04 00:39:37 -05:00
return err
}
2014-01-29 17:50:31 -05:00
2014-12-27 08:11:19 -05:00
if n . paginator != nil {
paginatePath := viper . GetString ( "paginatePath" )
// write alias for page 1
s . WriteDestAlias ( filepath . FromSlash ( fmt . Sprintf ( "/%s/%s/%d" , section , paginatePath , 1 ) ) , s . permalink ( section ) )
pagers := n . paginator . Pagers ( )
for i , pager := range pagers {
if i == 0 {
// already created
continue
}
sectionPagerNode := s . newSectionListNode ( section , data )
sectionPagerNode . paginator = pager
if pager . TotalPages ( ) > 0 {
sectionPagerNode . Date = pager . Pages ( ) [ 0 ] . Date
}
pageNumber := i + 1
htmlBase := fmt . Sprintf ( "/%s/%s/%d" , section , paginatePath , pageNumber )
2015-03-31 16:33:24 -04:00
if err := s . renderAndWritePage ( fmt . Sprintf ( "section %s" , section ) , filepath . FromSlash ( htmlBase ) , sectionPagerNode , layouts ... ) ; err != nil {
2014-12-27 08:11:19 -05:00
return err
}
}
}
2014-11-16 09:41:07 -05:00
if ! viper . GetBool ( "DisableRSS" ) && section != "" {
2014-01-29 17:50:31 -05:00
// XML Feed
2015-03-18 01:16:54 -04:00
n . URL = s . permalinkStr ( section + "/index.xml" )
2014-11-16 09:41:07 -05:00
n . Permalink = s . permalink ( section )
2014-04-10 08:10:12 -04:00
rssLayouts := [ ] string { "section/" + section + ".rss.xml" , "_default/rss.xml" , "rss.xml" , "_internal/_default/rss.xml" }
2015-01-30 14:51:06 -05:00
if err := s . renderAndWriteXML ( "section " + section + " rss" , section + "/index.xml" , n , s . appendThemeTemplates ( rssLayouts ) ... ) ; err != nil {
2014-11-04 00:39:37 -05:00
return err
}
2014-01-29 17:50:31 -05:00
}
}
return nil
2013-07-04 11:32:55 -04:00
}
2014-10-18 14:25:10 -04:00
func ( s * Site ) newHomeNode ( ) * Node {
2014-01-29 17:50:31 -05:00
n := s . NewNode ( )
n . Title = n . Site . Title
2015-03-18 01:16:54 -04:00
s . setURLs ( n , "/" )
2014-01-29 17:50:31 -05:00
n . Data [ "Pages" ] = s . Pages
2014-10-18 14:25:10 -04:00
return n
}
func ( s * Site ) RenderHomePage ( ) error {
n := s . newHomeNode ( )
2014-12-27 08:11:19 -05:00
layouts := s . appendThemeTemplates ( [ ] string { "index.html" , "_default/list.html" , "_default/single.html" } )
2015-03-07 09:41:01 -05:00
if err := s . renderAndWritePage ( "homepage" , helpers . FilePathSeparator , n , layouts ... ) ; err != nil {
2014-11-04 00:39:37 -05:00
return err
}
2014-01-29 17:50:31 -05:00
2014-12-27 08:11:19 -05:00
if n . paginator != nil {
paginatePath := viper . GetString ( "paginatePath" )
// write alias for page 1
s . WriteDestAlias ( filepath . FromSlash ( fmt . Sprintf ( "/%s/%d" , paginatePath , 1 ) ) , s . permalink ( "/" ) )
pagers := n . paginator . Pagers ( )
for i , pager := range pagers {
if i == 0 {
// already created
continue
}
homePagerNode := s . newHomeNode ( )
homePagerNode . paginator = pager
if pager . TotalPages ( ) > 0 {
homePagerNode . Date = pager . Pages ( ) [ 0 ] . Date
}
pageNumber := i + 1
htmlBase := fmt . Sprintf ( "/%s/%d" , paginatePath , pageNumber )
2015-03-31 16:33:24 -04:00
if err := s . renderAndWritePage ( fmt . Sprintf ( "homepage" ) , filepath . FromSlash ( htmlBase ) , homePagerNode , layouts ... ) ; err != nil {
2014-12-27 08:11:19 -05:00
return err
}
}
}
2014-04-09 17:45:34 -04:00
if ! viper . GetBool ( "DisableRSS" ) {
2014-01-29 17:50:31 -05:00
// XML Feed
2015-04-24 14:25:09 -04:00
n . URL = s . permalinkStr ( viper . GetString ( "RSSUri" ) )
2014-11-16 09:41:07 -05:00
n . Title = ""
2014-01-29 17:50:31 -05:00
high := 50
if len ( s . Pages ) < high {
high = len ( s . Pages )
}
n . Data [ "Pages" ] = s . Pages [ : high ]
if len ( s . Pages ) > 0 {
n . Date = s . Pages [ 0 ] . Date
}
2014-04-10 08:10:12 -04:00
2014-11-16 09:41:07 -05:00
rssLayouts := [ ] string { "rss.xml" , "_default/rss.xml" , "_internal/_default/rss.xml" }
2015-01-30 14:51:06 -05:00
2015-04-24 14:25:09 -04:00
if err := s . renderAndWriteXML ( "homepage rss" , viper . GetString ( "RSSUri" ) , n , s . appendThemeTemplates ( rssLayouts ) ... ) ; err != nil {
2014-11-16 09:41:07 -05:00
return err
2014-08-20 22:57:51 -04:00
}
}
2015-03-18 01:16:54 -04:00
n . URL = helpers . URLize ( "404.html" )
2014-06-26 15:41:29 -04:00
n . Title = "404 Page not found"
n . Permalink = s . permalink ( "404.html" )
nfLayouts := [ ] string { "404.html" }
2015-01-30 15:05:05 -05:00
if nfErr := s . renderAndWritePage ( "404 page" , "404.html" , n , s . appendThemeTemplates ( nfLayouts ) ... ) ; nfErr != nil {
2014-06-26 15:41:29 -04:00
return nfErr
2014-01-29 17:50:31 -05:00
}
return nil
2013-07-04 11:32:55 -04:00
}
2014-05-06 06:50:23 -04:00
func ( s * Site ) RenderSitemap ( ) error {
if viper . GetBool ( "DisableSitemap" ) {
return nil
}
2014-05-06 11:02:56 -04:00
sitemapDefault := parseSitemap ( viper . GetStringMap ( "Sitemap" ) )
2014-05-06 06:50:23 -04:00
n := s . NewNode ( )
2014-05-06 11:02:56 -04:00
// Prepend homepage to the list of pages
pages := make ( Pages , 0 )
page := & Page { }
2014-05-10 04:45:33 -04:00
page . Date = s . Info . LastChange
2014-05-28 19:11:54 -04:00
page . Site = & s . Info
2015-03-18 01:16:54 -04:00
page . URL = "/"
2014-05-06 11:02:56 -04:00
pages = append ( pages , page )
pages = append ( pages , s . Pages ... )
n . Data [ "Pages" ] = pages
for _ , page := range pages {
if page . Sitemap . ChangeFreq == "" {
page . Sitemap . ChangeFreq = sitemapDefault . ChangeFreq
}
if page . Sitemap . Priority == - 1 {
page . Sitemap . Priority = sitemapDefault . Priority
}
}
2014-05-06 06:50:23 -04:00
smLayouts := [ ] string { "sitemap.xml" , "_default/sitemap.xml" , "_internal/_default/sitemap.xml" }
2015-01-30 14:51:06 -05:00
if err := s . renderAndWriteXML ( "sitemap" , "sitemap.xml" , n , s . appendThemeTemplates ( smLayouts ) ... ) ; err != nil {
2014-11-04 00:39:37 -05:00
return err
}
2014-05-06 06:50:23 -04:00
return nil
}
2013-07-04 11:32:55 -04:00
func ( s * Site ) Stats ( ) {
2014-08-20 11:09:35 -04:00
jww . FEEDBACK . Println ( s . draftStats ( ) )
jww . FEEDBACK . Println ( s . futureStats ( ) )
2015-03-12 10:46:42 -04:00
jww . FEEDBACK . Printf ( "%d pages created\n" , len ( s . Pages ) )
jww . FEEDBACK . Printf ( "%d paginator pages created\n" , s . Info . paginationPageCount )
2014-04-08 23:15:57 -04:00
taxonomies := viper . GetStringMapString ( "Taxonomies" )
2014-04-05 01:26:43 -04:00
2014-04-08 23:15:57 -04:00
for _ , pl := range taxonomies {
jww . FEEDBACK . Printf ( "%d %s created\n" , len ( s . Taxonomies [ pl ] ) , pl )
2014-01-29 17:50:31 -05:00
}
2013-07-04 11:32:55 -04:00
}
2015-03-18 01:16:54 -04:00
func ( s * Site ) setURLs ( n * Node , in string ) {
n . URL = helpers . URLizeAndPrep ( in )
n . Permalink = s . permalink ( n . URL )
2014-02-27 18:32:09 -05:00
n . RSSLink = s . permalink ( in + ".xml" )
}
2013-09-25 00:24:49 -04:00
2014-02-27 18:32:09 -05:00
func ( s * Site ) permalink ( plink string ) template . HTML {
2014-10-18 14:25:10 -04:00
return template . HTML ( s . permalinkStr ( plink ) )
}
func ( s * Site ) permalinkStr ( plink string ) string {
2015-03-11 13:34:57 -04:00
return helpers . MakePermalink ( string ( viper . GetString ( "BaseURL" ) ) , helpers . URLizeAndPrep ( plink ) ) . String ( )
2013-08-31 20:47:21 -04:00
}
func ( s * Site ) NewNode ( ) * Node {
2014-01-29 17:50:31 -05:00
return & Node {
Data : make ( map [ string ] interface { } ) ,
2014-05-28 19:11:54 -04:00
Site : & s . Info ,
2014-01-29 17:50:31 -05:00
}
2013-07-04 11:32:55 -04:00
}
2014-04-08 23:15:57 -04:00
func ( s * Site ) layoutExists ( layouts ... string ) bool {
_ , found := s . findFirstLayout ( layouts ... )
return found
}
2015-01-30 14:51:06 -05:00
func ( s * Site ) renderAndWriteXML ( name string , dest string , d interface { } , layouts ... string ) error {
renderBuffer := bp . GetBuffer ( )
defer bp . PutBuffer ( renderBuffer )
renderBuffer . WriteString ( "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n" )
2014-12-18 14:59:39 -05:00
2015-01-30 14:51:06 -05:00
err := s . render ( name , d , renderBuffer , layouts ... )
2014-12-18 14:59:39 -05:00
2015-03-18 14:42:46 -04:00
absURLInXML , err := transform . AbsURLInXML ( )
2014-12-18 14:59:39 -05:00
if err != nil {
2015-01-30 14:51:06 -05:00
return err
2014-12-18 14:59:39 -05:00
}
2015-01-30 14:51:06 -05:00
outBuffer := bp . GetBuffer ( )
defer bp . PutBuffer ( outBuffer )
2014-12-18 14:59:39 -05:00
transformer := transform . NewChain ( absURLInXML ... )
transformer . Apply ( outBuffer , renderBuffer )
2015-01-30 14:51:06 -05:00
if err == nil {
err = s . WriteDestFile ( dest , outBuffer )
}
return err
2014-11-04 00:39:37 -05:00
}
2013-10-01 22:58:15 -04:00
2015-01-30 15:05:05 -05:00
func ( s * Site ) renderAndWritePage ( name string , dest string , d interface { } , layouts ... string ) error {
renderBuffer := bp . GetBuffer ( )
defer bp . PutBuffer ( renderBuffer )
2014-11-04 00:39:37 -05:00
err := s . render ( name , d , renderBuffer , layouts ... )
2015-01-30 15:05:05 -05:00
outBuffer := bp . GetBuffer ( )
defer bp . PutBuffer ( outBuffer )
2014-01-29 17:50:31 -05:00
transformLinks := transform . NewEmptyTransforms ( )
2015-03-11 13:34:57 -04:00
if viper . GetBool ( "CanonifyURLs" ) {
2015-03-18 14:42:46 -04:00
absURL , err := transform . AbsURL ( )
2014-01-29 17:50:31 -05:00
if err != nil {
2015-01-30 15:05:05 -05:00
return err
2014-01-29 17:50:31 -05:00
}
transformLinks = append ( transformLinks , absURL ... )
}
2014-05-16 17:49:27 -04:00
if viper . GetBool ( "watch" ) && ! viper . GetBool ( "DisableLiveReload" ) {
transformLinks = append ( transformLinks , transform . LiveReloadInject )
}
2014-01-29 17:50:31 -05:00
transformer := transform . NewChain ( transformLinks ... )
2014-11-04 00:39:37 -05:00
transformer . Apply ( outBuffer , renderBuffer )
2015-01-30 15:05:05 -05:00
if err == nil {
if err = s . WriteDestPage ( dest , outBuffer ) ; err != nil {
return err
}
}
return err
2014-11-04 00:39:37 -05:00
}
2014-01-29 17:50:31 -05:00
2014-11-04 00:39:37 -05:00
func ( s * Site ) render ( name string , d interface { } , renderBuffer * bytes . Buffer , layouts ... string ) error {
layout , found := s . findFirstLayout ( layouts ... )
if found == false {
jww . WARN . Printf ( "Unable to locate layout for %s: %s\n" , name , layouts )
return nil
2014-01-29 17:50:31 -05:00
}
2014-11-04 00:39:37 -05:00
if err := s . renderThing ( d , layout , renderBuffer ) ; err != nil {
2014-01-29 17:50:31 -05:00
// Behavior here should be dependent on if running in server or watch mode.
2015-03-31 16:33:24 -04:00
distinctErrorLogger . Printf ( "Error while rendering %s: %v" , name , err )
2014-01-29 17:50:31 -05:00
if ! s . Running ( ) {
os . Exit ( - 1 )
}
}
2014-11-04 00:39:37 -05:00
return nil
2013-10-01 22:58:15 -04:00
}
2014-04-08 23:15:57 -04:00
func ( s * Site ) findFirstLayout ( layouts ... string ) ( string , bool ) {
for _ , layout := range layouts {
2014-01-29 17:50:31 -05:00
if s . Tmpl . Lookup ( layout ) != nil {
2014-04-08 23:15:57 -04:00
return layout , true
2014-01-29 17:50:31 -05:00
}
}
2014-04-08 23:15:57 -04:00
return "" , false
2013-07-04 11:32:55 -04:00
}
2013-11-05 00:28:08 -05:00
func ( s * Site ) renderThing ( d interface { } , layout string , w io . Writer ) error {
2014-01-29 17:50:31 -05:00
// If the template doesn't exist, then return, but leave the Writer open
if s . Tmpl . Lookup ( layout ) == nil {
return fmt . Errorf ( "Layout not found: %s" , layout )
}
return s . Tmpl . ExecuteTemplate ( w , layout , d )
2013-08-27 05:09:50 -04:00
}
2013-11-05 00:28:08 -05:00
func ( s * Site ) NewXMLBuffer ( ) * bytes . Buffer {
2014-01-29 17:50:31 -05:00
header := "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n"
return bytes . NewBufferString ( header )
2013-07-04 11:32:55 -04:00
}
2014-11-04 00:39:37 -05:00
func ( s * Site ) PageTarget ( ) target . Output {
2015-02-20 12:38:35 -05:00
s . initTargetList ( )
2014-11-04 00:39:37 -05:00
return s . Targets . Page
2013-09-03 23:52:50 -04:00
}
2014-11-04 00:39:37 -05:00
func ( s * Site ) FileTarget ( ) target . Output {
2015-02-20 12:38:35 -05:00
s . initTargetList ( )
2014-11-04 00:39:37 -05:00
return s . Targets . File
2013-07-04 11:32:55 -04:00
}
2013-09-12 19:17:53 -04:00
2014-11-04 00:39:37 -05:00
func ( s * Site ) AliasTarget ( ) target . AliasPublisher {
2015-02-20 12:38:35 -05:00
s . initTargetList ( )
2014-11-04 00:39:37 -05:00
return s . Targets . Alias
}
2014-01-29 17:50:31 -05:00
2015-02-20 12:38:35 -05:00
func ( s * Site ) initTargetList ( ) {
s . targetListInit . Do ( func ( ) {
if s . Targets . Page == nil {
s . Targets . Page = & target . PagePub {
PublishDir : s . absPublishDir ( ) ,
2015-03-11 13:34:57 -04:00
UglyURLs : viper . GetBool ( "UglyURLs" ) ,
2015-02-20 12:38:35 -05:00
}
}
if s . Targets . File == nil {
s . Targets . File = & target . Filesystem {
PublishDir : s . absPublishDir ( ) ,
}
}
if s . Targets . Alias == nil {
s . Targets . Alias = & target . HTMLRedirectAlias {
PublishDir : s . absPublishDir ( ) ,
}
}
} )
}
2014-11-04 00:39:37 -05:00
func ( s * Site ) WriteDestFile ( path string , reader io . Reader ) ( err error ) {
jww . DEBUG . Println ( "creating file:" , path )
return s . FileTarget ( ) . Publish ( path , reader )
}
func ( s * Site ) WriteDestPage ( path string , reader io . Reader ) ( err error ) {
jww . DEBUG . Println ( "creating page:" , path )
return s . PageTarget ( ) . Publish ( path , reader )
}
2014-01-29 17:50:31 -05:00
2014-11-04 00:39:37 -05:00
func ( s * Site ) WriteDestAlias ( path string , permalink template . HTML ) ( err error ) {
jww . DEBUG . Println ( "alias created at:" , path )
return s . AliasTarget ( ) . Publish ( path , permalink )
2013-09-12 19:17:53 -04:00
}
2014-08-20 11:09:35 -04:00
func ( s * Site ) draftStats ( ) string {
var msg string
switch s . draftCount {
case 0 :
2015-03-12 10:46:42 -04:00
return "0 draft content"
2014-08-20 11:09:35 -04:00
case 1 :
2015-03-12 10:46:42 -04:00
msg = "1 draft rendered"
2014-08-20 11:09:35 -04:00
default :
msg = fmt . Sprintf ( "%d drafts rendered" , s . draftCount )
}
if viper . GetBool ( "BuildDrafts" ) {
return fmt . Sprintf ( "%d of " , s . draftCount ) + msg
2014-08-25 13:13:11 -04:00
}
2014-08-20 11:09:35 -04:00
return "0 of " + msg
}
func ( s * Site ) futureStats ( ) string {
2014-08-25 13:13:11 -04:00
var msg string
switch s . futureCount {
case 0 :
return "0 future content "
case 1 :
msg = "1 future rendered "
default :
msg = fmt . Sprintf ( "%d future rendered" , s . draftCount )
}
if viper . GetBool ( "BuildFuture" ) {
return fmt . Sprintf ( "%d of " , s . futureCount ) + msg
}
return "0 of " + msg
2014-08-20 11:09:35 -04:00
}
2014-08-29 13:40:21 -04:00
func getGoMaxProcs ( ) int {
if gmp := os . Getenv ( "GOMAXPROCS" ) ; gmp != "" {
if p , err := strconv . Atoi ( gmp ) ; err != nil {
return p
}
}
return 1
}