Featured image of post Configuring PagesCMS to Match HUGO's Directory Structure

Configuring PagesCMS to Match HUGO's Directory Structure

In the previous article Pages CMS: A Barely Adequate Backend for HUGO Blog, I mentioned that Pages CMS can be used as a backend management panel for Hugo articles. However, there is an issue that is not easy to solve, which is that Hugo’s default directory structure is difficult to adapt to Pages CMS. This is not a unique problem for Pages CMS; any other CMS might encounter this issue when adapting to Hugo. But with the strong support of the Hugo community, I eventually solved this problem.


Problem Manifestation

What Kind of Image Directory Structure Does Hugo Require

The directory structure of Hugo is generally in the form below, where the index.md file and image files are stored in the same folder. When referencing images in index.md, you only need to enter ![a](a.jpg), without needing to input the directory structure of the image. Unless there is a subdirectory within article-1 as shown in the code below, then you would need to reference the subdirectory path in the image.

1
2
3
4
5
6
7
8
9
content/
├── post/
│   ├── article-1/
│   │   ├── a.jpg
│   │   └── index.md
│   └── article-2/
│       ├── b.jpg
│       └── index.md
└── _index.md

What Kind of Image Directory Structure Can CMS Provide

The image directory structure of CMS is generally in the form below, usually with only one entry and exit for centralized image management. In CMS, when uploading or inserting images, the default link (set according to the code below) is content/post/article-1/a.jpg. After using markdown syntax, it becomes ![a](content/post/article-1/a.jpg). Once the image link is manually set to ![a](a.jpg), an error will occur. However, the link format ![a](content/post/article-1/a.jpg) will be considered by Hugo as the image being saved in a subdirectory of the directory where index.md is located, i.e., content/post/article-1/content/post/article-1/a.jpg, which is obviously an incorrect path. The conflict arises from this.

1
2
3
media:
  input: content/post
  output: /content/post

Cause Analysis

Admittedly, this seems like a small issue. However, solving this problem is not easy. In CMS, if you want the program to directly associate the link structure ![a](a.jpg) with the image in the directory where index.md is located, it requires a lot of effort. Because most people use CMS to manage a unified image folder, using absolute addresses for images, it is difficult to make a separate solution for Hugo, and it may cause other conflicts. For example, the situation where there is a subdirectory for image files under the directory where index.md is located.

Thus, the pressure is on Hugo.

During local testing, it was found that ![a](content/post/article-1/a.jpg) was converted by Hugo to https://localhost/content/post/article-1/a.jpg, where /content/post/ comes from the CMS’s image output settings, and /article-1/ is the name of the folder where index.md is located.

Meanwhile, ![a](a.jpg) was converted by Hugo to https://localhost/article/slug/a.jpg, where /article/ is output from Hugo.yaml’s permalinks: { post: /article/:slug/ }, and /slug/ is manually set in front-matter.

At this point, the solution to the problem gradually became clear.


Preliminary Solution

  1. Remove the /content/ part from the CMS image output path, so the CMS output image path becomes ![a](post/article-1/a.jpg).

  2. Remove permalinks: { post: /article/:slug/ } from Hugo.yaml, and delete the manually set slug in the test index.md, so the fixed link will use the folder name by default.

Thus, ![a](content/post/article-1/a.jpg) in CMS is converted by Hugo to https://localhost/post/article-1/a.jpg, and the manually entered ![a](a.jpg) is also converted by Hugo to https://localhost/post/article-1/a.jpg. The problem is basically solved.


Advanced Solution

Since the image path in CMS is the absolute address after Hugo generates the static site, it means CMS has predicted in advance that there will be a file at this address, so no error will occur in the webpage.

However, this method is still not quite enough for many complex Hugo theme templates. Because Hugo does not process the image file at this address at all, and the theme template will not preset CSS for the image file at this address. In the entire process, this is actually equivalent to a third-party image.

