; ============================================================================ ; 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