SelfHost/Admin/Includes/MainWindow.sbi
2025-12-15 19:46:13 +01:00

928 lines
34 KiB
Plaintext

; ============================================================================
; MainWindow Module - UI and Event Handling
; ============================================================================
Module MainWindow
EnableExplicit
; ========================================================================
; UI Structures
; ========================================================================
Structure ArticleLanguageGadgets
Name.i
Image.i
Blurb.i
Content.i
EndStructure
Structure TagLanguageGadgets
Name.i
EndStructure
Structure UIState
Window.i
CurrentSelection.i
CurrentArticleID.i
CurrentTagID.i
UploadBase64.s
EndStructure
Structure MenuBarGadgets
Container.i
Font.i
Articles.i
Files.i
Tags.i
Separator0.i
Separator1.i
EndStructure
Structure ArticleListGadgets
Container.i
ListIcon.i
ButtonNew.i
ButtonEdit.i
ButtonDelete.i
EndStructure
Structure ArticleEditorGadgets
Container.i
StringTitle.i
CheckboxDraft.i
Date.i
Panel.i
ButtonSave.i
ButtonPreview.i
ListviewTags.i
List LanguageTabs.ArticleLanguageGadgets()
EndStructure
Structure FilesGadgets
Container.i
ListIcon.i
ButtonNew.i
ButtonDelete.i
EndStructure
Structure TagsListGadgets
Container.i
ListIcon.i
ButtonNew.i
ButtonEdit.i
EndStructure
Structure TagEditorGadgets
Container.i
StringName.i
StringColor.i
Panel.i
ButtonSave.i
List LanguageTabs.TagLanguageGadgets()
EndStructure
; -- Constants --
#MenuBar_Height = 60
#MenuBar_Height_Mobile = 80
#MobileBreakpoint = 750
#MaxContentWidth = 950
Enumeration Selection
#Sel_ArticleList
#Sel_Article
#Sel_Files
#Sel_TagList
#Sel_Tag
EndEnumeration
; -- Module Variables --
Global State.UIState
Global MenuBar.MenuBarGadgets
Global ArticleList.ArticleListGadgets
Global ArticleEditor.ArticleEditorGadgets
Global FilesView.FilesGadgets
Global TagsList.TagsListGadgets
Global TagEditor.TagEditorGadgets
; -- Forward Declarations --
Declare CreateMenuBar()
Declare CreateArticleListView()
Declare CreateArticleEditorView()
Declare CreateFilesView()
Declare CreateTagsListView()
Declare CreateTagEditorView()
Declare.s SanitizeURL(InputString.s)
Declare.s StringToBase64(String.s)
Declare ChangeSelection()
Declare SetMenuHighlight(ActiveGadget)
Declare OpenURLInNewTab(URL.s)
Declare Handler_Resize()
Declare Handler_Articles()
Declare Handler_Files()
Declare Handler_Tags()
Declare Handler_NewArticle()
Declare Handler_EditArticle()
Declare Handler_DeleteArticle()
Declare Handler_UpdateArticle()
Declare Handler_PreviewArticle()
Declare Handler_NewFile()
Declare Handler_DeleteFile()
Declare Handler_FileListIcon()
Declare Handler_NewTag()
Declare Handler_EditTag()
Declare Handler_UpdateTag()
Declare UpdateArticle_Callback(Success, Result.s, UserData)
Declare EditArticle_Callback(Success, Result.s, UserData)
Declare NewArticle_Callback(Success, Result.s, UserData)
Declare DeleteArticle_Callback(Success, Result.s, UserData)
Declare NewFile_Callback()
Declare OpenFile_Callback(Status, Filename$, File, SizeRead)
Declare UploadFile_Callback(Success, Result.s, UserData)
Declare DeleteFile_Callback(Success, Result.s, UserData)
Declare PreviewArticle_Callback(Success, Result.s, UserData)
Declare NewTag_Callback(Success, Result.s, UserData)
Declare EditTag_Callback(Success, Result.s, UserData)
Declare UpdateTag_Callback(Success, Result.s, UserData)
; ========================================================================
; Public Entry Point
; ========================================================================
Procedure Open()
State\Window = OpenWindow(#PB_Any, 0, 0, 10, 10, General::#AppName, #PB_Window_Background)
State\CurrentSelection = #Sel_ArticleList
CreateMenuBar()
CreateArticleListView()
CreateArticleEditorView()
CreateFilesView()
CreateTagsListView()
CreateTagEditorView()
BindEvent(#PB_Event_SizeWindow, @Handler_Resize())
Handler_Resize()
EndProcedure
; ========================================================================
; UI Creation
; ========================================================================
Procedure CreateMenuBar()
Protected TitleGadget
MenuBar\Font = LoadFont(#PB_Any, "Tahoma", 20)
MenuBar\Container = ContainerGadget(#PB_Any, 0, 0, 10, #MenuBar_Height)
SetGadgetColor(MenuBar\Container, #PB_Gadget_BackColor, $2A2822)
TitleGadget = TextGadget(#PB_Any, 10, 0, 170, 60, General::#AppName, #PB_Text_Center | #PB_Text_VerticalCenter)
SetGadgetFont(TitleGadget, FontID(MenuBar\Font))
SetGadgetColor(TitleGadget, #PB_Gadget_FrontColor, $FFFFFF)
MenuBar\Articles = HyperLinkGadget(#PB_Any, 0, 0, 50, 20, "Articles", $8A8578)
SetGadgetColor(MenuBar\Articles, #PB_Gadget_FrontColor, $999999)
BindGadgetEvent(MenuBar\Articles, @Handler_Articles())
MenuBar\Separator0 = TextGadget(#PB_Any, 0, 0, 10, 20, Chr(9679), #PB_Text_VerticalCenter)
SetGadgetColor(MenuBar\Separator0, #PB_Gadget_FrontColor, $FFFFFF)
MenuBar\Files = HyperLinkGadget(#PB_Any, 0, 0, 60, 20, "Files", $8A8578)
SetGadgetColor(MenuBar\Files, #PB_Gadget_FrontColor, $FFFFFF)
BindGadgetEvent(MenuBar\Files, @Handler_Files())
MenuBar\Separator1 = TextGadget(#PB_Any, 0, 0, 10, 20, Chr(9679), #PB_Text_VerticalCenter)
SetGadgetColor(MenuBar\Separator1, #PB_Gadget_FrontColor, $FFFFFF)
MenuBar\Tags = HyperLinkGadget(#PB_Any, 0, 0, 60, 20, "Tags", $8A8578)
SetGadgetColor(MenuBar\Tags, #PB_Gadget_FrontColor, $FFFFFF)
BindGadgetEvent(MenuBar\Tags, @Handler_Tags())
CloseGadgetList()
EndProcedure
Procedure CreateArticleListView()
ArticleList\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
ArticleList\ListIcon = ListIconGadget(#PB_Any, 0, 0, 10, 10, "Title", 200)
BindGadgetEvent(ArticleList\ListIcon, @Handler_EditArticle(), #PB_EventType_LeftDoubleClick)
AddGadgetColumn(ArticleList\ListIcon, 1, "Status", 50)
AddGadgetColumn(ArticleList\ListIcon, 2, "Date", 100)
ForEach DataModel::Articles()
AddGadgetItem(ArticleList\ListIcon, 0, DataModel::FormatArticleListItem(DataModel::@Articles()))
SetGadgetItemData(ArticleList\ListIcon, 0, DataModel::Articles()\ID)
Next
ArticleList\ButtonNew = ButtonGadget(#PB_Any, 10, 10, 120, 20, "New Article")
ArticleList\ButtonEdit = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Edit")
ArticleList\ButtonDelete = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Delete")
BindGadgetEvent(ArticleList\ButtonNew, @Handler_NewArticle())
BindGadgetEvent(ArticleList\ButtonEdit, @Handler_EditArticle())
BindGadgetEvent(ArticleList\ButtonDelete, @Handler_DeleteArticle())
CloseGadgetList()
EndProcedure
Procedure CreateArticleEditorView()
ArticleEditor\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
TextGadget(#PB_Any, 20, 20, 50, 20, "Title:", #PB_Text_VerticalCenter)
ArticleEditor\StringTitle = StringGadget(#PB_Any, 80, 20, 510, 20, "")
TextGadget(#PB_Any, 20, 60, 50, 20, "Date:", #PB_Text_VerticalCenter)
ArticleEditor\Date = DateGadget(#PB_Any, 80, 60, 200, 20)
ArticleEditor\CheckboxDraft = CheckBoxGadget(#PB_Any, 570, 60, 60, 20, "Draft", #PB_CheckBox_Center)
ArticleEditor\ListviewTags = ListViewGadget(#PB_Any, 20, 100, 100, 100, #PB_ListView_ClickSelect)
ForEach DataModel::Tags()
AddGadgetItem(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\DefaultName)
SetGadgetItemData(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\BinaryID)
Next
ArticleEditor\Panel = PanelGadget(#PB_Any, 0, 0, 10, 10)
ForEach DataModel::Language()
AddElement(ArticleEditor\LanguageTabs())
AddGadgetItem(ArticleEditor\Panel, -1, DataModel::Language())
TextGadget(#PB_Any, 20, 20, 90, 20, "Localized title:", #PB_Text_VerticalCenter)
ArticleEditor\LanguageTabs()\Name = StringGadget(#PB_Any, 130, 20, 80, 20, "")
TextGadget(#PB_Any, 20, 60, 60, 20, "Image:", #PB_Text_VerticalCenter)
ArticleEditor\LanguageTabs()\Image = StringGadget(#PB_Any, 130, 60, 80, 20, "")
TextGadget(#PB_Any, 20, 100, 50, 20, "Blurb:", #PB_Text_VerticalCenter)
ArticleEditor\LanguageTabs()\Blurb = EditorGadget(#PB_Any, 20, 120, 80, 50, #PB_Editor_WordWrap)
TextGadget(#PB_Any, 20, 180, 50, 20, "Content:", #PB_Text_VerticalCenter)
ArticleEditor\LanguageTabs()\Content = EditorGadget(#PB_Any, 20, 200, 80, 20)
Next
CloseGadgetList()
ArticleEditor\ButtonPreview = ButtonGadget(#PB_Any, 0, 0, 180, 25, "Preview")
ArticleEditor\ButtonSave = ButtonGadget(#PB_Any, 0, 0, 180, 25, "Save")
BindGadgetEvent(ArticleEditor\ButtonSave, @Handler_UpdateArticle())
BindGadgetEvent(ArticleEditor\ButtonPreview, @Handler_PreviewArticle())
CloseGadgetList()
HideGadget(ArticleEditor\Container, #True)
EndProcedure
Procedure CreateFilesView()
FilesView\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
FilesView\ListIcon = ListIconGadget(#PB_Any, 0, 0, 10, 10, "Name", 200)
AddGadgetColumn(FilesView\ListIcon, 1, "Type", 50)
ForEach DataModel::Files()
AddGadgetItem(FilesView\ListIcon, 0, DataModel::Files()\Name + Chr(10) + DataModel::Files()\Type)
SetGadgetItemData(FilesView\ListIcon, 0, DataModel::Files()\ID)
Next
FilesView\ButtonNew = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Upload File")
FilesView\ButtonDelete = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Delete File")
BindGadgetEvent(FilesView\ButtonNew, @Handler_NewFile())
BindGadgetEvent(FilesView\ButtonDelete, @Handler_DeleteFile())
BindGadgetEvent(FilesView\ListIcon, @Handler_FileListIcon())
CloseGadgetList()
HideGadget(FilesView\Container, #True)
EndProcedure
Procedure CreateTagsListView()
TagsList\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
TagsList\ListIcon = ListIconGadget(#PB_Any, 0, 0, 10, 10, "Name", 200)
BindGadgetEvent(TagsList\ListIcon, @Handler_EditTag(), #PB_EventType_LeftDoubleClick)
AddGadgetColumn(TagsList\ListIcon, 1, "Color", 100)
ForEach DataModel::Tags()
AddGadgetItem(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::FormatTagListItem(DataModel::@Tags()))
SetGadgetItemData(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::Tags()\ID)
Next
TagsList\ButtonNew = ButtonGadget(#PB_Any, 10, 10, 120, 20, "New Tag")
TagsList\ButtonEdit = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Edit Tag")
BindGadgetEvent(TagsList\ButtonNew, @Handler_NewTag())
BindGadgetEvent(TagsList\ButtonEdit, @Handler_EditTag())
CloseGadgetList()
HideGadget(TagsList\Container, #True)
EndProcedure
Procedure CreateTagEditorView()
TagEditor\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
TextGadget(#PB_Any, 20, 20, 80, 20, "Default Name:", #PB_Text_VerticalCenter)
TagEditor\StringName = StringGadget(#PB_Any, 110, 20, 200, 20, "")
TextGadget(#PB_Any, 20, 60, 50, 20, "Color:", #PB_Text_VerticalCenter)
TagEditor\StringColor = StringGadget(#PB_Any, 110, 60, 100, 20, "")
TagEditor\Panel = PanelGadget(#PB_Any, 0, 0, 10, 10)
ForEach DataModel::Language()
AddElement(TagEditor\LanguageTabs())
AddGadgetItem(TagEditor\Panel, -1, DataModel::Language())
TextGadget(#PB_Any, 20, 20, 100, 20, "Localized name:", #PB_Text_VerticalCenter)
TagEditor\LanguageTabs()\Name = StringGadget(#PB_Any, 130, 20, 200, 20, "")
Next
CloseGadgetList()
TagEditor\ButtonSave = ButtonGadget(#PB_Any, 0, 0, 180, 25, "Save")
BindGadgetEvent(TagEditor\ButtonSave, @Handler_UpdateTag())
CloseGadgetList()
HideGadget(TagEditor\Container, #True)
EndProcedure
; ========================================================================
; Utilities
; ========================================================================
Procedure.s SanitizeURL(InputString.s)
Protected Result.s, Char.s, i, CharCode, Len = Len(InputString)
For i = 1 To Len
Char = Mid(InputString, i, 1)
CharCode = Asc(Char)
If (CharCode >= 65 And CharCode <= 90) Or (CharCode >= 97 And CharCode <= 122) Or (CharCode >= 48 And CharCode <= 57) Or Char = "-" Or Char = "." Or Char = "_" Or Char = "/"
Result + Char
EndIf
Next
ProcedureReturn Result
EndProcedure
Procedure.s StringToBase64(String.s)
Protected Size, *Memory, Result.s
Size = StringByteLength(String)
If Size > 0
*Memory = AllocateMemory(Size, #PB_Memory_NoClear)
PokeS(*Memory, 0, String)
Result = Base64Encoder(*Memory, 0, Size)
FreeMemory(*Memory)
EndIf
ProcedureReturn Result
EndProcedure
Procedure ChangeSelection()
Select State\CurrentSelection
Case #Sel_ArticleList
HideGadget(ArticleList\Container, #True)
SetGadgetColor(MenuBar\Articles, #PB_Gadget_FrontColor, $FFFFFF)
Case #Sel_Article
HideGadget(ArticleEditor\Container, #True)
Case #Sel_Files
HideGadget(FilesView\Container, #True)
SetGadgetColor(MenuBar\Files, #PB_Gadget_FrontColor, $FFFFFF)
Case #Sel_TagList
HideGadget(TagsList\Container, #True)
SetGadgetColor(MenuBar\Tags, #PB_Gadget_FrontColor, $FFFFFF)
Case #Sel_Tag
HideGadget(TagEditor\Container, #True)
EndSelect
EndProcedure
Procedure SetMenuHighlight(ActiveGadget)
SetGadgetColor(MenuBar\Articles, #PB_Gadget_FrontColor, $FFFFFF)
SetGadgetColor(MenuBar\Files, #PB_Gadget_FrontColor, $FFFFFF)
SetGadgetColor(MenuBar\Tags, #PB_Gadget_FrontColor, $FFFFFF)
If ActiveGadget
SetGadgetColor(ActiveGadget, #PB_Gadget_FrontColor, $999999)
EndIf
EndProcedure
Procedure OpenURLInNewTab(URL.s)
!window.open(v_url, '_blank').focus();
EndProcedure
; ========================================================================
; Resize Handler
; ========================================================================
Procedure Handler_Resize()
Protected WinWidth = WindowWidth(State\Window)
Protected WinHeight = WindowHeight(State\Window)
Protected ContentWidth = WinWidth
Protected ContentHeight, ContentX = 0, ContentY = #MenuBar_Height_Mobile
Protected MenuX, PanelWidth, PanelHeight
If WinWidth < #MobileBreakpoint
ResizeGadget(MenuBar\Container, 0, 0, WinWidth, #MenuBar_Height_Mobile)
ResizeGadget(MenuBar\Articles, 15, 55, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Separator0, 66, 55, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Files, 85, 55, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Separator1, 123, 55, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Tags, 140, 55, #PB_Ignore, #PB_Ignore)
ContentHeight = WinHeight - #MenuBar_Height_Mobile
Else
ResizeGadget(MenuBar\Container, 0, 0, WinWidth, #MenuBar_Height)
MenuX = WinWidth - 185
ResizeGadget(MenuBar\Articles, MenuX + 15, 22, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Separator0, MenuX + 66, 22, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Files, MenuX + 85, 22, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Separator1, MenuX + 123, 22, #PB_Ignore, #PB_Ignore)
ResizeGadget(MenuBar\Tags, MenuX + 140, 22, #PB_Ignore, #PB_Ignore)
ContentHeight = WinHeight - (2 * #MenuBar_Height_Mobile)
ContentWidth = WinWidth * 0.8
If ContentWidth > #MaxContentWidth : ContentWidth = #MaxContentWidth : EndIf
ContentY + 30
ContentX = (WinWidth - ContentWidth) * 0.5
EndIf
Select State\CurrentSelection
Case #Sel_ArticleList
ResizeGadget(ArticleList\Container, ContentX, ContentY, ContentWidth, ContentHeight)
ResizeGadget(ArticleList\ListIcon, 0, 0, ContentWidth, ContentHeight - 40)
ResizeGadget(ArticleList\ButtonNew, ContentWidth - 130, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
ResizeGadget(ArticleList\ButtonEdit, ContentWidth - 260, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
ResizeGadget(ArticleList\ButtonDelete, ContentWidth - 390, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
Case #Sel_Article
ResizeGadget(ArticleEditor\Container, ContentX, ContentY, ContentWidth, ContentHeight)
ResizeGadget(ArticleEditor\StringTitle, #PB_Ignore, #PB_Ignore, ContentWidth - 100, #PB_Ignore)
ResizeGadget(ArticleEditor\Date, #PB_Ignore, #PB_Ignore, ContentWidth - 170, #PB_Ignore)
ResizeGadget(ArticleEditor\CheckboxDraft, ContentWidth - 80, 60, 60, #PB_Ignore)
ResizeGadget(ArticleEditor\ListviewTags, #PB_Ignore, #PB_Ignore, ContentWidth - 40, 100)
If ContentWidth > 550
ResizeGadget(ArticleEditor\Panel, 20, 210, ContentWidth - 40, ContentHeight - 265)
Else
ResizeGadget(ArticleEditor\Panel, 0, 210, ContentWidth, ContentHeight - 265)
EndIf
ResizeGadget(ArticleEditor\ButtonPreview, 20, ContentHeight - 40, (ContentWidth - 60) * 0.5, 25)
ResizeGadget(ArticleEditor\ButtonSave, 40 + (ContentWidth - 60) * 0.5, ContentHeight - 40, (ContentWidth - 60) * 0.5, 25)
PanelWidth = GadgetWidth(ArticleEditor\Panel)
PanelHeight = GadgetHeight(ArticleEditor\Panel)
ForEach ArticleEditor\LanguageTabs()
ResizeGadget(ArticleEditor\LanguageTabs()\Name, #PB_Ignore, #PB_Ignore, PanelWidth - 150, #PB_Ignore)
ResizeGadget(ArticleEditor\LanguageTabs()\Image, #PB_Ignore, #PB_Ignore, PanelWidth - 150, #PB_Ignore)
ResizeGadget(ArticleEditor\LanguageTabs()\Blurb, #PB_Ignore, #PB_Ignore, PanelWidth - 40, #PB_Ignore)
ResizeGadget(ArticleEditor\LanguageTabs()\Content, #PB_Ignore, #PB_Ignore, PanelWidth - 40, PanelHeight - 250)
Next
Case #Sel_Files
ResizeGadget(FilesView\Container, ContentX, ContentY, ContentWidth, ContentHeight)
ResizeGadget(FilesView\ListIcon, 0, 0, ContentWidth, ContentHeight - 40)
ResizeGadget(FilesView\ButtonNew, ContentWidth - 130, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
ResizeGadget(FilesView\ButtonDelete, ContentWidth - 260, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
Case #Sel_TagList
ResizeGadget(TagsList\Container, ContentX, ContentY, ContentWidth, ContentHeight)
ResizeGadget(TagsList\ListIcon, 0, 0, ContentWidth, ContentHeight - 40)
ResizeGadget(TagsList\ButtonNew, ContentWidth - 130, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
ResizeGadget(TagsList\ButtonEdit, ContentWidth - 260, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
Case #Sel_Tag
ResizeGadget(TagEditor\Container, ContentX, ContentY, ContentWidth, ContentHeight)
ResizeGadget(TagEditor\StringName, #PB_Ignore, #PB_Ignore, ContentWidth - 130, #PB_Ignore)
ResizeGadget(TagEditor\StringColor, #PB_Ignore, #PB_Ignore, ContentWidth - 130, #PB_Ignore)
If ContentWidth > 550
ResizeGadget(TagEditor\Panel, 20, 100, ContentWidth - 40, ContentHeight - 155)
Else
ResizeGadget(TagEditor\Panel, 0, 100, ContentWidth, ContentHeight - 155)
EndIf
ResizeGadget(TagEditor\ButtonSave, 20, ContentHeight - 40, ContentWidth - 40, 25)
PanelWidth = GadgetWidth(TagEditor\Panel)
ForEach TagEditor\LanguageTabs()
ResizeGadget(TagEditor\LanguageTabs()\Name, #PB_Ignore, #PB_Ignore, PanelWidth - 150, #PB_Ignore)
Next
EndSelect
EndProcedure
; ========================================================================
; Navigation Handlers
; ========================================================================
Procedure Handler_Articles()
If State\CurrentSelection <> #Sel_ArticleList
ChangeSelection()
State\CurrentSelection = #Sel_ArticleList
SetMenuHighlight(MenuBar\Articles)
HideGadget(ArticleList\Container, #False)
Handler_Resize()
EndIf
EndProcedure
Procedure Handler_Files()
If State\CurrentSelection <> #Sel_Files
ChangeSelection()
State\CurrentSelection = #Sel_Files
SetMenuHighlight(MenuBar\Files)
HideGadget(FilesView\Container, #False)
Handler_Resize()
EndIf
EndProcedure
Procedure Handler_Tags()
If State\CurrentSelection <> #Sel_TagList
ChangeSelection()
State\CurrentSelection = #Sel_TagList
SetMenuHighlight(MenuBar\Tags)
HideGadget(TagsList\Container, #False)
Handler_Resize()
EndIf
EndProcedure
; ========================================================================
; Article Handlers
; ========================================================================
Procedure Handler_NewArticle()
HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "newarticle", @NewArticle_Callback())
EndProcedure
Procedure Handler_EditArticle()
Protected Item = GetGadgetState(ArticleList\ListIcon)
If Item >= 0
State\CurrentArticleID = GetGadgetItemData(ArticleList\ListIcon, Item)
HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "getarticle=" + State\CurrentArticleID, @EditArticle_Callback())
EndIf
EndProcedure
Procedure Handler_DeleteArticle()
Protected SelectedItem = GetGadgetState(ArticleList\ListIcon)
If SelectedItem > -1
Protected ArticleID = GetGadgetItemData(ArticleList\ListIcon, SelectedItem)
HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "deletearticle=" + ArticleID, @DeleteArticle_Callback())
EndIf
EndProcedure
Procedure Handler_UpdateArticle()
Protected TextBundle.s, tags.i, Title.s
;Sanitize the title and make sure it's unique
Title = SanitizeURL(GetGadgetText(ArticleEditor\StringTitle))
SetGadgetText(ArticleEditor\StringTitle, Title)
ForEach DataModel::Articles()
If DataModel::Articles()\Title = Title
If DataModel::Articles()\ID <> State\CurrentArticleID
Debug "[Error] An article with the same name already exists."
ProcedureReturn #False
EndIf
EndIf
Next
ForEach DataModel::Tags()
If GetGadgetItemState(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()))
tags = tags | DataModel::Tags()\BinaryID
EndIf
Next
TextBundle = Str(State\CurrentArticleID) + General::#FieldSeparator
TextBundle + Title + General::#FieldSeparator
TextBundle + Str(GetGadgetState(ArticleEditor\CheckboxDraft)) + General::#FieldSeparator
TextBundle + Tags + General::#FieldSeparator
TextBundle + Str(GetGadgetState(ArticleEditor\Date)) + General::#RecordSeparator
ForEach ArticleEditor\LanguageTabs()
TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Name)) + General::#FieldSeparator
TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Image)) + General::#FieldSeparator
TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Blurb)) + General::#FieldSeparator
TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Content)) + General::#RecordSeparator
Next
HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "updatearticle=" + TextBundle, @UpdateArticle_Callback(), tags)
EndProcedure
Procedure Handler_PreviewArticle()
Protected TextBundle.s
SelectElement(ArticleEditor\LanguageTabs(), GetGadgetState(ArticleEditor\Panel))
TextBundle = StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Name)) + General::#FieldSeparator
TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Content))
HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "previewarticle=" + TextBundle, @PreviewArticle_Callback())
EndProcedure
; ========================================================================
; File Handlers
; ========================================================================
Procedure Handler_NewFile()
OpenFileRequester("*.*", @NewFile_Callback())
EndProcedure
Procedure Handler_DeleteFile()
Protected SelectedItem = GetGadgetState(FilesView\ListIcon)
If SelectedItem > -1
Protected FileID = GetGadgetItemData(FilesView\ListIcon, SelectedItem)
HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "deletefile=" + FileID, @DeleteFile_Callback())
EndIf
EndProcedure
Procedure Handler_FileListIcon()
Protected URL.s, EventType = EventType()
Protected SelectedItem = GetGadgetState(FilesView\ListIcon)
If SelectedItem > -1
URL = General::#Address + RemoveString(GetGadgetItemText(FilesView\ListIcon, SelectedItem, 0), "/", #PB_String_CaseSensitive, 0, 1)
If EventType = #PB_EventType_LeftDoubleClick
OpenURLInNewTab(URL)
ElseIf EventType = #PB_EventType_RightClick
SetClipboardText(URL)
EndIf
EndIf
EndProcedure
; ========================================================================
; Tag Handlers
; ========================================================================
Procedure Handler_NewTag()
HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "newtag", @NewTag_Callback())
EndProcedure
Procedure Handler_EditTag()
Protected Item = GetGadgetState(TagsList\ListIcon)
If Item >= 0
State\CurrentTagID = GetGadgetItemData(TagsList\ListIcon, Item)
HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "gettag=" + State\CurrentTagID, @EditTag_Callback())
EndIf
EndProcedure
Procedure Handler_UpdateTag()
Protected TextBundle.s, Name.s
;Sanitize the title
Name = SanitizeURL(GetGadgetText(TagEditor\StringName))
SetGadgetText(TagEditor\StringName, Name)
ForEach DataModel::Tags()
If DataModel::Tags()\DefaultName = Name
If DataModel::Tags()\ID <> State\CurrentTagID
Debug "[Error] A tag with the same name already exists."
ProcedureReturn #False
EndIf
EndIf
Next
TextBundle = Str(State\CurrentTagID) + General::#FieldSeparator
TextBundle + GetGadgetText(TagEditor\StringName) + General::#FieldSeparator
TextBundle + GetGadgetText(TagEditor\StringColor) + General::#RecordSeparator
ForEach TagEditor\LanguageTabs()
TextBundle + StringToBase64(GetGadgetText(TagEditor\LanguageTabs()\Name)) + General::#RecordSeparator
Next
HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "updatetag=" + TextBundle, @UpdateTag_Callback())
EndProcedure
; ========================================================================
; HTTP Callbacks
; ========================================================================
Procedure UpdateArticle_Callback(Success, Result.s, Tags.i)
If Not Success
MessageRequester("Error - Failed to update article.")
ProcedureReturn
EndIf
If DataModel::FindArticleByID(State\CurrentArticleID)
DataModel::Articles()\Date = GetGadgetState(ArticleEditor\Date)
DataModel::Articles()\Draft = GetGadgetState(ArticleEditor\CheckboxDraft)
DataModel::Articles()\Title = GetGadgetText(ArticleEditor\StringTitle)
DataModel::Articles()\Tags = Tags
Protected Item = GetGadgetState(ArticleList\ListIcon)
SetGadgetItemText(ArticleList\ListIcon, Item, DataModel::FormatArticleListItem(DataModel::@Articles()))
Handler_Articles()
EndIf
EndProcedure
Procedure EditArticle_Callback(Success, Result.s, UserData)
Protected Entry.s
If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_ArticleContent)
MessageRequester("Error - Failed to load article.")
ProcedureReturn
EndIf
Result = DataModel::StripPrefix(Result, General::#Prefix_ArticleContent)
ResetList(ArticleEditor\LanguageTabs())
While NextElement(ArticleEditor\LanguageTabs())
Entry = DataModel::GetRecord(Result, 1 + ListIndex(ArticleEditor\LanguageTabs()))
SetGadgetText(ArticleEditor\LanguageTabs()\Name, DataModel::GetField(Entry, 1))
SetGadgetText(ArticleEditor\LanguageTabs()\Image, DataModel::GetField(Entry, 2))
SetGadgetText(ArticleEditor\LanguageTabs()\Blurb, DataModel::GetField(Entry, 3))
SetGadgetText(ArticleEditor\LanguageTabs()\Content, DataModel::GetField(Entry, 4))
Wend
If DataModel::FindArticleByID(State\CurrentArticleID)
SetGadgetText(ArticleEditor\StringTitle, DataModel::Articles()\Title)
SetGadgetState(ArticleEditor\CheckboxDraft, DataModel::Articles()\Draft)
SetGadgetState(ArticleEditor\Date, DataModel::Articles()\Date)
ForEach DataModel::Tags()
SetGadgetItemState(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), Bool(DataModel::Tags()\BinaryID & DataModel::Articles()\Tags))
Next
EndIf
ChangeSelection()
State\CurrentSelection = #Sel_Article
HideGadget(ArticleEditor\Container, #False)
Handler_Resize()
EndProcedure
Procedure NewArticle_Callback(Success, Result.s, UserData)
If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_NewArticle)
MessageRequester("Error - Failed to create new article.")
ProcedureReturn
EndIf
Result = DataModel::StripPrefix(Result, General::#Prefix_NewArticle)
LastElement(DataModel::Articles())
AddElement(DataModel::Articles())
DataModel::Articles()\ID = Val(DataModel::GetField(Result, 1))
DataModel::Articles()\Title = DataModel::GetField(Result, 2)
DataModel::Articles()\Draft = Val(DataModel::GetField(Result, 3))
DataModel::Articles()\Tags = Val(DataModel::GetField(Result, 4))
DataModel::Articles()\Date = Val(DataModel::GetField(Result, 5))
AddGadgetItem(ArticleList\ListIcon, 0, DataModel::FormatArticleListItem(DataModel::@Articles()))
SetGadgetItemData(ArticleList\ListIcon, 0, DataModel::Articles()\ID)
State\CurrentArticleID = DataModel::Articles()\ID
HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "getarticle=" + State\CurrentArticleID, @EditArticle_Callback())
EndProcedure
Procedure DeleteArticle_Callback(Success, Result.s, UserData)
If Not Success
MessageRequester("Error - Failed to delete article.")
ProcedureReturn
EndIf
Protected SelectedItem = GetGadgetState(ArticleList\ListIcon)
Protected ArticleID = GetGadgetItemData(ArticleList\ListIcon, SelectedItem)
; Remove from list
RemoveGadgetItem(ArticleList\ListIcon, SelectedItem)
; Remove from local data
If DataModel::FindArticleByID(ArticleID)
DeleteElement(DataModel::Articles())
EndIf
EndProcedure
Procedure NewFile_Callback()
If NextSelectedFile()
OpenFile(#PB_Any, SelectedFileName(), @OpenFile_Callback(), #PB_LocalFile)
EndIf
EndProcedure
Procedure OpenFile_Callback(Status, Filename$, File, SizeRead)
Protected *Buffer
If Status <> #PB_Status_Loaded
ProcedureReturn
EndIf
*Buffer = AllocateMemory(SizeRead)
ReadData(File, *Buffer, 0, SizeRead)
State\UploadBase64 = Base64Encoder(*Buffer, 0, SizeRead)
FreeMemory(*Buffer)
CloseFile(File)
Filename$ = ReplaceString(Filename$, "\", "/")
Filename$ = StringField(Filename$, CountString(Filename$, "/") + 1, "/")
Filename$ = InputRequester("FilePath", General::#Address + Filename$)
Filename$ = RemoveString(Filename$, General::#Address, #PB_String_CaseSensitive, 0, 1)
Filename$ = SanitizeURL(Filename$)
If Filename$ = ""
ProcedureReturn
EndIf
ForEach DataModel::Files()
If DataModel::Files()\Name = Filename$
MessageRequester("Error - A file with this name already exists.")
ProcedureReturn
EndIf
Next
If Left(Filename$, 1) <> "/"
Filename$ = "/" + Filename$
EndIf
HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "newfile=" + Filename$ + "&filedata=" + State\UploadBase64, @UploadFile_Callback())
EndProcedure
Procedure UploadFile_Callback(Success, Result.s, UserData)
If Not Success
MessageRequester("Error - Failed to upload file.")
ProcedureReturn
EndIf
Result = DataModel::StripPrefix(Result, General::#Prefix_NewFile)
LastElement(DataModel::Files())
AddElement(DataModel::Files())
DataModel::Files()\ID = Val(DataModel::GetField(Result, 1))
DataModel::Files()\Name = DataModel::GetField(Result, 2)
DataModel::Files()\Type = DataModel::MIMETypes(GetExtensionPart(DataModel::Files()\Name))
AddGadgetItem(FilesView\ListIcon, 0, DataModel::Files()\Name + Chr(10) + DataModel::Files()\Type)
SetGadgetItemData(FilesView\ListIcon, 0, DataModel::Files()\ID)
EndProcedure
Procedure DeleteFile_Callback(Success, Result.s, UserData)
If Not Success
MessageRequester("Error - Failed to delete file.")
ProcedureReturn
EndIf
Protected SelectedItem = GetGadgetState(FilesView\ListIcon)
Protected FileID = GetGadgetItemData(FilesView\ListIcon, SelectedItem)
RemoveGadgetItem(FilesView\ListIcon, SelectedItem)
If DataModel::FindFileByID(FileID)
DeleteElement(DataModel::Files())
EndIf
EndProcedure
Procedure PreviewArticle_Callback(Success, Result.s, UserData)
If Not Success
MessageRequester("Error - Failed to generate preview.")
ProcedureReturn
EndIf
Protected URL.s = General::#Address + "preview?UUID=" + DataModel::StripPrefix(Result, General::#Prefix_PreviewArticle)
OpenURLInNewTab(URL)
EndProcedure
Procedure NewTag_Callback(Success, Result.s, UserData)
If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_NewTag)
MessageRequester("Error - Failed to create new tag.")
ProcedureReturn
EndIf
Result = DataModel::StripPrefix(Result, General::#Prefix_NewTag)
LastElement(DataModel::Tags())
AddElement(DataModel::Tags())
DataModel::Tags()\ID = Val(DataModel::GetField(Result, 1))
DataModel::Tags()\DefaultName = DataModel::GetField(Result, 2)
DataModel::Tags()\Color = DataModel::GetField(Result, 3)
AddGadgetItem(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::FormatTagListItem(DataModel::@Tags()))
SetGadgetItemData(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::Tags()\ID)
SetGadgetState(TagsList\ListIcon, ListIndex(DataModel::Tags()))
AddGadgetItem(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\DefaultName)
SetGadgetItemData(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\BinaryID)
State\CurrentTagID = DataModel::Tags()\ID
HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "gettag=" + State\CurrentTagID, @EditTag_Callback())
EndProcedure
Procedure EditTag_Callback(Success, Result.s, UserData)
Protected Entry.s
If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_TagContent)
MessageRequester("Error - Failed to load tag.")
ProcedureReturn
EndIf
Result = DataModel::StripPrefix(Result, General::#Prefix_TagContent)
Entry = DataModel::GetRecord(Result, 1)
SetGadgetText(TagEditor\StringName, DataModel::GetField(Entry, 1))
SetGadgetText(TagEditor\StringColor, DataModel::GetField(Entry, 2))
ResetList(TagEditor\LanguageTabs())
While NextElement(TagEditor\LanguageTabs())
Entry = DataModel::GetRecord(Result, 2 + ListIndex(TagEditor\LanguageTabs()))
SetGadgetText(TagEditor\LanguageTabs()\Name, Entry)
Wend
ChangeSelection()
State\CurrentSelection = #Sel_Tag
HideGadget(TagEditor\Container, #False)
Handler_Resize()
EndProcedure
Procedure UpdateTag_Callback(Success, Result.s, UserData)
If Not Success
MessageRequester("Error - Failed to update tag.")
ProcedureReturn
EndIf
If DataModel::FindTagByID(State\CurrentTagID)
DataModel::Tags()\DefaultName = GetGadgetText(TagEditor\StringName)
DataModel::Tags()\Color = GetGadgetText(TagEditor\StringColor)
Protected Item = GetGadgetState(TagsList\ListIcon)
SetGadgetItemText(TagsList\ListIcon, Item, DataModel::FormatTagListItem(DataModel::@Tags()))
SetGadgetItemText(ArticleEditor\ListviewTags, Item, DataModel::Tags()\DefaultName)
Handler_Tags()
EndIf
EndProcedure
EndModule
; IDE Options = SpiderBasic 3.10 (Linux - x64)
; CursorPosition = 667
; FirstLine = 209
; Folding = BAAAAAw
; iOSAppOrientation = 0
; AndroidAppCode = 0
; AndroidAppOrientation = 0
; EnableXP
; DPIAware
; CompileSourceDirectory