Therefore, the actual problem to be solved next is: How to import ![a](content/post/article-1/a.jpg) into Hugo’s image rendering program. Since I raised the previous directory structure issue on the Hugo official forum, a technical expert pointed out that I could use Hugo’s image hooks to ensure that the image at this path is rendered.

But this setting is not simple.

I tried many methods to define image hooks. For example, judging whether the image Markdown syntax ![a](content/post/article-1/a.jpg) contains the / slash symbol, whether it is an absolute address, or changing the Resources.GetMatch function, etc. But none of these were successful.

Finally, I chose the most old-fashioned method, directly extracting the image file name from ![a](content/post/article-1/a.jpg), ignoring the content/post/article-1 path part, and importing it into Hugo rendering as long as a.jpg is matched. But this has a cost, as mentioned before, the image can no longer be placed in a subdirectory within article-1, it must be directly placed in article-1.


At this point, I think the title of the previous blog post can be modified. Pages CMS is no longer a barely adequate backend for Hugo blogs, but a backend with full capabilities, a very well-designed CMS software.

Example: Image Hook Settings in the Hugo-theme-stack Theme

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{{- $destination := .Destination | safeURL -}}  
{{- $filename := path.Base $destination -}}  // Add a variable to extract the file name
{{- $image := .Page.Resources.GetMatch $filename -}}  // Still use the Resources.GetMatch function to match image resources, but replace it with the $filename variable instead of the previous pure path matching

{{- if $image -}}
  {{- $permalink := $image.RelPermalink | safeURL -}}  // Assign $image.RelPermalink to the $permalink variable
  {{- $alt := .PlainText | safeHTML -}}  
  {{- $width := $image.Width -}}  // Assign $image.Width to the $width variable
  {{- $height := $image.Height -}}  // Assign $image.Height to the $height variable
  {{- $srcset := "" -}}  

   {{- $notSVG := ne (path.Ext $filename) ".svg" -}}  // Modify the SVG judgment logic synchronously
  {{- $galleryImage := false -}}  

  {{- if $notSVG -}}
      {{- $width = $image.Width -}}  
      {{- $height = $image.Height -}}  
      {{- $galleryImage = true -}}  

      {{- if (default true .Page.Site.Params.imageProcessing.content.enabled) -}}
          {{- $small := $image.Resize "480x" -}}  
          {{- $big := $image.Resize "1024x" -}}  
          {{- $srcset = printf "%s 480w, %s 1024w" $small.RelPermalink $big.RelPermalink -}}  
      {{- end -}}
  {{- end -}}

  <img src="{{ $permalink }}"  
       {{ with $width }}width="{{ . }}"{{ end }}  
       {{ with $height }}height="{{ . }}"{{ end }} 
       {{ with $srcset }}srcset="{{ . }}"{{ end }}  
       loading="lazy"
       {{ with $alt }}alt="{{ . }}"{{ end }}  
       {{ if $galleryImage }}
           class="gallery-image"
           data-flex-grow="{{ div (mul $image.Width 100) $image.Height }}"  
           data-flex-basis="{{ div (mul $image.Width 240) $image.Height }}px"  
       {{ end }}>
{{- else -}}
    <img src="{{ "links/error.webp" | relURL }}" alt="Default Image" loading="lazy">  // Add handling for the case where the image is not found
{{- end -}}

Note: Need to batch move all image files in the images folder within the folder where index.md is located one level up to be at the same level as index.md

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash

# Define the path to the content directory
CONTENT_DIR="D:/Hugo/content/posts"

find "$CONTENT_DIR" -type d -name "images" | while read -r IMAGES_DIR; do
   
    PARENT_DIR=$(dirname "$IMAGES_DIR")
    
       mv "$IMAGES_DIR"/* "$PARENT_DIR"/
    
    # Delete empty folders
    rmdir "$IMAGES_DIR"
done
All textual works on this website are protected by copyright, and the authors reserve all rights. The photos on this website, unless specifically stated, licensed under the CC BY-NC-ND 4.0 license.
Built with Hugo & Stack, Powered by Github.
Total Posts: 81, Total Words: 75857.
本站已加入BLOGS·CN