Compare commits
6 Commits
edge-2023-
...
master
Author | SHA1 | Date | |
---|---|---|---|
227a8a308a | |||
1c0626aaed | |||
0de79f2cf1 | |||
d676d742c8 | |||
759e04dc25 | |||
d41851cfbc |
1
base.css
1
base.css
@ -6,6 +6,7 @@ Under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 Internati
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-family: "Times New Roman", Times, serif;
|
||||||
}
|
}
|
||||||
#st{
|
#st{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
19
conf/page.go
Normal file
19
conf/page.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type PageYaml struct {
|
||||||
|
PageName string `yaml:"pageName"`
|
||||||
|
PagePath string `yaml:"pagePath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (py PageYaml) GetPagePath() string {
|
||||||
|
toReturn := py.PagePath
|
||||||
|
if !strings.HasSuffix(toReturn, ".go") {
|
||||||
|
toReturn += ".go"
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(toReturn, "/") {
|
||||||
|
toReturn = "/" + toReturn
|
||||||
|
}
|
||||||
|
return toReturn
|
||||||
|
}
|
@ -9,10 +9,13 @@ import (
|
|||||||
|
|
||||||
type ServeYaml struct {
|
type ServeYaml struct {
|
||||||
DataStorage string `yaml:"dataStorage"`
|
DataStorage string `yaml:"dataStorage"`
|
||||||
|
TemplateStorage string `yaml:"templateStorage"`
|
||||||
Domains []string `yaml:"domains"`
|
Domains []string `yaml:"domains"`
|
||||||
RangeSupported bool `yaml:"rangeSupported"`
|
RangeSupported bool `yaml:"rangeSupported"`
|
||||||
EnableGoInfoPage bool `yaml:"enableGoInfoPage"`
|
EnableGoInfoPage bool `yaml:"enableGoInfoPage"`
|
||||||
CacheSettings CacheSettingsYaml `yaml:"cacheSettings"`
|
CacheSettings CacheSettingsYaml `yaml:"cacheSettings"`
|
||||||
|
PageSettings []PageYaml `yaml:"pageSettings"`
|
||||||
|
YmlDataFallback bool `yaml:"ymlDataFallback"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sy ServeYaml) GetDomainString() string {
|
func (sy ServeYaml) GetDomainString() string {
|
||||||
@ -39,3 +42,20 @@ func (sy ServeYaml) GetDataStoragePath() string {
|
|||||||
return sy.DataStorage
|
return sy.DataStorage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sy ServeYaml) GetTemplateStoragePath() string {
|
||||||
|
if sy.TemplateStorage == "" || !filepath.IsAbs(sy.TemplateStorage) {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
if sy.TemplateStorage == "" {
|
||||||
|
return wd
|
||||||
|
} else {
|
||||||
|
return path.Join(wd, sy.TemplateStorage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return sy.TemplateStorage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,9 +4,13 @@ listen:
|
|||||||
webMethod: "http"
|
webMethod: "http"
|
||||||
identify: true
|
identify: true
|
||||||
serve:
|
serve:
|
||||||
|
dataStorage: ""
|
||||||
|
domains: []
|
||||||
rangeSupported: true
|
rangeSupported: true
|
||||||
enableGoInfoPage: true
|
enableGoInfoPage: true
|
||||||
cacheSettings:
|
cacheSettings:
|
||||||
|
enableTemplateCaching: false
|
||||||
|
enableTemplateCachePurge: false
|
||||||
enableContentsCaching: true
|
enableContentsCaching: true
|
||||||
enableContentsCachePurge: true
|
enableContentsCachePurge: true
|
||||||
maxAge: 3600
|
maxAge: 3600
|
||||||
|
@ -150,6 +150,10 @@
|
|||||||
<th>Memory Page Size</th>
|
<th>Memory Page Size</th>
|
||||||
<td>{{ .PageSize }}</td>
|
<td>{{ .PageSize }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>System Time</th>
|
||||||
|
<td>{{ .CurrentTime }}</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<meta name="description" content="Captain ALM's City University Portfolio">
|
<meta name="description" content="Captain ALM's City University Portfolio">
|
||||||
<meta name="keywords" content="CaptainALM Captain_ALM Captain ALM portfolio Alfred Manville projects programming hacking cracking">
|
<meta name="keywords" content="CaptainALM Captain_ALM Captain ALM portfolio Alfred Manville projects programming hacking cracking city uni cityuni cuol City University of London mycityuni">
|
||||||
<title>City University Portfolio</title>
|
<title>City University Portfolio</title>
|
||||||
<link rel="stylesheet" href="{{ .Data.CSSBaseURL }}"/>
|
<link rel="stylesheet" href="{{ .Data.CSSBaseURL }}"/>
|
||||||
{{ if .Light }}
|
{{ if .Light }}
|
||||||
@ -155,10 +155,15 @@
|
|||||||
<div class="item-table-360">
|
<div class="item-table-360">
|
||||||
<div id="video-{{ $c }}">
|
<div id="video-{{ $c }}">
|
||||||
{{ if eq .VideoLocation "" }}
|
{{ if eq .VideoLocation "" }}
|
||||||
<img src="{{ $.Data.NoVideoImageLocation }}" alt="No Video" width="360px">
|
<img src="{{ .GetVideoThumbnail $.Data.NoVideoImageLocation }}" alt="No Video" width="360px">
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
{{ if .IsVideoLink }}
|
||||||
|
<a href="{{ .VideoLocation }}">
|
||||||
|
<img src="{{ .GetVideoThumbnail $.Data.PlayVideoImageLocation }}" alt="Play Video" title="Play" width="360px">
|
||||||
|
</a>
|
||||||
|
{{ else }}
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
CreateVideoPlaceholder({{ $c }})
|
CreateVideoPlaceholder({{ $c }}, "{{ .GetVideoThumbnail $.Data.PlayVideoImageLocation }}")
|
||||||
</script>
|
</script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<video controls width="360px">
|
<video controls width="360px">
|
||||||
@ -166,6 +171,7 @@
|
|||||||
<a href="{{ .VideoLocation }}">The Video</a>
|
<a href="{{ .VideoLocation }}">The Video</a>
|
||||||
</video>
|
</video>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
119
index.go.yml
119
index.go.yml
@ -18,14 +18,14 @@ about:
|
|||||||
title: "Alfred Manville (Captain ALM)"
|
title: "Alfred Manville (Captain ALM)"
|
||||||
content: >
|
content: >
|
||||||
<p>
|
<p>
|
||||||
Hello, I'm Alfred Manville (#age# Years Old).
|
Hello, I'm Alfred Manville (#age# Years Old) and a third year student at City, University of London.
|
||||||
I'm a free and open-source developer who enjoys networking my laptops together,
|
I'm a free and open-source developer who enjoys networking my laptops together,
|
||||||
writes network software to communicate between them and then tries to break said software.
|
writes network software to communicate between them and then tries to break said software.
|
||||||
I also have a <a href="https://youtube.com/c/CaptainALM">Youtube Channel</a> which is in the process of being resumed from a hiatus.
|
I also have a <a href="https://youtube.com/c/CaptainALM">Youtube Channel</a> which is in the process of being resumed from a hiatus.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
On the programming side, I know Visual Basic .net, C# .net, C, Java, Go, Javascript, C++, Python,
|
On the programming side, I know Visual Basic .net, C# .net, C, Java, Go, Javascript, C++, Python, Bash,
|
||||||
Processing and Microsoft Smallbasic (I have also dabbled in Bash/Batch).
|
Haskell, Processing and Microsoft Smallbasic (I have also dabbled in Batch).
|
||||||
I am currently in the progress of writing infrastructure software in Go, in the past, I wrote a <a href="https://github.com/Captain-ALM/CALM-Console">command console</a>
|
I am currently in the progress of writing infrastructure software in Go, in the past, I wrote a <a href="https://github.com/Captain-ALM/CALM-Console">command console</a>
|
||||||
in VB .net for my own pluggable libraries (I created a CMD emulator to get past the school disabling interactive CMD) and some <a href="https://github.com/Captain-ALM/CALMNetLibSamples">
|
in VB .net for my own pluggable libraries (I created a CMD emulator to get past the school disabling interactive CMD) and some <a href="https://github.com/Captain-ALM/CALMNetLibSamples">
|
||||||
network communication applications</a> (Including a peer-to-peer <a href="https://github.com/Captain-ALM/C-ALM-VOIP">VOIP client</a>
|
network communication applications</a> (Including a peer-to-peer <a href="https://github.com/Captain-ALM/C-ALM-VOIP">VOIP client</a>
|
||||||
@ -56,7 +56,7 @@ about:
|
|||||||
Here is my <a href="https://cdn.captainalm.com/download/keys/alfred@captainalm.com.asc">GPG Key</a> for my email address.
|
Here is my <a href="https://cdn.captainalm.com/download/keys/alfred@captainalm.com.asc">GPG Key</a> for my email address.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
My CV is available in the following formats: ( <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2022.docx">DOCX</a> | <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2022.pdf">PDF</a> )
|
My CV is available in the following formats: ( <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2024.docx">DOCX</a> | <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2024.pdf">PDF</a> )
|
||||||
</p>
|
</p>
|
||||||
thumbnailLocation: "resources/assets/imageofyou_t.jpg"
|
thumbnailLocation: "resources/assets/imageofyou_t.jpg"
|
||||||
imageLocation: "resources/assets/imageofyou.jpg"
|
imageLocation: "resources/assets/imageofyou.jpg"
|
||||||
@ -89,6 +89,7 @@ entries:
|
|||||||
endDate: "31/10/2021"
|
endDate: "31/10/2021"
|
||||||
videoLocation: "resources/stream/vid-bootcamp.mp4"
|
videoLocation: "resources/stream/vid-bootcamp.mp4"
|
||||||
videoContentType: "video/mp4"
|
videoContentType: "video/mp4"
|
||||||
|
videoThumbnailLocation: "resources/assets/bootcamp-vid.png"
|
||||||
thumbnailLocations:
|
thumbnailLocations:
|
||||||
- "resources/assets/pic1_t.jpg"
|
- "resources/assets/pic1_t.jpg"
|
||||||
- "resources/assets/pic2_t.jpg"
|
- "resources/assets/pic2_t.jpg"
|
||||||
@ -148,6 +149,7 @@ entries:
|
|||||||
endDate: "08/05/2022"
|
endDate: "08/05/2022"
|
||||||
videoLocation: "resources/stream/vid-ninjaformer-2022.mp4"
|
videoLocation: "resources/stream/vid-ninjaformer-2022.mp4"
|
||||||
videoContentType: "video/mp4"
|
videoContentType: "video/mp4"
|
||||||
|
videoThumbnailLocation: "resources/assets/ninjaformer-vid.png"
|
||||||
thumbnailLocations:
|
thumbnailLocations:
|
||||||
- "resources/assets/pic4_t.jpg"
|
- "resources/assets/pic4_t.jpg"
|
||||||
- "resources/assets/pic5_t.jpg"
|
- "resources/assets/pic5_t.jpg"
|
||||||
@ -188,6 +190,7 @@ entries:
|
|||||||
endDate: "30/01/2022"
|
endDate: "30/01/2022"
|
||||||
videoLocation: "resources/stream/vid-shadowwork.mp4"
|
videoLocation: "resources/stream/vid-shadowwork.mp4"
|
||||||
videoContentType: "video/mp4"
|
videoContentType: "video/mp4"
|
||||||
|
videoThumbnailLocation: "resources/assets/shadowwork-vid.png"
|
||||||
thumbnailLocations:
|
thumbnailLocations:
|
||||||
- "resources/assets/shadowwork-2_t.jpg"
|
- "resources/assets/shadowwork-2_t.jpg"
|
||||||
- "resources/assets/shadowwork-3_t.jpg"
|
- "resources/assets/shadowwork-3_t.jpg"
|
||||||
@ -242,6 +245,7 @@ entries:
|
|||||||
endDate: "10/12/2022"
|
endDate: "10/12/2022"
|
||||||
videoLocation: "resources/stream/vid-pycom.mp4"
|
videoLocation: "resources/stream/vid-pycom.mp4"
|
||||||
videoContentType: "video/mp4"
|
videoContentType: "video/mp4"
|
||||||
|
videoThumbnailLocation: "resources/assets/pycom-vid.png"
|
||||||
thumbnailLocations:
|
thumbnailLocations:
|
||||||
- "resources/assets/pycom-1_t.jpg"
|
- "resources/assets/pycom-1_t.jpg"
|
||||||
- "resources/assets/pycom-2_t.jpg"
|
- "resources/assets/pycom-2_t.jpg"
|
||||||
@ -257,3 +261,110 @@ entries:
|
|||||||
- "File Messaging."
|
- "File Messaging."
|
||||||
- "Exploit Testing."
|
- "Exploit Testing."
|
||||||
- "Failed Exploit Testing."
|
- "Failed Exploit Testing."
|
||||||
|
- name: "Group Project - AirVia ATS (AirTicket Sales)"
|
||||||
|
content: >
|
||||||
|
<p>
|
||||||
|
This group project was creating a Ticket Sales system for the fictional company AirVia LTD, for this the group had to both design the implementation and then write the code for it.
|
||||||
|
Unfortunately, the project was not finished to a state where all the required features were added in and while all of the backend functionality except for the reports existed,
|
||||||
|
the GUIs to view and control those backends was not available.
|
||||||
|
Examples of this include: Sales, Transactions, Discounts and Reports where no GUIs were created or finished for any of these components.
|
||||||
|
The design of the program was followed with a lot of adaptations (Rather than not being followed at all) and it architecturally made sense with the use of facade implementations;
|
||||||
|
The use of facade allowed for other people to code against an interface while waiting for a controller to be finished which extended the interface.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The database system was handled via an abstraction layer I designed and wrote myself which supports 'locking' a record for keeping consistency when multiple instances of the program are running.
|
||||||
|
This system uses an auxiliary table that has only the primary key column, a record is locked if it does not exist in the auxiliary table (Cannot delete) and is not locked if it does (Cannot insert);
|
||||||
|
This allows for atomic locking and unlocking of the record.
|
||||||
|
The implementation requires the record locked for safe access (Loading, Storing).
|
||||||
|
The abstraction layer makes use of two base classes, one for a single record and one for a table; with tha table one allowing the creation and deletion of the extending table via a schema and name being provided (As seen in the source code).
|
||||||
|
I also developed a backup system for the database that supports any table with the specifically supported data types used in the tables defined in the schema - removing the need to use third-party programs like SQLDump.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In the end, the following features were implemented: Login, Help / Error / Status Bar, Account System + GUI, Blank Types + GUI, Blanks + GUI, Customers + GUI, Discounts, Flexible Discounts, Sales, Transactions, Refunds, Dashboard + Notifications, Database Interfacing + Backup and Rates + GUI.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Find the source code here: <a href="https://github.com/karansambee/IN2018-Team-Project/tree/master">https://github.com/karansambee/IN2018-Team-Project/tree/master</a>
|
||||||
|
</p>
|
||||||
|
startDate: "01/02/2023"
|
||||||
|
endDate: "30/04/2023"
|
||||||
|
videoLocation: "resources/stream/vid-groupproject-2023.mp4"
|
||||||
|
videoContentType: "video/mp4"
|
||||||
|
videoThumbnailLocation: "resources/assets/groupproject-vid.png"
|
||||||
|
thumbnailLocations:
|
||||||
|
- "resources/assets/groupproject-1_t.jpg"
|
||||||
|
- "resources/assets/groupproject-2_t.jpg"
|
||||||
|
- "resources/assets/groupproject-3_t.jpg"
|
||||||
|
- "resources/assets/groupproject-4_t.jpg"
|
||||||
|
- "resources/assets/groupproject-5_t.jpg"
|
||||||
|
- "resources/assets/groupproject-6_t.jpg"
|
||||||
|
- "resources/assets/groupproject-7_t.jpg"
|
||||||
|
- "resources/assets/groupproject-8_t.jpg"
|
||||||
|
- "resources/assets/groupproject-9_t.jpg"
|
||||||
|
- "resources/assets/groupproject-10_t.jpg"
|
||||||
|
imageLocations:
|
||||||
|
- "resources/assets/groupproject-1.png"
|
||||||
|
- "resources/assets/groupproject-2.png"
|
||||||
|
- "resources/assets/groupproject-3.png"
|
||||||
|
- "resources/assets/groupproject-4.png"
|
||||||
|
- "resources/assets/groupproject-5.png"
|
||||||
|
- "resources/assets/groupproject-6.png"
|
||||||
|
- "resources/assets/groupproject-7.png"
|
||||||
|
- "resources/assets/groupproject-8.png"
|
||||||
|
- "resources/assets/groupproject-9.png"
|
||||||
|
- "resources/assets/groupproject-10.png"
|
||||||
|
imageAltTexts:
|
||||||
|
- "Logon Interface."
|
||||||
|
- "Administrator Dashboard Interface."
|
||||||
|
- "Blank Modifier."
|
||||||
|
- "Customer Creator."
|
||||||
|
- "Disabling an Account."
|
||||||
|
- "Rate Creator."
|
||||||
|
- "Blank Type Editor."
|
||||||
|
- "Help on Force Unlocking (Database Manager Interface)."
|
||||||
|
- "Account Editor on a Manager within The Dashboard Interface."
|
||||||
|
- "Part of the Main.java source code Screenshot."
|
||||||
|
- name: "City-University Promotional Video"
|
||||||
|
content: >
|
||||||
|
<p>
|
||||||
|
Here, I star in an interview for City, University of London's Promotional Marketing Campaign. Join <a href="https://www.city.ac.uk/">City</a>!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Find the video here: <a href="https://www.youtube.com/watch?v=tOccImgskec">https://www.youtube.com/watch?v=tOccImgskec</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Find the general School of Science and Technology video here: <a href="https://www.youtube.com/watch?v=pkTCf4CWFSY">https://www.youtube.com/watch?v=pkTCf4CWFSY</a>
|
||||||
|
</p>
|
||||||
|
startDate: "05/06/2023"
|
||||||
|
endDate: "05/06/2023"
|
||||||
|
videoLocation: "https://www.youtube.com/watch?v=tOccImgskec"
|
||||||
|
videoContentType: "text/uri-list"
|
||||||
|
videoThumbnailLocation: "resources/assets/citypromo-vid.png"
|
||||||
|
- name: "Decide Quiz - GCloud City"
|
||||||
|
content: >
|
||||||
|
<p>
|
||||||
|
This project was <a href="https://cdn.captainalm.com/download/gcloudcity/CloudApplicationSpecification-AlfredManville-MohammadMasood.docx">designed</a> <a href="https://cdn.captainalm.com/download/gcloudcity/CloudPresentation-AlfredManville-MohammadMasood.pptx">to</a> operate as a Kahoot clone although the front-end never got completed by the other member of the pair (This was written in React).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This was created in a pair as part of the Cloud Computing module at City, University of London. It was built for use using google cloud and therefore
|
||||||
|
uses many Google Cloud Platform Technologies which are listed and shown off in the video.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The communication protocol uses JSON packets passed either through a WebSocket connection or a REST based protocol that polls the server for data while sending the queued packets.
|
||||||
|
The REST connection is used as a fallback system where WebSockets do not work; in addition, a REST connection is made to the master server to first work out which app server has
|
||||||
|
the least load, once found, this is sent back to the client where the client will then attempt a WebSocket connection and, on failure, a REST session is created (A key is returned
|
||||||
|
which is then used as a parameter in subsequent communications). In the event there is no more capacity, a service unavailable error is sent and a new VM will be spun up, in the case
|
||||||
|
that any are left. The source code for this library can be found at <a href="https://github.com/Captain-ALM/gc-c-com">https://github.com/Captain-ALM/gc-c-com</a> and the master server source
|
||||||
|
code can be found at <a href="https://github.com/Captain-ALM/gc-c-master-srv">https://github.com/Captain-ALM/gc-c-master-srv</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The main game loop was supposed to allow user generated quizzes once logged in along-side allowing for public quizzes that could be used and copied by other users.
|
||||||
|
Only logged in users could start games where any user - both logged in or not - could join and answer questions within a set amount of time, with the person answering
|
||||||
|
the fastest getting more points than people who answer later. The system would also have a leaderboard shown at the end of each question; it was also designed to recover
|
||||||
|
from crashes and resume from the last question executed. The source code for the app server can be found at <a href="https://github.com/Captain-ALM/gc-c-app-srv">
|
||||||
|
https://github.com/Captain-ALM/gc-c-app-srv</a> where all this functionality does exist in the backend; the database source code can be found at <a href="https://github.com/Captain-ALM/gc-c-db">https://github.com/Captain-ALM/gc-c-db</a>.
|
||||||
|
</p>
|
||||||
|
startDate: "27/11/2023"
|
||||||
|
endDate: "14/01/2024"
|
||||||
|
videoLocation: "resources/stream/vid-gc-c-v2.mp4"
|
||||||
|
videoContentType: "video/mp4"
|
||||||
|
videoThumbnailLocation: "resources/assets/vid-gc-c-v2.png"
|
||||||
|
4
index.js
4
index.js
@ -26,9 +26,9 @@ function CreateEntry(id, name, videourl, videotype, start, end, duration) {
|
|||||||
duration : parseInt(duration, 10)
|
duration : parseInt(duration, 10)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function CreateVideoPlaceholder(id) {
|
function CreateVideoPlaceholder(id,phImageURL) {
|
||||||
var imgPH = document.createElement("img")
|
var imgPH = document.createElement("img")
|
||||||
imgPH.src = PlayImageURL
|
imgPH.src = phImageURL
|
||||||
imgPH.id = "play-"+id
|
imgPH.id = "play-"+id
|
||||||
imgPH.alt = "Play Video"
|
imgPH.alt = "Play Video"
|
||||||
imgPH.title = "Play"
|
imgPH.title = "Play"
|
||||||
|
@ -45,6 +45,7 @@ func (gipg *goInfoPage) GetCacheIDExtension(urlParameters url.Values) string {
|
|||||||
|
|
||||||
type goInfoTemplateMarshal struct {
|
type goInfoTemplateMarshal struct {
|
||||||
FullOutput bool
|
FullOutput bool
|
||||||
|
CurrentTime time.Time
|
||||||
RegisteredPages []string
|
RegisteredPages []string
|
||||||
CachedPages []string
|
CachedPages []string
|
||||||
ProcessID int
|
ProcessID int
|
||||||
@ -98,6 +99,7 @@ func (gipg *goInfoPage) GetContents(urlParameters url.Values) (contentType strin
|
|||||||
theBuffer := &io.BufferedWriter{}
|
theBuffer := &io.BufferedWriter{}
|
||||||
err = theTemplate.ExecuteTemplate(theBuffer, templateName, &goInfoTemplateMarshal{
|
err = theTemplate.ExecuteTemplate(theBuffer, templateName, &goInfoTemplateMarshal{
|
||||||
FullOutput: urlParameters.Has("full"),
|
FullOutput: urlParameters.Has("full"),
|
||||||
|
CurrentTime: time.Now(),
|
||||||
RegisteredPages: regPages,
|
RegisteredPages: regPages,
|
||||||
CachedPages: cacPages,
|
CachedPages: cacPages,
|
||||||
ProcessID: os.Getpid(),
|
ProcessID: os.Getpid(),
|
||||||
|
@ -43,9 +43,9 @@ func NewPageHandler(config conf.ServeYaml) *PageHandler {
|
|||||||
CacheSettings: config.CacheSettings,
|
CacheSettings: config.CacheSettings,
|
||||||
}
|
}
|
||||||
if config.EnableGoInfoPage {
|
if config.EnableGoInfoPage {
|
||||||
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.DataStorage, toReturn)
|
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.GetDataStoragePath(), toReturn, config.GetTemplateStoragePath(), config.PageSettings, config.YmlDataFallback)
|
||||||
} else {
|
} else {
|
||||||
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.DataStorage, nil)
|
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.GetDataStoragePath(), nil, config.GetTemplateStoragePath(), config.PageSettings, config.YmlDataFallback)
|
||||||
}
|
}
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
@ -286,6 +286,9 @@ func (ph *PageHandler) GetRegisteredPages() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ph *PageHandler) GetCachedPages() []string {
|
func (ph *PageHandler) GetCachedPages() []string {
|
||||||
|
if ph.pageContentsCacheRWMutex == nil {
|
||||||
|
return make([]string, 0)
|
||||||
|
}
|
||||||
ph.pageContentsCacheRWMutex.RLock()
|
ph.pageContentsCacheRWMutex.RLock()
|
||||||
defer ph.pageContentsCacheRWMutex.RUnlock()
|
defer ph.pageContentsCacheRWMutex.RUnlock()
|
||||||
pages := make([]string, len(ph.PageContentsCache))
|
pages := make([]string, len(ph.PageContentsCache))
|
||||||
@ -298,6 +301,9 @@ func (ph *PageHandler) GetCachedPages() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ph *PageHandler) GetNumberOfCachedPages() int {
|
func (ph *PageHandler) GetNumberOfCachedPages() int {
|
||||||
|
if ph.pageContentsCacheRWMutex == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
ph.pageContentsCacheRWMutex.RLock()
|
ph.pageContentsCacheRWMutex.RLock()
|
||||||
defer ph.pageContentsCacheRWMutex.RUnlock()
|
defer ph.pageContentsCacheRWMutex.RUnlock()
|
||||||
return len(ph.PageContentsCache)
|
return len(ph.PageContentsCache)
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
package pageHandler
|
package pageHandler
|
||||||
|
|
||||||
import "golang.captainalm.com/cityuni-webserver/pageHandler/pages/index"
|
import (
|
||||||
|
"golang.captainalm.com/cityuni-webserver/conf"
|
||||||
|
"golang.captainalm.com/cityuni-webserver/pageHandler/pages/index"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
var providers map[string]PageProvider
|
var providers map[string]PageProvider
|
||||||
|
|
||||||
func GetProviders(cacheTemplates bool, dataStorage string, pageHandler *PageHandler) map[string]PageProvider {
|
func GetProviders(cacheTemplates bool, dataStorage string, pageHandler *PageHandler, templateStorage string, pageSettings []conf.PageYaml, ymlDataFallback bool) map[string]PageProvider {
|
||||||
if providers == nil {
|
if providers == nil {
|
||||||
providers = make(map[string]PageProvider)
|
providers = make(map[string]PageProvider)
|
||||||
if pageHandler != nil {
|
if pageHandler != nil {
|
||||||
infoPage := newGoInfoPage(pageHandler, dataStorage, cacheTemplates)
|
infoPage := newGoInfoPage(pageHandler, dataStorage, cacheTemplates)
|
||||||
providers[infoPage.GetPath()] = infoPage //Go Information Page
|
providers[infoPage.GetPath()] = infoPage //Go Information Page
|
||||||
}
|
}
|
||||||
indexPage := index.NewPage(dataStorage, cacheTemplates)
|
for _, cpg := range pageSettings { //Register pages
|
||||||
providers[indexPage.GetPath()] = indexPage
|
if strings.EqualFold(cpg.PageName, index.PageName) {
|
||||||
|
indexPage := index.NewPage(dataStorage, cacheTemplates, templateStorage, cpg.GetPagePath(), ymlDataFallback)
|
||||||
|
providers[indexPage.GetPath()] = indexPage
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return providers
|
return providers
|
||||||
}
|
}
|
||||||
|
@ -5,21 +5,23 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const dateFormat = "01/2006"
|
const dateFormat = "01/2006"
|
||||||
|
|
||||||
type EntryYaml struct {
|
type EntryYaml struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Content string `yaml:"content"`
|
Content string `yaml:"content"`
|
||||||
StartDate yaml.DateType `yaml:"startDate"`
|
StartDate yaml.DateType `yaml:"startDate"`
|
||||||
EndDate yaml.DateType `yaml:"endDate"`
|
EndDate yaml.DateType `yaml:"endDate"`
|
||||||
VideoLocation template.URL `yaml:"videoLocation"`
|
VideoLocation template.URL `yaml:"videoLocation"`
|
||||||
VideoContentType string `yaml:"videoContentType"`
|
VideoContentType string `yaml:"videoContentType"`
|
||||||
ThumbnailLocations []template.URL `yaml:"thumbnailLocations"`
|
ThumbnailLocations []template.URL `yaml:"thumbnailLocations"`
|
||||||
ImageLocations []template.URL `yaml:"imageLocations"`
|
ImageLocations []template.URL `yaml:"imageLocations"`
|
||||||
ImageAltTexts []string `yaml:"imageAltTexts"`
|
ImageAltTexts []string `yaml:"imageAltTexts"`
|
||||||
|
VideoThumbnailLocation template.URL `yaml:"videoThumbnailLocation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageReference struct {
|
type ImageReference struct {
|
||||||
@ -28,6 +30,18 @@ type ImageReference struct {
|
|||||||
ImageAltText string
|
ImageAltText string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ey EntryYaml) GetVideoThumbnail(usual template.URL) template.URL {
|
||||||
|
if ey.VideoThumbnailLocation == "" {
|
||||||
|
return usual
|
||||||
|
} else {
|
||||||
|
return ey.VideoThumbnailLocation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ey EntryYaml) IsVideoLink() bool {
|
||||||
|
return strings.EqualFold(ey.VideoContentType, "text/uri-list")
|
||||||
|
}
|
||||||
|
|
||||||
func (ey EntryYaml) GetStartDate() string {
|
func (ey EntryYaml) GetStartDate() string {
|
||||||
return ey.StartDate.Format(dateFormat)
|
return ey.StartDate.Format(dateFormat)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"golang.captainalm.com/cityuni-webserver/utils/io"
|
"golang.captainalm.com/cityuni-webserver/utils/io"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -12,10 +13,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const PageName = "index"
|
||||||
const templateName = "index.go.html"
|
const templateName = "index.go.html"
|
||||||
const yamlName = "index.go.yml"
|
|
||||||
|
|
||||||
func NewPage(dataStore string, cacheTemplates bool) *Page {
|
func NewPage(dataStore string, cacheTemplates bool, templateStore string, pagePath string, ymlDataFallback bool) *Page {
|
||||||
var ptm *sync.Mutex
|
var ptm *sync.Mutex
|
||||||
var sdm *sync.Mutex
|
var sdm *sync.Mutex
|
||||||
if cacheTemplates {
|
if cacheTemplates {
|
||||||
@ -23,7 +24,10 @@ func NewPage(dataStore string, cacheTemplates bool) *Page {
|
|||||||
sdm = &sync.Mutex{}
|
sdm = &sync.Mutex{}
|
||||||
}
|
}
|
||||||
pageToReturn := &Page{
|
pageToReturn := &Page{
|
||||||
DataStore: dataStore,
|
YMLDataFallback: ymlDataFallback,
|
||||||
|
PagePath: pagePath,
|
||||||
|
DataPath: path.Join(dataStore, pagePath),
|
||||||
|
TemplatePath: path.Join(templateStore, templateName),
|
||||||
StoredDataMutex: sdm,
|
StoredDataMutex: sdm,
|
||||||
PageTemplateMutex: ptm,
|
PageTemplateMutex: ptm,
|
||||||
}
|
}
|
||||||
@ -31,7 +35,10 @@ func NewPage(dataStore string, cacheTemplates bool) *Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
DataStore string
|
YMLDataFallback bool
|
||||||
|
PagePath string
|
||||||
|
DataPath string
|
||||||
|
TemplatePath string
|
||||||
StoredDataMutex *sync.Mutex
|
StoredDataMutex *sync.Mutex
|
||||||
StoredData *DataYaml
|
StoredData *DataYaml
|
||||||
LastModifiedData time.Time
|
LastModifiedData time.Time
|
||||||
@ -41,7 +48,7 @@ type Page struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) GetPath() string {
|
func (p *Page) GetPath() string {
|
||||||
return "/index.go"
|
return p.PagePath
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) GetLastModified() time.Time {
|
func (p *Page) GetLastModified() time.Time {
|
||||||
@ -129,16 +136,12 @@ func (p *Page) getPageTemplate() (*template.Template, error) {
|
|||||||
defer p.PageTemplateMutex.Unlock()
|
defer p.PageTemplateMutex.Unlock()
|
||||||
}
|
}
|
||||||
if p.PageTemplate == nil {
|
if p.PageTemplate == nil {
|
||||||
thePath := templateName
|
stat, err := os.Stat(p.TemplatePath)
|
||||||
if p.DataStore != "" {
|
|
||||||
thePath = path.Join(p.DataStore, thePath)
|
|
||||||
}
|
|
||||||
stat, err := os.Stat(thePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
p.LastModifiedTemplate = stat.ModTime()
|
p.LastModifiedTemplate = stat.ModTime()
|
||||||
loadedData, err := os.ReadFile(thePath)
|
loadedData, err := os.ReadFile(p.TemplatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -161,13 +164,18 @@ func (p *Page) getPageData() (*DataYaml, error) {
|
|||||||
defer p.StoredDataMutex.Unlock()
|
defer p.StoredDataMutex.Unlock()
|
||||||
}
|
}
|
||||||
if p.StoredData == nil {
|
if p.StoredData == nil {
|
||||||
thePath := yamlName
|
thePath := p.DataPath
|
||||||
if p.DataStore != "" {
|
|
||||||
thePath = path.Join(p.DataStore, thePath)
|
|
||||||
}
|
|
||||||
stat, err := os.Stat(thePath)
|
stat, err := os.Stat(thePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if p.YMLDataFallback && errors.Is(err, os.ErrNotExist) {
|
||||||
|
thePath += ".yml"
|
||||||
|
stat, err = os.Stat(thePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.LastModifiedData = stat.ModTime()
|
p.LastModifiedData = stat.ModTime()
|
||||||
fileHandle, err := os.Open(thePath)
|
fileHandle, err := os.Open(thePath)
|
||||||
@ -189,6 +197,7 @@ func (p *Page) getPageData() (*DataYaml, error) {
|
|||||||
p.StoredData = dataYaml
|
p.StoredData = dataYaml
|
||||||
}
|
}
|
||||||
return dataYaml, nil
|
return dataYaml, nil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return p.StoredData, nil
|
return p.StoredData, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user