├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ ├── daily-build.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── forms ├── fmconnect.lfm ├── fmconnect.pas ├── fmmain.lfm ├── fmmain.pas ├── framebase.lfm ├── framebase.pas ├── framecroneditor.lfm ├── framecroneditor.pas ├── frametaskcommandeditor.lfm └── frametaskcommandeditor.pas ├── pg_timetable_gui.ico ├── pg_timetable_gui.lpi ├── pg_timetable_gui.lpr ├── pg_timetable_gui.res ├── res ├── control-play-disabled.png ├── control-play.png ├── nav-add-disabled.png ├── nav-add.png ├── nav-cancel-disabled.png ├── nav-cancel.png ├── nav-delete-disabled.png ├── nav-delete.png ├── nav-down-disabled.png ├── nav-down.png ├── nav-edit-disabled.png ├── nav-edit.png ├── nav-next.png ├── nav-post-disabled.png ├── nav-post.png ├── nav-prev.png ├── nav-refresh-disabled.png ├── nav-refresh.png ├── nav-up-disabled.png ├── nav-up.png ├── pg_timetable_gui.png ├── shut-down-disabled.png ├── shut-down.png ├── sort-asc.png ├── sort-desc.png ├── tab-chain.png ├── tab-dashboard.png ├── tab-log.png ├── tab-task.png ├── task-builtin.png ├── task-program.png └── task-sql.png ├── udatamodule.lfm ├── udatamodule.pas └── uobjects.pas /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "daily" 9 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build-test 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.operating-system }} 10 | strategy: 11 | matrix: 12 | operating-system: [windows-latest,ubuntu-latest,macos-latest] 13 | lazarus-versions: [stable] #[dist, stable, 2.0.12, 2.0.10, 2.0.8, 2.0.6] 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Install Lazarus 19 | uses: gcarreno/setup-lazarus@v3.2 20 | with: 21 | lazarus-version: ${{ matrix.lazarus-versions }} 22 | with-cache: true 23 | 24 | - name: Build the application 25 | if: ${{ matrix.operating-system != 'macos-latest' }} 26 | run: | 27 | lazbuild -B "pg_timetable_gui.lpi" 28 | ls 29 | 30 | - name: Build the application (macOS) 31 | if: ${{ matrix.operating-system == 'macos-latest' }} 32 | run: lazbuild -B --ws=cocoa "pg_timetable_gui.lpi" 33 | 34 | - name: Upload build artifacts 35 | uses: actions/upload-artifact@v4 36 | with: 37 | name: pg_timetable_gui for ${{ matrix.operating-system }} 38 | retention-days: 3 39 | path: | 40 | pg_timetable_gui.exe 41 | pg_timetable_gui -------------------------------------------------------------------------------- /.github/workflows/daily-build.yml: -------------------------------------------------------------------------------- 1 | name: daily-build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - 'main' 8 | paths: 9 | - '**.pas' 10 | - '**.lfm' 11 | 12 | jobs: 13 | daily_build: 14 | if: true # set to false to disable 15 | runs-on: ${{ matrix.operating-system }} 16 | strategy: 17 | matrix: 18 | operating-system: [windows-latest,ubuntu-latest,macos-latest] 19 | lazarus-versions: [stable] #[dist, stable, 2.0.12, 2.0.10, 2.0.8, 2.0.6] 20 | 21 | steps: 22 | 23 | - name: Check out the latest version 24 | uses: actions/checkout@v4 25 | 26 | - name: Install Lazarus 27 | uses: gcarreno/setup-lazarus@v3.2 28 | with: 29 | lazarus-version: ${{ matrix.lazarus-versions }} 30 | with-cache: true 31 | 32 | - name: Build the application 33 | if: ${{ matrix.operating-system != 'macos-latest' }} 34 | run: | 35 | lazbuild -B "pg_timetable_gui.lpi" 36 | ls 37 | 38 | - name: Build the application (macOS) 39 | if: ${{ matrix.operating-system == 'macos-latest' }} 40 | run: lazbuild -B --ws=cocoa "pg_timetable_gui.lpi" 41 | 42 | - name: Pack 43 | shell: bash 44 | run: | 45 | release_name="pg_timetable_gui-${{ matrix.operating-system }}" 46 | ls -a 47 | if [ "${{ matrix.operating-system }}" == "windows-latest" ]; then 48 | # Pack to zip for Windows 49 | 7z a -tzip "${release_name}.zip" "pg_timetable_gui.exe" 50 | else 51 | tar czvf "${release_name}.tar.gz" "pg_timetable_gui" 52 | fi 53 | 54 | - name: Publish the daily build 55 | uses: softprops/action-gh-release@v2 56 | with: 57 | tag_name: daily_builds 58 | files: | 59 | *.zip 60 | *.tar.gz 61 | prerelease: true -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | jobs: 9 | release: 10 | runs-on: ${{ matrix.operating-system }} 11 | strategy: 12 | matrix: 13 | operating-system: [windows-latest,ubuntu-latest,macos-latest] 14 | lazarus-versions: [stable] #[dist, stable, 2.0.12, 2.0.10, 2.0.8, 2.0.6] 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Install Lazarus 20 | uses: gcarreno/setup-lazarus@v3.2 21 | with: 22 | lazarus-version: ${{ matrix.lazarus-versions }} 23 | with-cache: true 24 | 25 | - name: Build the application 26 | if: ${{ matrix.operating-system != 'macos-latest' }} 27 | run: | 28 | lazbuild -B "pg_timetable_gui.lpi" 29 | ls 30 | 31 | - name: Build the application (macOS) 32 | if: ${{ matrix.operating-system == 'macos-latest' }} 33 | run: lazbuild -B --ws=cocoa "pg_timetable_gui.lpi" 34 | 35 | - name: Pack 36 | shell: bash 37 | run: | 38 | release_name="pg_timetable_gui-${{ matrix.target }}" 39 | ls -a 40 | if [ "${{ matrix.target }}" == "win-x64" ]; then 41 | # Pack to zip for Windows 42 | 7z a -tzip "${release_name}.zip" "pg_timetable_gui.exe" 43 | else 44 | tar czvf "${release_name}.tar.gz" "pg_timetable_gui" 45 | fi 46 | 47 | - name: Publish Release Artifacts 48 | uses: softprops/action-gh-release@v2 49 | with: 50 | files: | 51 | *.zip 52 | *.tar.gz 53 | LICENSE 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Lazarus compiler-generated binaries (safe to delete) 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | *.lrs 7 | *.res 8 | *.compiled 9 | *.dbg 10 | *.ppu 11 | *.o 12 | *.or 13 | *.a 14 | 15 | # Lazarus autogenerated files (duplicated info) 16 | *.rst 17 | *.rsj 18 | *.lrt 19 | 20 | # Lazarus local files (user-specific info) 21 | *.lps 22 | 23 | # Lazarus backups and unit output folders. 24 | # These can be changed by user in Lazarus/project options. 25 | backup/ 26 | *.bak 27 | lib/ 28 | 29 | # Application bundle for Mac OS 30 | *.app/ 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CYBERTEC PostgreSQL International GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) 2 | [![build-test](https://github.com/cybertec-postgresql/pg_timetable_gui/actions/workflows/build.yml/badge.svg)](https://github.com/cybertec-postgresql/pg_timetable_gui/actions/workflows/build.yml) 3 | [![Release](https://img.shields.io/github/v/release/cybertec-postgresql/pg_timetable_gui?include_prereleases)](https://github.com/cybertec-postgresql/pg_timetable_gui/releases) 4 | [![Github All Releases](https://img.shields.io/github/downloads/cybertec-postgresql/pg_timetable_gui/total?style=flat-square)](https://github.com/cybertec-postgresql/pg_timetable_gui/releases) 5 | 6 | 7 | # pg_timetable_gui: IDE for [pg_timetable](https://github.com/cybertec-postgresql/pg_timetable) scheduler 8 | 9 | **pg_timetable_gui** is a free cross-platform tool for developers and administrators, who need to work with advanced PostgreSQL [pg_timetable](https://github.com/cybertec-postgresql/pg_timetable) scheduler. While every operation can be done in a preferred PostgreSQL client (e.g. psql, pgadmin, dbeaber) we found that special tool may increase productivity during creating, debugging and monitoring jobs. 10 | 11 | ![pg_timetable_gui main window](res/pg_timetable_gui.png) 12 | 13 | Enhanced description of chain and task properties can be found in the official [pg_timetable manual](https://pg-timetable.readthedocs.io/en/master/). 14 | 15 | ## Controls 16 | ![connect/disconnect](res/shut-down.png) - connect/disconnect to the target PostgreSQL database 17 | 18 | ![add](res/nav-add.png) - add chain/task 19 | 20 | ![delete](res/nav-delete.png) - delete chain/task 21 | 22 | ![edit](res/nav-edit.png) - edit current chain/task 23 | 24 | ![post](res/nav-post.png) - post chain/task changes to the database 25 | 26 | ![cancel](res/nav-cancel.png) - cancel editing chain/task 27 | 28 | ![refresh](res/nav-refresh.png) - refresh chain/task list 29 | 30 | ![up](res/nav-up.png) - move task up in the chain order 31 | 32 | ![down](res/nav-down.png) - move task down in the chain order 33 | 34 | ## Main features 35 | - List database chains and tasks ✅ 36 | - Add/update/delete chains and tasks ✅ 37 | - Change task order within the chain ✅ 38 | - Execute arbitrary SQL script from file 39 | - Enhanced *cron-syntax* editor 40 | - Enhanced task editor with debugging functionality 41 | 42 | ## Contributing 43 | 44 | If you want to contribute to **pg_timetable_gui** and help make it better: 45 | - ⭐[give a star][star] to the project, 46 | - feel free to open an 🤚[issue][issue] or even 47 | - consider submitting a 📜[pull request][PR]. 48 | 49 | [issue]: https://github.com/cybertec-postgresql/pg_timetable_gui/issues 50 | [PR]: https://github.com/cybertec-postgresql/pg_timetable_gui/pulls 51 | [star]: https://github.com/cybertec-postgresql/pg_timetable_gui/stargazers 52 | 53 | ## Support 54 | 55 | For professional support, please contact [Cybertec](https://www.cybertec-postgresql.com/). 56 | 57 | ## Credits 58 | - Author: [Pavlo Golub](https://github.com/pashagolub) 59 | - Media: open-source [RemixIcons](https://remixicon.com/) set 60 | -------------------------------------------------------------------------------- /forms/fmconnect.lfm: -------------------------------------------------------------------------------- 1 | object fmConnect: TfmConnect 2 | Left = 1090 3 | Height = 349 4 | Top = 1467 5 | Width = 510 6 | BorderStyle = bsDialog 7 | Caption = 'Connect to database...' 8 | ClientHeight = 349 9 | ClientWidth = 510 10 | Color = clBtnFace 11 | DesignTimePPI = 144 12 | Font.Color = clWindowText 13 | Font.Height = -17 14 | Font.Name = 'MS Sans Serif' 15 | Position = poMainFormCenter 16 | LCLVersion = '2.0.12.0' 17 | object Bevel1: TBevel 18 | Left = 0 19 | Height = 242 20 | Top = 38 21 | Width = 510 22 | Align = alTop 23 | Shape = bsFrame 24 | end 25 | object laUser: TLabel 26 | Left = 12 27 | Height = 25 28 | Top = 98 29 | Width = 62 30 | Caption = '&User ID:' 31 | FocusControl = edUserID 32 | ParentColor = False 33 | ParentFont = False 34 | end 35 | object laPass: TLabel 36 | Left = 12 37 | Height = 25 38 | Top = 140 39 | Width = 79 40 | Caption = '&Password:' 41 | ParentColor = False 42 | ParentFont = False 43 | end 44 | object laDbName: TLabel 45 | Left = 12 46 | Height = 25 47 | Top = 54 48 | Width = 78 49 | Caption = '&Database:' 50 | FocusControl = edDatabase 51 | ParentColor = False 52 | ParentFont = False 53 | end 54 | object laHost: TLabel 55 | Left = 12 56 | Height = 25 57 | Top = 183 58 | Width = 116 59 | Caption = '&Host Name/IP:' 60 | FocusControl = edHost 61 | ParentColor = False 62 | ParentFont = False 63 | end 64 | object laPort: TLabel 65 | Left = 12 66 | Height = 25 67 | Top = 225 68 | Width = 90 69 | Caption = 'Server &Port:' 70 | FocusControl = edPort 71 | ParentColor = False 72 | ParentFont = False 73 | end 74 | object edUserID: TEdit 75 | Left = 144 76 | Height = 33 77 | Top = 94 78 | Width = 350 79 | ParentFont = False 80 | TabOrder = 1 81 | end 82 | object edDatabase: TEdit 83 | Left = 144 84 | Height = 33 85 | Top = 51 86 | Width = 350 87 | ParentFont = False 88 | TabOrder = 0 89 | end 90 | object edHost: TEdit 91 | Left = 144 92 | Height = 33 93 | Top = 180 94 | Width = 350 95 | ParentFont = False 96 | TabOrder = 3 97 | end 98 | object edPort: TEdit 99 | Left = 144 100 | Height = 33 101 | Top = 222 102 | Width = 350 103 | ParentFont = False 104 | TabOrder = 4 105 | end 106 | object btnOk: TButton 107 | Left = 265 108 | Height = 38 109 | Top = 296 110 | Width = 112 111 | Caption = '&OK' 112 | Default = True 113 | Font.Color = clWindowText 114 | Font.Height = -17 115 | Font.Name = 'MS Sans Serif' 116 | ModalResult = 1 117 | ParentFont = False 118 | TabOrder = 5 119 | end 120 | object btnCancel: TButton 121 | Left = 391 122 | Height = 38 123 | Top = 296 124 | Width = 112 125 | Cancel = True 126 | Caption = '&Cancel' 127 | Font.Color = clWindowText 128 | Font.Height = -17 129 | Font.Name = 'MS Sans Serif' 130 | ModalResult = 2 131 | ParentFont = False 132 | TabOrder = 6 133 | end 134 | object pnlHeader: TPanel 135 | Left = 0 136 | Height = 38 137 | Top = 0 138 | Width = 510 139 | Align = alTop 140 | Alignment = taLeftJustify 141 | BevelOuter = bvNone 142 | Caption = ' PostgreSQL Connection Options' 143 | Color = clGray 144 | Font.Color = clWhite 145 | Font.Height = -17 146 | Font.Name = 'MS Sans Serif' 147 | Font.Style = [fsBold] 148 | ParentColor = False 149 | ParentFont = False 150 | TabOrder = 7 151 | end 152 | object btnTest: TButton 153 | Left = 12 154 | Height = 38 155 | Top = 296 156 | Width = 113 157 | Caption = 'Test' 158 | Font.Color = clWindowText 159 | ParentFont = False 160 | TabOrder = 8 161 | end 162 | object edPasswd: TEditButton 163 | Left = 144 164 | Height = 33 165 | Top = 136 166 | Width = 350 167 | ButtonCaption = '👁️‍🗨️' 168 | ButtonWidth = 34 169 | EchoMode = emPassword 170 | MaxLength = 0 171 | NumGlyphs = 1 172 | OnButtonClick = edPasswdButtonClick 173 | ParentFont = False 174 | PasswordChar = '*' 175 | TabOrder = 2 176 | Text = 'edPasswd' 177 | end 178 | end 179 | -------------------------------------------------------------------------------- /forms/fmconnect.pas: -------------------------------------------------------------------------------- 1 | unit fmConnect; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 9 | StdCtrls, ExtCtrls, EditBtn, DB, PQConnection, SQLDB; 10 | 11 | type 12 | 13 | { TfmConnect } 14 | 15 | TfmConnect = class(TForm) 16 | Bevel1: TBevel; 17 | btnTest: TButton; 18 | edPasswd: TEditButton; 19 | laUser: TLabel; 20 | laPass: TLabel; 21 | edUserID: TEdit; 22 | laDbName: TLabel; 23 | edDatabase: TEdit; 24 | laHost: TLabel; 25 | edHost: TEdit; 26 | laPort: TLabel; 27 | edPort: TEdit; 28 | btnOk: TButton; 29 | btnCancel: TButton; 30 | pnlHeader: TPanel; 31 | procedure edPasswdButtonClick(Sender: TObject); 32 | private 33 | Database: TPQConnection; 34 | function Edit: boolean; 35 | public 36 | procedure GetDatabaseProperty(Db: TPQConnection); 37 | procedure SetDatabaseProperty(Db: TPQConnection); 38 | end; 39 | 40 | function EditDatabase(ADatabase: TPQConnection): boolean; 41 | 42 | implementation 43 | 44 | {$R *.lfm} 45 | 46 | function EditDatabase(ADatabase: TPQConnection): boolean; 47 | begin 48 | with TfmConnect.Create(Application) do 49 | try 50 | Database := ADatabase; 51 | Result := Edit; 52 | finally 53 | Free; 54 | end; 55 | end; 56 | 57 | procedure TfmConnect.edPasswdButtonClick(Sender: TObject); 58 | begin 59 | with (Sender as TEditButton) do 60 | if EchoMode = emNormal then 61 | EchoMode := emPassword 62 | else 63 | EchoMode := emNormal; 64 | end; 65 | 66 | function TfmConnect.Edit: boolean; 67 | begin 68 | GetDatabaseProperty(Database); 69 | Result := False; 70 | if ShowModal() = mrOk then 71 | begin 72 | SetDatabaseProperty(Database); 73 | Result := True; 74 | end; 75 | end; 76 | 77 | procedure TfmConnect.GetDatabaseProperty(Db: TPQConnection); 78 | begin 79 | edDatabase.Text := DB.DatabaseName; 80 | edUserID.Text := DB.UserName; 81 | edPasswd.Text := DB.Password; 82 | edHost.Text := DB.HostName; 83 | edPort.Text := DB.Params.Values['port']; 84 | if edPort.Text = '' then 85 | edPort.Text := '5432'; 86 | end; 87 | 88 | procedure TfmConnect.SetDatabaseProperty(Db: TPQConnection); 89 | begin 90 | DB.DatabaseName := edDatabase.Text; 91 | DB.UserName := edUserID.Text; 92 | DB.Password := edPasswd.Text; 93 | DB.HostName := edHost.Text; 94 | DB.Params.Add(Format('port=%d', [StrToIntDef(edPort.Text, 5432)])); 95 | end; 96 | 97 | end. 98 | 99 | -------------------------------------------------------------------------------- /forms/fmmain.lfm: -------------------------------------------------------------------------------- 1 | object fmMain: TfmMain 2 | Left = 654 3 | Height = 1054 4 | Top = 325 5 | Width = 1524 6 | Caption = 'pg_timetable IDE' 7 | ClientHeight = 1024 8 | ClientWidth = 1524 9 | DesignTimePPI = 144 10 | Menu = menuMain 11 | OnCloseQuery = FormCloseQuery 12 | LCLVersion = '2.2.4.0' 13 | object pcEditors: TPageControl 14 | Left = 0 15 | Height = 1024 16 | Top = 0 17 | Width = 1524 18 | ActivePage = tsOverview 19 | Align = alClient 20 | Images = imglTabs 21 | TabIndex = 0 22 | TabOrder = 0 23 | OnChange = pcEditorsChange 24 | object tsOverview: TTabSheet 25 | Caption = 'Overview' 26 | ClientHeight = 986 27 | ClientWidth = 1516 28 | ImageIndex = 0 29 | object pnlAdmin: TPanel 30 | Left = 0 31 | Height = 986 32 | Top = 0 33 | Width = 1516 34 | Align = alClient 35 | BevelOuter = bvNone 36 | ClientHeight = 986 37 | ClientWidth = 1516 38 | ParentColor = False 39 | TabOrder = 0 40 | object pnlChains: TPanel 41 | Left = 0 42 | Height = 436 43 | Top = 0 44 | Width = 1516 45 | Align = alTop 46 | BevelOuter = bvNone 47 | BorderStyle = bsSingle 48 | Caption = 'pnlChains' 49 | ClientHeight = 432 50 | ClientWidth = 1512 51 | ParentColor = False 52 | TabOrder = 0 53 | object gridChains: TDBGrid 54 | Left = 0 55 | Height = 390 56 | Top = 42 57 | Width = 1512 58 | Align = alClient 59 | AlternateColor = 16577773 60 | AutoAdvance = aaRight 61 | AutoEdit = False 62 | AutoFillColumns = True 63 | CellHintPriority = chpAll 64 | Color = clWindow 65 | Columns = < 66 | item 67 | MinSize = 9 68 | MaxSize = 26 69 | Title.Alignment = taCenter 70 | Title.Caption = 'Chain Name' 71 | Width = 264 72 | FieldName = 'chain_name' 73 | end 74 | item 75 | Alignment = taCenter 76 | ButtonStyle = cbsCheckboxColumn 77 | MinSize = 240 78 | MaxSize = 240 79 | SizePriority = 0 80 | Title.Alignment = taCenter 81 | Title.Caption = 'Live' 82 | Width = 100 83 | FieldName = 'live' 84 | end 85 | item 86 | ButtonStyle = cbsEllipsis 87 | PickList.Strings = ( 88 | '* * * * *' 89 | '@every ' 90 | '@after ' 91 | '@reboot' 92 | ) 93 | Title.Alignment = taCenter 94 | Title.Caption = 'Run at' 95 | Width = 264 96 | FieldName = 'run_at' 97 | end 98 | item 99 | Title.Alignment = taCenter 100 | Title.Caption = 'Client' 101 | Width = 264 102 | FieldName = 'client_name' 103 | end 104 | item 105 | SizePriority = 0 106 | Title.Alignment = taCenter 107 | Title.Caption = 'Exclusive' 108 | Width = 150 109 | FieldName = 'exclusive_execution' 110 | end 111 | item 112 | SizePriority = 0 113 | Title.Alignment = taCenter 114 | Title.Caption = 'Self Destruct' 115 | Width = 150 116 | FieldName = 'self_destruct' 117 | end 118 | item 119 | SizePriority = 0 120 | Title.Alignment = taCenter 121 | Title.Caption = 'Max Instances' 122 | Width = 150 123 | FieldName = 'max_instances' 124 | end 125 | item 126 | SizePriority = 0 127 | Title.Alignment = taCenter 128 | Title.Caption = 'Timeout (ms)' 129 | Width = 150 130 | FieldName = 'timeout' 131 | end> 132 | DataSource = dmPgEngine.dsChains 133 | DefaultRowHeight = 36 134 | Flat = True 135 | Options = [dgEditing, dgTitles, dgIndicator, dgColumnResize, dgColumnMove, dgColLines, dgRowLines, dgTabs, dgAlwaysShowSelection, dgConfirmDelete, dgCancelOnExit, dgHeaderHotTracking, dgHeaderPushedLook, dgDisableInsert, dgCellHints, dgTruncCellHints, dgCellEllipsis, dgDisplayMemoText] 136 | ParentFont = False 137 | Scrollbars = ssAutoVertical 138 | ShowHint = True 139 | TabAdvance = aaRight 140 | TabOrder = 0 141 | TitleImageList = imglGrids 142 | OnEditButtonClick = gridChainsEditButtonClick 143 | OnEditingDone = gridChainsEditingDone 144 | OnTitleClick = gridChainsTitleClick 145 | end 146 | object pnlMainToolbar: TPanel 147 | Left = 0 148 | Height = 42 149 | Top = 0 150 | Width = 1512 151 | Align = alTop 152 | BevelOuter = bvNone 153 | ClientHeight = 42 154 | ClientWidth = 1512 155 | ParentColor = False 156 | ParentShowHint = False 157 | ShowHint = True 158 | TabOrder = 1 159 | object toolbarMain: TToolBar 160 | Left = 0 161 | Height = 42 162 | Top = 0 163 | Width = 68 164 | Align = alLeft 165 | ButtonHeight = 40 166 | ButtonWidth = 40 167 | DisabledImages = imglToolbarsDisabled 168 | EdgeBorders = [] 169 | Images = imglToolbars 170 | TabOrder = 0 171 | object btnConnect: TToolButton 172 | Left = 1 173 | Top = 0 174 | Action = acConnect 175 | end 176 | end 177 | object toolbarChains: TToolBar 178 | Left = 68 179 | Height = 42 180 | Top = 0 181 | Width = 300 182 | Align = alLeft 183 | ButtonHeight = 40 184 | ButtonWidth = 40 185 | DisabledImages = imglToolbarsDisabled 186 | EdgeBorders = [] 187 | Images = imglToolbars 188 | TabOrder = 1 189 | object btnChainAdd: TToolButton 190 | Left = 1 191 | Top = 0 192 | Action = acChainAdd 193 | end 194 | object btnChainDelete: TToolButton 195 | Left = 41 196 | Top = 0 197 | Action = acChainDelete 198 | end 199 | object btnChainEdit: TToolButton 200 | Left = 81 201 | Top = 0 202 | Action = acChainEdit 203 | end 204 | object btnChainPost: TToolButton 205 | Left = 121 206 | Top = 0 207 | Action = acChainPost 208 | end 209 | object btnChainCancel: TToolButton 210 | Left = 161 211 | Top = 0 212 | Action = acChainCancel 213 | end 214 | object btnChainRefresh: TToolButton 215 | Left = 201 216 | Top = 0 217 | Action = acChainRefresh 218 | end 219 | end 220 | object toolbarRun: TToolBar 221 | Left = 368 222 | Height = 42 223 | Top = 0 224 | Width = 300 225 | Align = alLeft 226 | ButtonHeight = 40 227 | ButtonWidth = 40 228 | DisabledImages = imglToolbarsDisabled 229 | EdgeBorders = [] 230 | Images = imglToolbars 231 | TabOrder = 2 232 | object btnChainRun: TToolButton 233 | Left = 1 234 | Top = 0 235 | Action = acChainRun 236 | end 237 | end 238 | end 239 | end 240 | object splitChain: TSplitter 241 | Cursor = crVSplit 242 | Left = 0 243 | Height = 12 244 | Top = 436 245 | Width = 1516 246 | Align = alTop 247 | Color = clScrollBar 248 | DoubleBuffered = True 249 | ParentColor = False 250 | ParentDoubleBuffered = False 251 | ResizeAnchor = akTop 252 | ResizeStyle = rsPattern 253 | end 254 | object pnlDetails: TPanel 255 | Left = 0 256 | Height = 538 257 | Top = 448 258 | Width = 1516 259 | Align = alClient 260 | BevelOuter = bvNone 261 | ClientHeight = 538 262 | ClientWidth = 1516 263 | TabOrder = 2 264 | object gridTasks: TDBGrid 265 | Left = 0 266 | Height = 498 267 | Top = 40 268 | Width = 1516 269 | Align = alClient 270 | AlternateColor = 16577773 271 | AutoFillColumns = True 272 | Color = clWindow 273 | Columns = < 274 | item 275 | Title.Caption = 'Task Name' 276 | Width = 339 277 | FieldName = 'task_name' 278 | end 279 | item 280 | MinSize = 9 281 | MaxSize = 201 282 | Title.Caption = 'task_id' 283 | Visible = False 284 | FieldName = 'task_id' 285 | end 286 | item 287 | ButtonStyle = cbsPickList 288 | MinSize = 9 289 | MaxSize = 201 290 | PickList.Strings = ( 291 | 'SQL' 292 | 'PROGRAM' 293 | 'BUILTIN' 294 | ) 295 | SizePriority = 0 296 | Title.ImageIndex = 1 297 | Title.Caption = 'Kind' 298 | Width = 120 299 | FieldName = 'kind' 300 | end 301 | item 302 | ButtonStyle = cbsEllipsis 303 | MinSize = 9 304 | MaxSize = 99 305 | SizePriority = 2 306 | Title.Caption = 'Command' 307 | Width = 339 308 | FieldName = 'command' 309 | end 310 | item 311 | MinSize = 9 312 | MaxSize = 201 313 | SizePriority = 0 314 | Title.Alignment = taCenter 315 | Title.Caption = 'Ignore Error' 316 | Width = 120 317 | FieldName = 'ignore_error' 318 | end 319 | item 320 | MinSize = 9 321 | MaxSize = 201 322 | SizePriority = 0 323 | Title.Alignment = taCenter 324 | Title.Caption = 'Autonomous' 325 | Width = 120 326 | FieldName = 'autonomous' 327 | end 328 | item 329 | MinSize = 9 330 | MaxSize = 201 331 | SizePriority = 0 332 | Title.Alignment = taCenter 333 | Title.Caption = 'Run As' 334 | Width = 120 335 | FieldName = 'run_as' 336 | end 337 | item 338 | MinSize = 9 339 | MaxSize = 201 340 | Title.Caption = 'Connection String' 341 | Width = 339 342 | FieldName = 'connect_string' 343 | end> 344 | DataSource = dmPgEngine.dsTasks 345 | DefaultRowHeight = 36 346 | Flat = True 347 | Options = [dgEditing, dgTitles, dgIndicator, dgColumnResize, dgColumnMove, dgColLines, dgRowLines, dgTabs, dgAlwaysShowSelection, dgConfirmDelete, dgCancelOnExit, dgHeaderHotTracking, dgHeaderPushedLook, dgDisableInsert, dgCellHints, dgTruncCellHints, dgCellEllipsis, dgDisplayMemoText] 348 | ParentFont = False 349 | Scrollbars = ssAutoVertical 350 | ShowHint = True 351 | TabOrder = 0 352 | OnDrawColumnCell = gridTasksDrawColumnCell 353 | OnEditButtonClick = gridTasksEditButtonClick 354 | OnSelectEditor = gridTasksSelectEditor 355 | end 356 | object toolbarTasks: TToolBar 357 | Left = 0 358 | Height = 40 359 | Top = 0 360 | Width = 1516 361 | ButtonHeight = 40 362 | ButtonWidth = 40 363 | DisabledImages = imglToolbarsDisabled 364 | EdgeBorders = [] 365 | Images = imglToolbars 366 | ParentShowHint = False 367 | ShowHint = True 368 | TabOrder = 1 369 | object btnTaskMoveUp: TToolButton 370 | Left = 1 371 | Top = 0 372 | Action = acMoveTaskUp 373 | end 374 | object btnTaskMoveDown: TToolButton 375 | Left = 41 376 | Top = 0 377 | Action = acMoveTaskDown 378 | end 379 | object btnTaskAdd: TToolButton 380 | Left = 81 381 | Top = 0 382 | Action = acTaskAdd 383 | end 384 | object btnTaskSep1: TToolButton 385 | Left = 121 386 | Height = 40 387 | Top = 0 388 | AutoSize = True 389 | Caption = 'btnTaskSep1' 390 | Style = tbsSeparator 391 | end 392 | object btnTaskDelete: TToolButton 393 | Left = 129 394 | Top = 0 395 | Action = acTaskDelete 396 | end 397 | object btnTaskEdit: TToolButton 398 | Left = 169 399 | Top = 0 400 | Action = acTaskEdit 401 | end 402 | object btnTaskPost: TToolButton 403 | Left = 209 404 | Top = 0 405 | Action = acTaskPost 406 | end 407 | object btnTaskCancel: TToolButton 408 | Left = 249 409 | Top = 0 410 | Action = acTaskCancel 411 | end 412 | object btnTaskRefresh: TToolButton 413 | Left = 289 414 | Top = 0 415 | Action = acTaskRefresh 416 | end 417 | end 418 | end 419 | end 420 | end 421 | object tsLog: TTabSheet 422 | Caption = 'Log' 423 | ClientHeight = 986 424 | ClientWidth = 1516 425 | ImageIndex = 1 426 | object mmLog: TMemo 427 | Left = 4 428 | Height = 978 429 | Top = 4 430 | Width = 1508 431 | Align = alClient 432 | BorderSpacing.Left = 2 433 | BorderSpacing.Top = 2 434 | BorderSpacing.Right = 2 435 | BorderSpacing.Bottom = 2 436 | BorderSpacing.Around = 2 437 | BorderSpacing.InnerBorder = 2 438 | Font.Name = 'Consolas' 439 | ParentFont = False 440 | ScrollBars = ssVertical 441 | TabOrder = 0 442 | end 443 | end 444 | end 445 | object imglGrids: TImageList 446 | Height = 24 447 | Width = 24 448 | Left = 120 449 | Top = 936 450 | Bitmap = { 451 | 4C7A0500000018000000180000003E0200000000000078DAED5A018DC5200C9D 452 | 04242001099330099330097380042420010993F02520E1DF7E52720D198C1676 453 | F9BBF19226973BF680B67BD0DE86A18309B1DBB8DBBA9BDB6DDBCD837D7EB6BB 454 | 691843E5D5C0F326982EE096B0AEF08C83F57FD6A8606E01E3A6DD16D84B18BF 455 | C1DF5330689C22EC59A1795C665CF08964C44BA27DA480F72989EBB704FE6016 456 | 7C3CA2F982FF15F27F9C0B67FC2B31777CF4CC197FC00431FFC4EC85B85EF03B 457 | 037B1089E753FC82F93EF6F83E3BBEB6417CDD89866C15F12D5D9766C4D730FC 458 | 3AC15C47F10D67D8CC8C57C7FF8225E664CE5C8176D6DA99B671F114FE057440 459 | 5CC02F908E52E7285D3F770E8AFF397350F92DE2978DFD43E5A6F073B829FC6B 460 | 82FB4C0F28FE97049D7A8A3E5CA5CF5B436EDB8FEBDB41C11DD632EB7B6A3F45 461 | 37E01ED1BDF98D6A227C2F9F99BC5BC4AB12FD124ABF25F6858FEAB71873B43F 462 | 9389CD4CE08DB144B1990EC6ACCCDE12D67287EAE714FF9AF1DB06FE48F96165 463 | F08B44AFCF1FCCC1E18F7B7223F28369C01FF2633CE8B5F986FC32CAFF56FC06 464 | BD6312F66112E70837BEA99A5D35E00F7398E1B7FFEC2AF2D331F517DF27D784 465 | A671F4F748B3A6933304EBE25CA1B3B9B3EA95E927C6FDC22DB3668A2E6AA27E 466 | 974237EA27959CEF8EA9DF1DF7C6527017E2D40D21B7C23BA11AF38BE85DF70D 467 | DF0B91E8ABB946FCFA0F6BA456F5E737F35FDD7FFC86FEE693E3DBF3F3BEF9D9 468 | D17177F4F3A5F3F7F3A5E7E75DF8CD50F73D62098EFE2F94FA0E915BDFF921DF 469 | 77ADAD4FC782DE552DC2F79A1D04FC001EF4BEDA 470 | } 471 | end 472 | object menuMain: TMainMenu 473 | Left = 15 474 | Top = 936 475 | object miFile: TMenuItem 476 | Caption = 'File' 477 | object miConnect: TMenuItem 478 | Action = acConnect 479 | AutoCheck = True 480 | end 481 | object miClose: TMenuItem 482 | Caption = 'Close' 483 | OnClick = miCloseClick 484 | end 485 | end 486 | object miView: TMenuItem 487 | Caption = 'View' 488 | object miLog: TMenuItem 489 | AutoCheck = True 490 | Caption = 'Log' 491 | Checked = True 492 | OnClick = miLogClick 493 | end 494 | end 495 | object miHelp: TMenuItem 496 | Caption = 'Help' 497 | object miAbout: TMenuItem 498 | Caption = 'About...' 499 | end 500 | end 501 | end 502 | object alToolbars: TActionList 503 | Images = imglToolbars 504 | Left = 224 505 | Top = 941 506 | object acConnect: TAction 507 | Category = 'General' 508 | AutoCheck = True 509 | Caption = 'Connect to PostgreSQL...' 510 | Hint = 'Connect to the PostgreSQL server to manage pg_timetable chains and tasks' 511 | ImageIndex = 0 512 | OnExecute = acConnectClick 513 | ShortCut = 24643 514 | end 515 | object acMoveTaskUp: TAction 516 | Category = 'Tasks' 517 | Caption = 'Move Task Up' 518 | DisableIfNoHandler = False 519 | Hint = 'Move selected task up' 520 | ImageIndex = 2 521 | OnExecute = acMoveTaskUpExecute 522 | OnUpdate = acTaskToolbarUpdate 523 | end 524 | object acMoveTaskDown: TAction 525 | Category = 'Tasks' 526 | Caption = 'acMoveTaskDown' 527 | DisableIfNoHandler = False 528 | Hint = 'Move selected task down' 529 | ImageIndex = 1 530 | OnExecute = acMoveTaskDownExecute 531 | OnUpdate = acTaskToolbarUpdate 532 | end 533 | object acTaskDelete: TAction 534 | Category = 'Tasks' 535 | Caption = 'acTaskDelete' 536 | DisableIfNoHandler = False 537 | Hint = 'Delete selected task' 538 | ImageIndex = 4 539 | OnExecute = acTaskDeleteExecute 540 | OnUpdate = acTaskToolbarUpdate 541 | end 542 | object acTaskAdd: TAction 543 | Category = 'Tasks' 544 | Caption = 'acTaskAdd' 545 | DisableIfNoHandler = False 546 | Hint = 'Add new task' 547 | ImageIndex = 3 548 | OnExecute = acTaskAddExecute 549 | OnUpdate = acTaskToolbarUpdate 550 | end 551 | object acTaskEdit: TAction 552 | Category = 'Tasks' 553 | Caption = 'acTaskEdit' 554 | DisableIfNoHandler = False 555 | Hint = 'Edit selected task' 556 | ImageIndex = 5 557 | OnExecute = acTaskEditExecute 558 | OnUpdate = acTaskToolbarUpdate 559 | end 560 | object acTaskPost: TAction 561 | Category = 'Tasks' 562 | Caption = 'acTaskPost' 563 | DisableIfNoHandler = False 564 | Hint = 'Save changes to selected task' 565 | ImageIndex = 6 566 | OnExecute = acTaskPostExecute 567 | OnUpdate = acTaskToolbarUpdate 568 | end 569 | object acTaskCancel: TAction 570 | Category = 'Tasks' 571 | Caption = 'acTaskCancel' 572 | DisableIfNoHandler = False 573 | Hint = 'Cancel changes to selected task' 574 | ImageIndex = 7 575 | OnExecute = acTaskCancelExecute 576 | OnUpdate = acTaskToolbarUpdate 577 | end 578 | object acTaskRefresh: TAction 579 | Category = 'Tasks' 580 | Caption = 'acTaskRefresh' 581 | DisableIfNoHandler = False 582 | Hint = 'Refresh tasks for selected chain' 583 | ImageIndex = 8 584 | OnExecute = acTaskRefreshExecute 585 | OnUpdate = acTaskToolbarUpdate 586 | end 587 | object acChainAdd: TAction 588 | Category = 'Chains' 589 | Caption = 'acChainAdd' 590 | DisableIfNoHandler = False 591 | Hint = 'Add new chain' 592 | ImageIndex = 3 593 | OnExecute = acChainAddExecute 594 | OnUpdate = acChainToolbarUpdate 595 | end 596 | object acChainDelete: TAction 597 | Category = 'Chains' 598 | Caption = 'acChainDelete' 599 | Hint = 'Delete selected chain' 600 | ImageIndex = 4 601 | OnExecute = acChainDeleteExecute 602 | OnUpdate = acChainToolbarUpdate 603 | end 604 | object acChainEdit: TAction 605 | Category = 'Chains' 606 | Caption = 'acChainEdit' 607 | Hint = 'Edit selected chain' 608 | ImageIndex = 5 609 | OnExecute = acChainEditExecute 610 | OnUpdate = acChainToolbarUpdate 611 | end 612 | object acChainPost: TAction 613 | Category = 'Chains' 614 | Caption = 'acChainPost' 615 | Hint = 'Save changes in selected chain' 616 | ImageIndex = 6 617 | OnExecute = acChainPostExecute 618 | OnUpdate = acChainToolbarUpdate 619 | end 620 | object acChainCancel: TAction 621 | Category = 'Chains' 622 | Caption = 'acChainCancel' 623 | Hint = 'Cancel changes in selected chain' 624 | ImageIndex = 7 625 | OnExecute = acChainCancelExecute 626 | OnUpdate = acChainToolbarUpdate 627 | end 628 | object acChainRefresh: TAction 629 | Category = 'Chains' 630 | Caption = 'acChainRefresh' 631 | Hint = 'Refresh chain list from database' 632 | ImageIndex = 8 633 | OnExecute = acChainRefreshExecute 634 | OnUpdate = acChainToolbarUpdate 635 | end 636 | object acChainRun: TAction 637 | Category = 'Chains' 638 | Caption = 'Run' 639 | ImageIndex = 9 640 | OnUpdate = acChainToolbarUpdate 641 | ShortCut = 120 642 | end 643 | end 644 | object imglToolbars: TImageList 645 | Height = 24 646 | Width = 24 647 | Left = 96 648 | Top = 120 649 | Bitmap = { 650 | 4C7A0A0000001800000018000000C20300000000000078DAED9C8D8DEB200C80 651 | 3D424660848C9011320223304237C8081DA123640446C80819A1AF4F02D5970B 652 | 60C0567FCE9690EE9AF6730063CC2FC09F927B48EFCE778FB456F06F8F74A960 653 | 47CE48E01BF47949C7F4487BF8AEAD78FF093D9B327C5F788F5CF9DBF06C4F3C 654 | 9FD1F3A1B17EB74CDE6F84322CF1631E6E27CFD6F06CEEE08FE1F996C99BE9E0 655 | 0F993AD809FC920C9977F004FB2A49AE7CAEE199EB6CF7A9FA9D0BF64B291B5F 656 | B011DF9187F8EE3ED37EB02FB1156C1BF2BD13EA0FFB37472813876CAFC5876E 657 | E1FF0931C750C61ED599CB944BCAD63CD2934AFEC48F4365D95E51FBDEC3DF6B 658 | 7836808A8AC66FCAFF6E3ED5CF99E01F6BE25B1B7E43F1D32ED3479EF12DEABF 659 | 16621E96848E23BF859DD381F93DEC940EFC772FFB4CC71DF5911CEC9C8E85D9 660 | F6174136D6B1808A8A8A84B8CEF125D7B8A987CDADC365C6468E916D0F7354BD 661 | 3A5CA17FECD1E188FD7B8B8EDAF864AA1CF39BD0BFD6C45753F8CDD859DF1A3F 662 | 2BFF9DF92A2A2AEA1F94FF5AFEBD31BD0B5FEB57F92A2A2A7F5706A0AD790C8D 663 | ECFF7B187CE1F7F17B6B033FAE8DA7744476FC8E61D4D1CBCEE9E062A77470B2 664 | CF7470B38FE57D27D8552BDB13ECAA876D88B6DBCAAE691FAD6C0E1D032A6B43 665 | FC5E4B1E0CA31F5451F9A6BE7F09FDEE28C0BEC2CFB5F25180BDC3CFFD752333 666 | 3BEE918CFB6A3701F608CF7D775741F6DA11C729FB3BD820CCC6F30B12ECE3FC 667 | 05371BF371E2628330FB1BE4CE945EC557515169130BE7E75EB8D871CFA61364 668 | 2F6FC01E81B6CFA6853D40FE7C1F4799A4F6267396774A07675D1E7548D8C902 669 | 727B92CF74E8BE6115151E71507706B2961DDBAC1164DB0F6597D206CF33F5D4 670 | F25B3AC613AE4147AE7C4C18835F0F634D4E1D38DED83BF24199B79B1AEC7901 671 | FA795E3C3772116A93F1FCB6548C9B3B5BFF09FCA9D24E6BE52218C34C403F9F 672 | DEC2DE986D7308DC05B5DD1BD0E6A26A7D5BEDD9772A730DEF3F828A4ABEFD5C 673 | 82BD7878AE1B5D217F570AA5DFBB01ED7E86DA186C46EDD287F71FE1B97F600E 674 | 9F6DD076466840DC52BBAC3917361D74D4FAFE523CB076F84007F9753C7CBF4A 675 | 6BCC9CBBFF07C762AD6B7D3613431C6DD134DA74AA8FDFE0F7DA05670C11EDBD 676 | 653DE476D23F1CA5E7FE1F73D2A653F5DB3ABF58B20F8EFB7F4A71BDEFCCC325 677 | 513667E53837E661AAF05717A25FD81A7DC9D13FC7BD44069545EB78967AFF4F 678 | EF1E0E0BBFEFFF89B195055D9F5479FD3C99A40D469BB7827CECD70741BE445E 679 | 527E872B2FA5719E15E473E4A5661C3C09F36721FEDA111353E60624EAB7E79D 680 | 4BEDCB09B52FAE773EF36F92F7861850F948F907A43935AA 681 | } 682 | end 683 | object imglToolbarsDisabled: TImageList 684 | Height = 24 685 | Width = 24 686 | Left = 96 687 | Top = 209 688 | Bitmap = { 689 | 4C7A0A0000001800000018000000710400000000000078DAED5CEB8DDB300CBE 690 | 113A4247E8081DA12374848CD00DEEC725EECF00CDE37E76848CE0113C8246B8 691 | 8AB6E4D08E1E14455D733912301040CA473D288A22293D3D7D1E3A1F776FF0DD 692 | 3BFEF9B8DD9C8FDD858A7F3EEDFE9E0E2FBFE8D813CEE9B4FB96C37FDDFFFE3A 693 | D7CFF0381C5EBEDB7A66ACFB67F793DA7EF7BFB10C7E27C6B74FB52335FED01E 694 | 576E82FF3D743F7CF97EFFFC8533BFB66C88F6DDCE516E0C73F8731F2CD6ED7F 695 | BBCB5866FBC1C6B7F2E0EA0CB1BE813C70F1615C6373E0E526859F2384FF1693 696 | 9D947CE528353EAFC7DD7E2ADB6EEAD67D647E917C568C4D9F9291B99CD107A4 697 | 57FAD8FA59E892C01AC9C83DC887C9CD1FD66FB97E4C6332D63714FD16E601EB 698 | 62BBF1ED02CC514EA6F9EAFD9C419DD8B82464AD477C625FBFD6E345326DC7D6 699 | C9EE706D2BFCEE2E5056D2662525B5DF14FF23E253F59CDBC38612FBD6ED5F03 700 | 454F2FECF3158F103EDA1B6D59F74C1B87EE39C4638DCFC14EF1C0F835D8311E 701 | ABDF55D8211EE813C14EF390C10EF390C55EF26883ADA4A4F60FEC0DFCF3A5D4 702 | B9A90E5B9647185B86C77A2FC63EAA5A1EA17D1EEF8F353C6236C47A7FE7F028 703 | B54F300FCA99DFD95786E13F3435E760B59F15FFDEF1959494D4BFA4F8FF179F 704 | E0930F7EF782AFF3ABF84A4A4A9F9720DE41897970E2BF635C7DCA77E953FFBF 705 | D6BBCD75CAE25F63E3411EA80D639DD2BC92148F5AEC140F29EC180F49EC080F 706 | 51ECC078BFE5E4AA02BBCFC9550D368C094576B9D825EB838B2DC103FD372927 707 | B81EA70F1419A4EA4125A547DAFB5DACF652EB330FAEBB3917728A954BF15861 708 | 1B9C5F57CBE306DBE548CE79B5819CCF5A6C97E337E5D5DAF256D8D007AE1DA7 709 | D88F810DD4127BE1B76A80BDF65F4863C7FD6E32D8617C39EC4720AEDF93EA07 710 | 6D8DAFA4A4C4A331BF2670EF450C7BCED994CD6D92CC3595C0867D919267C3C1 711 | 76E77193CBE591CE1B961EEF188F5679C380DB424E5679C346F38695943E8AAD 712 | BBDD94DC812CC5F66B56D20FBFC6966EFF7B6113BEC1DFA9A78E5FE49E08F1A3 713 | E6DEA6F7C6F99C0F39B4F60CBEF4C3D1626C541ED8DE28B5AD163C087E3B9C43 714 | 5C321FD4FBBCD83752F29F22F9F3F7B71BD9B8A9BBF51F01FF3AC765B160B22D 715 | 6DE7B5950D83DEBD3035EF0B24B00749D984B99C70C77568BC5C527C510CFD56 716 | 74F79D8E097E79BBC685E3174A8F4520E7D3DA1FE338BD8F1B8DFA3EF1560A69 717 | DF5BE62444DF6728B5C1DC5E61E6F71D6CFB81DF9C3F60CB9D3E1B3877847C5C 718 | 1D3072EBB2E45E18D67D257EEDC51DA1C47A1EE3954C1D88FA3124F7673BEE5C 719 | 9B39F5FE0FB6C5B8B1BEE4FB3F2B59E4F401BD6F63627D2BB527A9368497774E 720 | 3C24B00E43F8ECF77FF0BB357E4DC6E797E75FCCC987D0FB3F49BBBEE6FD1F64 721 | 63F5A47164E85E6FB790CF90045DE1EA0F4C5DB2D0CF3E9708FAE9C7827B9EA5 722 | BFFF5397C3117EFFC7D956FAFE8FD21DF8C95ACAE09C3FD4CC1787F664BBCEA4 723 | FB123A1B49F625A677A4FA923BE7D5F685729EACE94BC93998637B149DB3197B 724 | 2A0DBBBB54D8C459DF409BF9E5B739B7BE2463C82DDA1CD26F2DDF0D916EB3D2 725 | FBD13FC400676E 726 | } 727 | end 728 | object imglTabs: TImageList 729 | Height = 24 730 | Width = 24 731 | Left = 728 732 | Top = 6 733 | Bitmap = { 734 | 4C7A0400000018000000180000005E0200000000000078DAED58818D842010B4 735 | 044BA0044BB0044AA0044AA0034AB0044BB0044AB0044BB87F134C361B587601 736 | 7377FF92909C8A033B3B0CEB0DC3D304ED55E8D271EFC22FDDE78EFB74FCBBF9 737 | B90BBF559F77E37FABFE3F8DC75AFD73F9F954FC6FF79FBFAEFFA77D675B19DA 738 | C0DD76F4FFD639A4FA95CED182CF99A315BF34470FFC1AFF9C7EFB08AECFDFAA 739 | 933F9FD8C76F0F11778C3A3EE2B316FC0BFBBCB7801896780FCF21C1CF610FC4 740 | 1C52FCEB7A4B8C85F8BA819FD41C29EC96FCBE627E55E42924B087467D2AA4CF 741 | B963FD3074F2AF07BFADFEE1F65C0B1DB0D7A7CCB8A59D7BD645EF519D713DF0 742 | EAABBB0ED833D2D406BCF3F23CD3B06688A391DFEDE0399733CCC511EB8D3133 743 | DE0A389B125C70D784399B32F830664FACBB14F34C8CC73153F933022E712C54 744 | CC295D4D4C3D2AB43ECC19E6C210E7682E565FE00C738173A03373788461129C 745 | 6D09AE76F0CE52C801D669C8C43CA1EF296E0E202707E2BE660F621DDB026739 746 | 2EC6C8A727B493D3E354D023D6AB25B8A77CCB16741308ED507AB4092E6BF76E 747 | C9B7E64A1F1C989C49B990E8B5960BE9D9F6B4F7D5583A9EE35BF43727F435AE 748 | AFE2EE1BB509F7AA8BFBD724FC4FAA2F55F88EBF62DB2AEBFF9519FF08E2D082 749 | B54BE2F6C2DA5727623684BF6B2147167103FF5331047E2034B815D603CF8629 750 | 51E3E47235020D5894B323C3F38EDE0F446C2693CB00B409B1AE3341A1B8F68C 751 | 165C26F72A53EBFA44AC81A89D57466C70BD4EA8C5A5508F3BF46C15EE2527F0 752 | A914FF1C7F7C31DF71C4FF7E1CAFC9D51CB09E3A2A7C7F4C7C4F68D03935A0B4 753 | AEC2BD57DD03CFC1106331FFAD3EF90115EF7E4B 754 | } 755 | end 756 | end 757 | -------------------------------------------------------------------------------- /forms/fmmain.pas: -------------------------------------------------------------------------------- 1 | unit fmMain; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, Menus, 9 | StdCtrls, DBGrids, DBCtrls, ExtCtrls, uObjects, DB, Grids, ActnList, Buttons, 10 | frameTaskCommandEditor, frameCronEditor; 11 | 12 | type 13 | 14 | { TfmMain } 15 | 16 | TfmMain = class(TForm) 17 | acConnect: TAction; 18 | acMoveTaskUp: TAction; 19 | acMoveTaskDown: TAction; 20 | acTaskDelete: TAction; 21 | acTaskAdd: TAction; 22 | acTaskEdit: TAction; 23 | acTaskPost: TAction; 24 | acTaskCancel: TAction; 25 | acTaskRefresh: TAction; 26 | acChainAdd: TAction; 27 | acChainDelete: TAction; 28 | acChainEdit: TAction; 29 | acChainPost: TAction; 30 | acChainCancel: TAction; 31 | acChainRefresh: TAction; 32 | acChainRun: TAction; 33 | alToolbars: TActionList; 34 | btnChainRun: TToolButton; 35 | btnTaskMoveUp: TToolButton; 36 | gridTasks: TDBGrid; 37 | gridChains: TDBGrid; 38 | imglTabs: TImageList; 39 | imglToolbarsDisabled: TImageList; 40 | imglToolbars: TImageList; 41 | imglGrids: TImageList; 42 | miLog: TMenuItem; 43 | miView: TMenuItem; 44 | miConnect: TMenuItem; 45 | menuMain: TMainMenu; 46 | miFile: TMenuItem; 47 | miClose: TMenuItem; 48 | miHelp: TMenuItem; 49 | miAbout: TMenuItem; 50 | mmLog: TMemo; 51 | pnlAdmin: TPanel; 52 | pcEditors: TPageControl; 53 | pnlMainToolbar: TPanel; 54 | pnlChains: TPanel; 55 | pnlDetails: TPanel; 56 | splitChain: TSplitter; 57 | toolbarRun: TToolBar; 58 | tsLog: TTabSheet; 59 | tsOverview: TTabSheet; 60 | toolbarChains: TToolBar; 61 | toolbarMain: TToolBar; 62 | btnConnect: TToolButton; 63 | toolbarTasks: TToolBar; 64 | btnTaskMoveDown: TToolButton; 65 | btnTaskAdd: TToolButton; 66 | btnTaskSep1: TToolButton; 67 | btnTaskDelete: TToolButton; 68 | btnTaskEdit: TToolButton; 69 | btnTaskPost: TToolButton; 70 | btnTaskCancel: TToolButton; 71 | btnTaskRefresh: TToolButton; 72 | btnChainAdd: TToolButton; 73 | btnChainDelete: TToolButton; 74 | btnChainEdit: TToolButton; 75 | btnChainPost: TToolButton; 76 | btnChainCancel: TToolButton; 77 | btnChainRefresh: TToolButton; 78 | procedure acChainAddExecute(Sender: TObject); 79 | procedure acChainCancelExecute(Sender: TObject); 80 | procedure acChainDeleteExecute(Sender: TObject); 81 | procedure acChainEditExecute(Sender: TObject); 82 | procedure acChainPostExecute(Sender: TObject); 83 | procedure acChainRefreshExecute(Sender: TObject); 84 | procedure acChainToolbarUpdate(Sender: TObject); 85 | procedure acMoveTaskDownExecute(Sender: TObject); 86 | procedure acMoveTaskUpExecute(Sender: TObject); 87 | procedure acTaskAddExecute(Sender: TObject); 88 | procedure acTaskCancelExecute(Sender: TObject); 89 | procedure acTaskDeleteExecute(Sender: TObject); 90 | procedure acTaskEditExecute(Sender: TObject); 91 | procedure acTaskPostExecute(Sender: TObject); 92 | procedure acTaskRefreshExecute(Sender: TObject); 93 | procedure acTaskToolbarUpdate(Sender: TObject); 94 | procedure acConnectClick(Sender: TObject); 95 | procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); 96 | procedure gridChainsEditButtonClick(Sender: TObject); 97 | procedure gridChainsEditingDone(Sender: TObject); 98 | procedure gridChainsTitleClick(Column: TColumn); 99 | procedure gridTasksDrawColumnCell(Sender: TObject; const Rect: TRect; 100 | DataCol: integer; Column: TColumn; State: TGridDrawState); 101 | procedure gridTasksEditButtonClick(Sender: TObject); 102 | procedure gridTasksSelectEditor(Sender: TObject; Column: TColumn; 103 | var Editor: TWinControl); 104 | procedure miCloseClick(Sender: TObject); 105 | procedure miLogClick(Sender: TObject); 106 | procedure pcEditorsChange(Sender: TObject); 107 | private 108 | FLastColumn: TColumn; //last sorted grid column 109 | FTaskCmd: TfrmTaskCommandEditor; 110 | FCronEdit: TfrmCronEditor; 111 | public 112 | procedure UpdateSortIndication(ACol: TColumn); 113 | end; 114 | 115 | var 116 | MainForm: TfmMain; 117 | 118 | implementation 119 | 120 | uses uDataModule, SQLDB, LCLType; 121 | 122 | {$R *.lfm} 123 | 124 | { TfmMain } 125 | 126 | procedure TfmMain.FormCloseQuery(Sender: TObject; var CanClose: boolean); 127 | begin 128 | dmPgEngine.Disconnect; 129 | CanClose := True; 130 | end; 131 | 132 | procedure TfmMain.gridChainsEditButtonClick(Sender: TObject); 133 | var 134 | P: TPoint; 135 | begin 136 | if not Assigned(FCronEdit) then 137 | begin 138 | FCronEdit := TfrmCronEditor.Create(Self); 139 | FCronEdit.Parent := Self; 140 | end; 141 | P := gridChains.ClientToParent(gridChains.SelectedFieldRect.TopLeft, Self); 142 | FCronEdit.ShowEditor(gridChains.SelectedField, P); 143 | end; 144 | 145 | procedure TfmMain.gridChainsEditingDone(Sender: TObject); 146 | var 147 | S: string; 148 | F: TField; 149 | begin 150 | F := gridChains.SelectedField; 151 | if not Assigned(F) or (F.FieldName <> 'run_at') or gridChains.EditorMode then 152 | Exit; 153 | S := F.AsString; 154 | if not dmPgEngine.IsCronValueValid(S) then 155 | MessageDlg('Cron Syntax Error', 'You have error in the cron value: ' + S, 156 | mtError, [mbOK], 0); 157 | end; 158 | 159 | procedure TfmMain.gridChainsTitleClick(Column: TColumn); 160 | const 161 | imgArrowUp = 3; 162 | imgArrowDown = 4; 163 | var 164 | ASC_IndexName, DESC_IndexName: string; 165 | AQuery: TSQLQuery; 166 | 167 | procedure UpdateIndexes; 168 | begin 169 | // Ensure index defs are up to date 170 | AQuery.IndexDefs.Updated := False; {<<<--This line is critical. IndexDefs.Update will not 171 | update if already true, which will happen on the first column sorted.} 172 | AQuery.IndexDefs.Update; 173 | end; 174 | 175 | begin 176 | AQuery := Column.Field.DataSet as TSQLQuery; 177 | ASC_IndexName := 'ASC_' + Column.FieldName; 178 | DESC_IndexName := 'DESC_' + Column.FieldName; 179 | // indexes can't sort binary types such as ftMemo, ftBLOB 180 | if (Column.Field.DataType in [ftBLOB, ftMemo, ftWideMemo]) then 181 | Exit; 182 | // check if an ascending index already exists for this column. 183 | // if not, create one 184 | if AQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then 185 | begin 186 | AQuery.AddIndex(ASC_IndexName, column.FieldName, []); 187 | UpdateIndexes; //ensure index defs are up to date 188 | end; 189 | // Check if a descending index already exists for this column 190 | // if not, create one 191 | if AQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then 192 | begin 193 | AQuery.AddIndex(DESC_IndexName, column.FieldName, [ixDescending]); 194 | UpdateIndexes; //ensure index defs are up to date 195 | end; 196 | 197 | // Use the column tag to toggle ASC/DESC 198 | Column.Tag := not Column.Tag; 199 | if boolean(Column.Tag) then 200 | begin 201 | Column.Title.ImageIndex := imgArrowUp; 202 | AQuery.IndexName := ASC_IndexName; 203 | end 204 | else 205 | begin 206 | Column.Title.ImageIndex := imgArrowDown; 207 | AQuery.IndexName := DESC_IndexName; 208 | end; 209 | UpdateSortIndication(Column); 210 | end; 211 | 212 | procedure TfmMain.gridTasksDrawColumnCell(Sender: TObject; const Rect: TRect; 213 | DataCol: integer; Column: TColumn; State: TGridDrawState); 214 | var 215 | ImgIdx, aLeft, aTop: integer; 216 | begin 217 | if Column.FieldName <> 'kind' then 218 | Exit; 219 | case Column.Field.AsString of 220 | 'SQL': ImgIdx := 0; 221 | 'PROGRAM': ImgIdx := 1; 222 | else 223 | ImgIdx := 2; 224 | end; 225 | aLeft := Rect.Left + Rect.Width - imglGrids.Width - 2; 226 | aTop := Rect.Top + (Rect.Height - imglGrids.Height) div 2; 227 | imglGrids.Draw(gridTasks.Canvas, aLeft, aTop, ImgIdx); 228 | end; 229 | 230 | procedure TfmMain.gridTasksEditButtonClick(Sender: TObject); 231 | var 232 | P: TPoint; 233 | begin 234 | if not Assigned(FTaskCmd) then 235 | begin 236 | FTaskCmd := TfrmTaskCommandEditor.Create(Self); 237 | FTaskCmd.Parent := Self; 238 | end; 239 | P := gridTasks.ClientToParent(gridTasks.SelectedFieldRect.TopLeft, Self); 240 | FTaskCmd.ShowEditor(gridTasks.SelectedField, P); 241 | end; 242 | 243 | procedure TfmMain.gridTasksSelectEditor(Sender: TObject; Column: TColumn; 244 | var Editor: TWinControl); 245 | begin 246 | case Column.FieldName of 247 | 'kind': 248 | with Editor as TCustomComboBox do 249 | begin 250 | Style := csDropDownList; 251 | AutoDropDown := True; 252 | end; 253 | end; 254 | end; 255 | 256 | procedure TfmMain.miCloseClick(Sender: TObject); 257 | begin 258 | Close(); 259 | end; 260 | 261 | procedure TfmMain.miLogClick(Sender: TObject); 262 | begin 263 | tsLog.TabVisible := not tsLog.TabVisible; 264 | if tsLog.TabVisible then tsLog.Show(); 265 | end; 266 | 267 | procedure TfmMain.pcEditorsChange(Sender: TObject); 268 | begin 269 | pcEditors.SetFocus(); 270 | end; 271 | 272 | procedure TfmMain.UpdateSortIndication(ACol: TColumn); 273 | begin 274 | // Remove the sort arrow from the previous column we sorted 275 | if Assigned(FLastColumn) and (FLastColumn <> ACol) then 276 | FLastColumn.Title.ImageIndex := -1; 277 | FLastColumn := ACol; 278 | end; 279 | 280 | procedure TfmMain.acChainToolbarUpdate(Sender: TObject); 281 | var 282 | CanModify: boolean; 283 | begin 284 | CanModify := dmPgEngine.IsConnected() and dmPgEngine.qryChains.CanModify; 285 | acChainAdd.Enabled := CanModify; 286 | acChainDelete.Enabled := CanModify and 287 | (not (dmPgEngine.qryChains.BOF and dmPgEngine.qryChains.EOF)); 288 | acChainEdit.Enabled := CanModify and not (dmPgEngine.qryChains.State in dsEditModes); 289 | acChainPost.Enabled := CanModify and (dmPgEngine.qryChains.State in dsEditModes); 290 | acChainCancel.Enabled := CanModify and (dmPgEngine.qryChains.State in dsEditModes); 291 | acChainRefresh.Enabled := CanModify; 292 | acChainRun.Enabled := acChainDelete.Enabled; 293 | end; 294 | 295 | procedure TfmMain.acMoveTaskDownExecute(Sender: TObject); 296 | var idx: integer; 297 | begin 298 | with dmPgEngine do 299 | begin 300 | gridTasks.BeginUpdate; 301 | try 302 | idx := qryTasks.RecNo; 303 | MoveTaskDown(qryTasks.FieldByName('task_id').AsInteger); 304 | qryTasks.Refresh; 305 | qryTasks.RecNo := idx + 1; 306 | finally 307 | gridTasks.EndUpdate(); 308 | end; 309 | end; 310 | end; 311 | 312 | procedure TfmMain.acMoveTaskUpExecute(Sender: TObject); 313 | var idx: integer; 314 | begin 315 | with dmPgEngine do 316 | begin 317 | gridTasks.BeginUpdate; 318 | try 319 | idx := qryTasks.RecNo; 320 | MoveTaskUp(qryTasks.FieldByName('task_id').AsInteger); 321 | qryTasks.Refresh; 322 | qryTasks.RecNo := idx - 1; 323 | finally 324 | gridTasks.EndUpdate(); 325 | end; 326 | end; 327 | end; 328 | 329 | procedure TfmMain.acChainAddExecute(Sender: TObject); 330 | begin 331 | dmPgEngine.qryChains.Append; 332 | end; 333 | 334 | procedure TfmMain.acChainCancelExecute(Sender: TObject); 335 | begin 336 | dmPgEngine.qryChains.Cancel; 337 | end; 338 | 339 | procedure TfmMain.acChainDeleteExecute(Sender: TObject); 340 | begin 341 | dmPgEngine.qryChains.Delete; 342 | end; 343 | 344 | procedure TfmMain.acChainEditExecute(Sender: TObject); 345 | begin 346 | dmPgEngine.qryChains.Edit; 347 | end; 348 | 349 | procedure TfmMain.acChainPostExecute(Sender: TObject); 350 | begin 351 | dmPgEngine.qryChains.Post; 352 | end; 353 | 354 | procedure TfmMain.acChainRefreshExecute(Sender: TObject); 355 | begin 356 | dmPgEngine.qryChains.Refresh; 357 | end; 358 | 359 | procedure TfmMain.acTaskAddExecute(Sender: TObject); 360 | begin 361 | dmPgEngine.qryTasks.Append; 362 | end; 363 | 364 | procedure TfmMain.acTaskCancelExecute(Sender: TObject); 365 | begin 366 | dmPgEngine.qryTasks.Cancel; 367 | end; 368 | 369 | procedure TfmMain.acTaskDeleteExecute(Sender: TObject); 370 | begin 371 | dmPgEngine.qryTasks.Delete; 372 | end; 373 | 374 | procedure TfmMain.acTaskEditExecute(Sender: TObject); 375 | begin 376 | dmPgEngine.qryTasks.Edit; 377 | end; 378 | 379 | procedure TfmMain.acTaskPostExecute(Sender: TObject); 380 | begin 381 | dmPgEngine.qryTasks.Post; 382 | end; 383 | 384 | procedure TfmMain.acTaskRefreshExecute(Sender: TObject); 385 | begin 386 | dmPgEngine.qryTasks.Refresh; 387 | end; 388 | 389 | procedure TfmMain.acTaskToolbarUpdate(Sender: TObject); 390 | var 391 | CanModify: boolean; 392 | begin 393 | CanModify := dmPgEngine.IsConnected() and dmPgEngine.qryTasks.CanModify; 394 | acTaskAdd.Enabled := CanModify; 395 | acMoveTaskUp.Enabled := CanModify and (dmPgEngine.qryTasks.RecNo > 1); 396 | acMoveTaskDown.Enabled := CanModify and (dmPgEngine.qryTasks.RecNo < dmPgEngine.qryTasks.RecordCount); 397 | acTaskDelete.Enabled := CanModify and dmPgEngine.IsTaskDeleteAllowed(); 398 | acTaskEdit.Enabled := CanModify and not (dmPgEngine.qryTasks.State in dsEditModes); 399 | acTaskPost.Enabled := CanModify and (dmPgEngine.qryTasks.State in dsEditModes); 400 | acTaskCancel.Enabled := CanModify and (dmPgEngine.qryTasks.State in dsEditModes); 401 | acTaskRefresh.Enabled := CanModify; 402 | end; 403 | 404 | procedure TfmMain.acConnectClick(Sender: TObject); 405 | begin 406 | if not dmPgEngine.connMain.Connected then 407 | try 408 | dmPgEngine.Connect; 409 | except 410 | on EAbort do 411 | mmLog.Lines.Append('Connection cancelled by the user'); 412 | on E: Exception do 413 | MessageDlg('PostgreSQL Error', E.Message, mtError, [mbOK], 0); 414 | end 415 | else 416 | begin 417 | dmPgEngine.Disconnect; 418 | UpdateSortIndication(nil); 419 | end; 420 | end; 421 | 422 | end. 423 | -------------------------------------------------------------------------------- /forms/framebase.lfm: -------------------------------------------------------------------------------- 1 | object BaseFrame: TBaseFrame 2 | Left = 0 3 | Height = 360 4 | Top = 0 5 | Width = 480 6 | ClientHeight = 360 7 | ClientWidth = 480 8 | DesignTimePPI = 144 9 | LCLVersion = '2.2.4.0' 10 | OnExit = FrameExit 11 | ParentFont = False 12 | TabOrder = 0 13 | DesignLeft = 2785 14 | DesignTop = 1022 15 | object pnlButtons: TPanel 16 | Left = 0 17 | Height = 56 18 | Top = 304 19 | Width = 480 20 | Align = alBottom 21 | ClientHeight = 56 22 | ClientWidth = 480 23 | TabOrder = 0 24 | object btnOK: TButton 25 | Left = 344 26 | Height = 38 27 | Top = 8 28 | Width = 113 29 | Caption = 'OK' 30 | OnClick = btnOKClick 31 | TabOrder = 0 32 | end 33 | object btnCancel: TButton 34 | Left = 16 35 | Height = 38 36 | Top = 8 37 | Width = 113 38 | Caption = 'Cancel' 39 | OnExit = FrameExit 40 | TabOrder = 1 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /forms/framebase.pas: -------------------------------------------------------------------------------- 1 | unit framebase; 2 | 3 | {$mode ObjFPC}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, Forms, Controls, ExtCtrls, StdCtrls, SynEdit, 9 | SynHighlighterSQL, ComCtrls, LCLType, Db; 10 | 11 | type 12 | 13 | { TBaseFrame } 14 | 15 | TBaseFrame = class(TFrame) 16 | btnOK: TButton; 17 | btnCancel: TButton; 18 | pnlButtons: TPanel; 19 | procedure btnOKClick(Sender: TObject); 20 | procedure FrameExit(Sender: TObject); 21 | protected 22 | fldCommand: TField; 23 | function GetEditorValue(): string; virtual; abstract; 24 | procedure SetEditorValue(AText: string); virtual; abstract; 25 | public 26 | procedure ShowEditor(AField: TField; ATopLeft: TPoint); 27 | procedure HideEditor(); 28 | procedure ApplyChanges(); 29 | end; 30 | 31 | implementation 32 | 33 | {$R *.lfm} 34 | 35 | { TBaseFrame } 36 | 37 | procedure TBaseFrame.FrameExit(Sender: TObject); 38 | begin 39 | HideEditor(); 40 | end; 41 | 42 | procedure TBaseFrame.btnOKClick(Sender: TObject); 43 | begin 44 | ApplyChanges(); 45 | end; 46 | 47 | procedure TBaseFrame.ShowEditor(AField: TField; ATopLeft: TPoint); 48 | begin 49 | SetEditorValue(AField.AsString); 50 | fldCommand := AField; 51 | Left := ATopLeft.X; 52 | Top := ATopLeft.Y; 53 | Show(); 54 | SetFocus(); 55 | end; 56 | 57 | procedure TBaseFrame.HideEditor; 58 | begin 59 | Hide(); 60 | Parent.SetFocus(); 61 | end; 62 | 63 | procedure TBaseFrame.ApplyChanges; 64 | begin 65 | fldCommand.DataSet.Edit; 66 | fldCommand.AsString := GetEditorValue(); 67 | HideEditor(); 68 | end; 69 | 70 | end. 71 | 72 | -------------------------------------------------------------------------------- /forms/framecroneditor.lfm: -------------------------------------------------------------------------------- 1 | inherited frmCronEditor: TfrmCronEditor 2 | Width = 472 3 | ClientWidth = 472 4 | inherited pnlButtons: TPanel 5 | Width = 472 6 | ClientWidth = 472 7 | inherited btnCancel: TButton 8 | OnClick = FrameExit 9 | OnExit = nil 10 | end 11 | end 12 | object pnlEditor: TPanel[1] 13 | Left = 0 14 | Height = 304 15 | Top = 0 16 | Width = 472 17 | Align = alClient 18 | ClientHeight = 304 19 | ClientWidth = 472 20 | TabOrder = 1 21 | object edCron: TEdit 22 | Left = 48 23 | Height = 35 24 | Top = 41 25 | Width = 375 26 | Alignment = taCenter 27 | Font.Height = -24 28 | Font.Name = 'Courier New' 29 | Font.Pitch = fpFixed 30 | OnChange = edCronChange 31 | ParentFont = False 32 | TabOrder = 0 33 | Text = '* * * * *' 34 | end 35 | object mmRuns: TMemo 36 | Left = 48 37 | Height = 180 38 | Top = 112 39 | Width = 375 40 | Color = clInfoBk 41 | Lines.Strings = ( 42 | 'mmRuns' 43 | ) 44 | TabOrder = 1 45 | end 46 | object lblNextRuns: TLabel 47 | Left = 26 48 | Height = 25 49 | Top = 80 50 | Width = 79 51 | Caption = 'Next runs:' 52 | ParentColor = False 53 | end 54 | object lblDayMonth: TLabel 55 | Left = 198 56 | Height = 25 57 | Top = 16 58 | Width = 75 59 | Alignment = taCenter 60 | AutoSize = False 61 | Caption = 'Day' 62 | ParentColor = False 63 | end 64 | object lblMonth: TLabel 65 | Left = 273 66 | Height = 25 67 | Top = 16 68 | Width = 75 69 | Alignment = taCenter 70 | AutoSize = False 71 | Caption = 'Month' 72 | ParentColor = False 73 | end 74 | object lblWeekday: TLabel 75 | Left = 348 76 | Height = 25 77 | Top = 16 78 | Width = 75 79 | Alignment = taCenter 80 | AutoSize = False 81 | Caption = 'Week' 82 | ParentColor = False 83 | end 84 | object lblHour: TLabel 85 | Left = 123 86 | Height = 25 87 | Top = 16 88 | Width = 75 89 | Alignment = taCenter 90 | AutoSize = False 91 | Caption = 'Hour' 92 | ParentColor = False 93 | end 94 | object lblMinute: TLabel 95 | Left = 48 96 | Height = 25 97 | Top = 17 98 | Width = 75 99 | Alignment = taCenter 100 | AutoSize = False 101 | Caption = 'Minute' 102 | ParentColor = False 103 | end 104 | end 105 | object timerChange: TTimer[2] 106 | Enabled = False 107 | OnTimer = timerChangeTimer 108 | Left = 416 109 | Top = 88 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /forms/framecroneditor.pas: -------------------------------------------------------------------------------- 1 | unit frameCronEditor; 2 | 3 | {$mode ObjFPC}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, 9 | framebase; 10 | 11 | type 12 | 13 | { TfrmCronEditor } 14 | 15 | TfrmCronEditor = class(TBaseFrame) 16 | edCron: TEdit; 17 | lblNextRuns: TLabel; 18 | lblDayMonth: TLabel; 19 | lblHour: TLabel; 20 | lblMinute: TLabel; 21 | lblWeekday: TLabel; 22 | lblMonth: TLabel; 23 | mmRuns: TMemo; 24 | pnlEditor: TPanel; 25 | timerChange: TTimer; 26 | procedure edCronChange(Sender: TObject); 27 | procedure timerChangeTimer(Sender: TObject); 28 | private 29 | 30 | public 31 | function GetEditorValue(): string; override; 32 | procedure SetEditorValue(AText: string); override; 33 | procedure SetFocus; override; 34 | end; 35 | 36 | implementation 37 | 38 | uses RegExpr, uDataModule; 39 | 40 | {$R *.lfm} 41 | 42 | { TfrmCronEditor } 43 | 44 | procedure TfrmCronEditor.edCronChange(Sender: TObject); 45 | begin 46 | timerChange.Enabled := True; //start timer to update next runs 47 | end; 48 | 49 | procedure TfrmCronEditor.timerChangeTimer(Sender: TObject); 50 | var 51 | ValidCron: boolean; 52 | S: string; 53 | Output: string; 54 | const 55 | cronRE = '^(((\d+,)+\d+|(\d+(\/|-)\d+)|(\*(\/|-)\d+)|\d+|\*) +){4}'+ 56 | '(((\d+,)+\d+|(\d+(\/|-)\d+)|(\*(\/|-)\d+)|\d+|\*) ?)$'; 57 | sqlCronRuns = 'SELECT to_char(r.r, ''FMDay, FMDD FMMon YYYY at HH24:MI:SS'') FROM '+ 58 | 'generate_series(now(), now() + 10 * :cron :: interval, :cron :: interval) AS r(r) LIMIT 10'; 59 | sqlIntervalRuns = 'SELECT to_char(r.r, ''FMDay, FMDD FMMon YYYY at HH24:MI:SS'') FROM '+ 60 | 'timetable.cron_runs(now(), :cron) AS r(r) LIMIT 10'; 61 | minIntervalLen = length('@every '); // @ modifier and at least one space char 62 | begin 63 | timerChange.Enabled := False; 64 | S := edCron.Text; 65 | ValidCron := S.Trim() = '@reboot'; 66 | if not ValidCron then 67 | if S.StartsWith('@every ') or S.StartsWith('@after ') then //special values 68 | ValidCron := (S.Length > minIntervalLen) 69 | and dmPgEngine.IsCronValueValid(S) 70 | and dmPgEngine.SelectSQL(sqlCronRuns, [S.Substring(minIntervalLen)], Output) 71 | else 72 | with TRegExpr.Create(cronRE) do 73 | try 74 | ValidCron := Exec(S) and dmPgEngine.SelectSQL(sqlIntervalRuns, [S], Output); 75 | finally 76 | Free(); 77 | end; 78 | mmRuns.Text := Output; 79 | if ValidCron then edCron.Color := clDefault else edCron.Color := clRed; 80 | end; 81 | 82 | 83 | function TfrmCronEditor.GetEditorValue: string; 84 | begin 85 | Exit(edCron.Text); 86 | end; 87 | 88 | procedure TfrmCronEditor.SetEditorValue(AText: string); 89 | begin 90 | edCron.Text := AText; 91 | end; 92 | 93 | procedure TfrmCronEditor.SetFocus; 94 | begin 95 | edCron.SetFocus(); 96 | end; 97 | 98 | end. 99 | 100 | -------------------------------------------------------------------------------- /forms/frametaskcommandeditor.lfm: -------------------------------------------------------------------------------- 1 | inherited frmTaskCommandEditor: TfrmTaskCommandEditor 2 | inherited pnlButtons: TPanel 3 | inherited btnCancel: TButton 4 | OnClick = FrameExit 5 | OnExit = nil 6 | end 7 | end 8 | inline edCommand: TSynEdit[1] 9 | Left = 0 10 | Height = 304 11 | Top = 0 12 | Width = 480 13 | Align = alClient 14 | Font.Height = -28 15 | Font.Name = 'Courier New' 16 | Font.Pitch = fpFixed 17 | Font.Quality = fqNonAntialiased 18 | ParentColor = False 19 | ParentFont = False 20 | TabOrder = 1 21 | OnKeyDown = edCommandKeyDown 22 | Gutter.Visible = False 23 | Gutter.Width = 95 24 | Gutter.MouseActions = <> 25 | RightGutter.Width = 0 26 | RightGutter.MouseActions = <> 27 | Highlighter = SynSQLSyn 28 | Keystrokes = < 29 | item 30 | Command = ecUp 31 | ShortCut = 38 32 | end 33 | item 34 | Command = ecSelUp 35 | ShortCut = 8230 36 | end 37 | item 38 | Command = ecScrollUp 39 | ShortCut = 16422 40 | end 41 | item 42 | Command = ecDown 43 | ShortCut = 40 44 | end 45 | item 46 | Command = ecSelDown 47 | ShortCut = 8232 48 | end 49 | item 50 | Command = ecScrollDown 51 | ShortCut = 16424 52 | end 53 | item 54 | Command = ecLeft 55 | ShortCut = 37 56 | end 57 | item 58 | Command = ecSelLeft 59 | ShortCut = 8229 60 | end 61 | item 62 | Command = ecWordLeft 63 | ShortCut = 16421 64 | end 65 | item 66 | Command = ecSelWordLeft 67 | ShortCut = 24613 68 | end 69 | item 70 | Command = ecRight 71 | ShortCut = 39 72 | end 73 | item 74 | Command = ecSelRight 75 | ShortCut = 8231 76 | end 77 | item 78 | Command = ecWordRight 79 | ShortCut = 16423 80 | end 81 | item 82 | Command = ecSelWordRight 83 | ShortCut = 24615 84 | end 85 | item 86 | Command = ecPageDown 87 | ShortCut = 34 88 | end 89 | item 90 | Command = ecSelPageDown 91 | ShortCut = 8226 92 | end 93 | item 94 | Command = ecPageBottom 95 | ShortCut = 16418 96 | end 97 | item 98 | Command = ecSelPageBottom 99 | ShortCut = 24610 100 | end 101 | item 102 | Command = ecPageUp 103 | ShortCut = 33 104 | end 105 | item 106 | Command = ecSelPageUp 107 | ShortCut = 8225 108 | end 109 | item 110 | Command = ecPageTop 111 | ShortCut = 16417 112 | end 113 | item 114 | Command = ecSelPageTop 115 | ShortCut = 24609 116 | end 117 | item 118 | Command = ecLineStart 119 | ShortCut = 36 120 | end 121 | item 122 | Command = ecSelLineStart 123 | ShortCut = 8228 124 | end 125 | item 126 | Command = ecEditorTop 127 | ShortCut = 16420 128 | end 129 | item 130 | Command = ecSelEditorTop 131 | ShortCut = 24612 132 | end 133 | item 134 | Command = ecLineEnd 135 | ShortCut = 35 136 | end 137 | item 138 | Command = ecSelLineEnd 139 | ShortCut = 8227 140 | end 141 | item 142 | Command = ecEditorBottom 143 | ShortCut = 16419 144 | end 145 | item 146 | Command = ecSelEditorBottom 147 | ShortCut = 24611 148 | end 149 | item 150 | Command = ecToggleMode 151 | ShortCut = 45 152 | end 153 | item 154 | Command = ecCopy 155 | ShortCut = 16429 156 | end 157 | item 158 | Command = ecPaste 159 | ShortCut = 8237 160 | end 161 | item 162 | Command = ecDeleteChar 163 | ShortCut = 46 164 | end 165 | item 166 | Command = ecCut 167 | ShortCut = 8238 168 | end 169 | item 170 | Command = ecDeleteLastChar 171 | ShortCut = 8 172 | end 173 | item 174 | Command = ecDeleteLastChar 175 | ShortCut = 8200 176 | end 177 | item 178 | Command = ecDeleteLastWord 179 | ShortCut = 16392 180 | end 181 | item 182 | Command = ecUndo 183 | ShortCut = 32776 184 | end 185 | item 186 | Command = ecRedo 187 | ShortCut = 40968 188 | end 189 | item 190 | Command = ecLineBreak 191 | ShortCut = 13 192 | end 193 | item 194 | Command = ecSelectAll 195 | ShortCut = 16449 196 | end 197 | item 198 | Command = ecCopy 199 | ShortCut = 16451 200 | end 201 | item 202 | Command = ecBlockIndent 203 | ShortCut = 24649 204 | end 205 | item 206 | Command = ecLineBreak 207 | ShortCut = 16461 208 | end 209 | item 210 | Command = ecInsertLine 211 | ShortCut = 16462 212 | end 213 | item 214 | Command = ecDeleteWord 215 | ShortCut = 16468 216 | end 217 | item 218 | Command = ecBlockUnindent 219 | ShortCut = 24661 220 | end 221 | item 222 | Command = ecPaste 223 | ShortCut = 16470 224 | end 225 | item 226 | Command = ecCut 227 | ShortCut = 16472 228 | end 229 | item 230 | Command = ecDeleteLine 231 | ShortCut = 16473 232 | end 233 | item 234 | Command = ecDeleteEOL 235 | ShortCut = 24665 236 | end 237 | item 238 | Command = ecUndo 239 | ShortCut = 16474 240 | end 241 | item 242 | Command = ecRedo 243 | ShortCut = 24666 244 | end 245 | item 246 | Command = ecGotoMarker0 247 | ShortCut = 16432 248 | end 249 | item 250 | Command = ecGotoMarker1 251 | ShortCut = 16433 252 | end 253 | item 254 | Command = ecGotoMarker2 255 | ShortCut = 16434 256 | end 257 | item 258 | Command = ecGotoMarker3 259 | ShortCut = 16435 260 | end 261 | item 262 | Command = ecGotoMarker4 263 | ShortCut = 16436 264 | end 265 | item 266 | Command = ecGotoMarker5 267 | ShortCut = 16437 268 | end 269 | item 270 | Command = ecGotoMarker6 271 | ShortCut = 16438 272 | end 273 | item 274 | Command = ecGotoMarker7 275 | ShortCut = 16439 276 | end 277 | item 278 | Command = ecGotoMarker8 279 | ShortCut = 16440 280 | end 281 | item 282 | Command = ecGotoMarker9 283 | ShortCut = 16441 284 | end 285 | item 286 | Command = ecSetMarker0 287 | ShortCut = 24624 288 | end 289 | item 290 | Command = ecSetMarker1 291 | ShortCut = 24625 292 | end 293 | item 294 | Command = ecSetMarker2 295 | ShortCut = 24626 296 | end 297 | item 298 | Command = ecSetMarker3 299 | ShortCut = 24627 300 | end 301 | item 302 | Command = ecSetMarker4 303 | ShortCut = 24628 304 | end 305 | item 306 | Command = ecSetMarker5 307 | ShortCut = 24629 308 | end 309 | item 310 | Command = ecSetMarker6 311 | ShortCut = 24630 312 | end 313 | item 314 | Command = ecSetMarker7 315 | ShortCut = 24631 316 | end 317 | item 318 | Command = ecSetMarker8 319 | ShortCut = 24632 320 | end 321 | item 322 | Command = ecSetMarker9 323 | ShortCut = 24633 324 | end 325 | item 326 | Command = EcFoldLevel1 327 | ShortCut = 41009 328 | end 329 | item 330 | Command = EcFoldLevel2 331 | ShortCut = 41010 332 | end 333 | item 334 | Command = EcFoldLevel3 335 | ShortCut = 41011 336 | end 337 | item 338 | Command = EcFoldLevel4 339 | ShortCut = 41012 340 | end 341 | item 342 | Command = EcFoldLevel5 343 | ShortCut = 41013 344 | end 345 | item 346 | Command = EcFoldLevel6 347 | ShortCut = 41014 348 | end 349 | item 350 | Command = EcFoldLevel7 351 | ShortCut = 41015 352 | end 353 | item 354 | Command = EcFoldLevel8 355 | ShortCut = 41016 356 | end 357 | item 358 | Command = EcFoldLevel9 359 | ShortCut = 41017 360 | end 361 | item 362 | Command = EcFoldLevel0 363 | ShortCut = 41008 364 | end 365 | item 366 | Command = EcFoldCurrent 367 | ShortCut = 41005 368 | end 369 | item 370 | Command = EcUnFoldCurrent 371 | ShortCut = 41003 372 | end 373 | item 374 | Command = EcToggleMarkupWord 375 | ShortCut = 32845 376 | end 377 | item 378 | Command = ecNormalSelect 379 | ShortCut = 24654 380 | end 381 | item 382 | Command = ecColumnSelect 383 | ShortCut = 24643 384 | end 385 | item 386 | Command = ecLineSelect 387 | ShortCut = 24652 388 | end 389 | item 390 | Command = ecTab 391 | ShortCut = 9 392 | end 393 | item 394 | Command = ecShiftTab 395 | ShortCut = 8201 396 | end 397 | item 398 | Command = ecMatchBracket 399 | ShortCut = 24642 400 | end 401 | item 402 | Command = ecColSelUp 403 | ShortCut = 40998 404 | end 405 | item 406 | Command = ecColSelDown 407 | ShortCut = 41000 408 | end 409 | item 410 | Command = ecColSelLeft 411 | ShortCut = 40997 412 | end 413 | item 414 | Command = ecColSelRight 415 | ShortCut = 40999 416 | end 417 | item 418 | Command = ecColSelPageDown 419 | ShortCut = 40994 420 | end 421 | item 422 | Command = ecColSelPageBottom 423 | ShortCut = 57378 424 | end 425 | item 426 | Command = ecColSelPageUp 427 | ShortCut = 40993 428 | end 429 | item 430 | Command = ecColSelPageTop 431 | ShortCut = 57377 432 | end 433 | item 434 | Command = ecColSelLineStart 435 | ShortCut = 40996 436 | end 437 | item 438 | Command = ecColSelLineEnd 439 | ShortCut = 40995 440 | end 441 | item 442 | Command = ecColSelEditorTop 443 | ShortCut = 57380 444 | end 445 | item 446 | Command = ecColSelEditorBottom 447 | ShortCut = 57379 448 | end> 449 | MouseActions = <> 450 | MouseTextActions = <> 451 | MouseSelActions = <> 452 | Lines.Strings = ( 453 | 'select * from foo where bar = ''baz''' 454 | ) 455 | VisibleSpecialChars = [vscSpace, vscTabAtLast] 456 | ScrollBars = ssAutoVertical 457 | SelectedColor.BackPriority = 50 458 | SelectedColor.ForePriority = 50 459 | SelectedColor.FramePriority = 50 460 | SelectedColor.BoldPriority = 50 461 | SelectedColor.ItalicPriority = 50 462 | SelectedColor.UnderlinePriority = 50 463 | SelectedColor.StrikeOutPriority = 50 464 | BracketHighlightStyle = sbhsBoth 465 | BracketMatchColor.Background = clNone 466 | BracketMatchColor.Foreground = clNone 467 | BracketMatchColor.Style = [fsBold] 468 | FoldedCodeColor.Background = clNone 469 | FoldedCodeColor.Foreground = clGray 470 | FoldedCodeColor.FrameColor = clGray 471 | MouseLinkColor.Background = clNone 472 | MouseLinkColor.Foreground = clBlue 473 | LineHighlightColor.Background = clNone 474 | LineHighlightColor.Foreground = clNone 475 | inline SynLeftGutterPartList1: TSynGutterPartList 476 | object SynGutterMarks1: TSynGutterMarks 477 | Width = 36 478 | MouseActions = <> 479 | end 480 | object SynGutterLineNumber1: TSynGutterLineNumber 481 | Width = 35 482 | MouseActions = <> 483 | MarkupInfo.Background = clBtnFace 484 | MarkupInfo.Foreground = clNone 485 | DigitCount = 2 486 | ShowOnlyLineNumbersMultiplesOf = 1 487 | ZeroStart = False 488 | LeadingZeros = False 489 | end 490 | object SynGutterChanges1: TSynGutterChanges 491 | Width = 6 492 | MouseActions = <> 493 | ModifiedColor = 59900 494 | SavedColor = clGreen 495 | end 496 | object SynGutterSeparator1: TSynGutterSeparator 497 | Width = 3 498 | MouseActions = <> 499 | MarkupInfo.Background = clWhite 500 | MarkupInfo.Foreground = clGray 501 | end 502 | object SynGutterCodeFolding1: TSynGutterCodeFolding 503 | Width = 15 504 | MouseActions = <> 505 | MarkupInfo.Background = clNone 506 | MarkupInfo.Foreground = clGray 507 | MouseActionsExpanded = <> 508 | MouseActionsCollapsed = <> 509 | end 510 | end 511 | end 512 | object SynSQLSyn: TSynSQLSyn[2] 513 | DefaultFilter = 'SQL Files (*.sql)|*.sql' 514 | Enabled = False 515 | SQLDialect = sqlPostgres 516 | Left = 360 517 | Top = 53 518 | end 519 | end 520 | -------------------------------------------------------------------------------- /forms/frametaskcommandeditor.pas: -------------------------------------------------------------------------------- 1 | unit frameTaskCommandEditor; 2 | 3 | {$mode ObjFPC}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, Forms, Controls, Graphics, Dialogs, SynEdit, 9 | SynHighlighterSQL, LCLType, framebase; 10 | 11 | type 12 | 13 | { TfrmTaskCommandEditor } 14 | 15 | TfrmTaskCommandEditor = class(TBaseFrame) 16 | edCommand: TSynEdit; 17 | SynSQLSyn: TSynSQLSyn; 18 | procedure edCommandKeyDown(Sender: TObject; var Key: Word; 19 | Shift: TShiftState); 20 | public 21 | function GetEditorValue(): string; override; 22 | procedure SetEditorValue(AText: string); override; 23 | procedure SetFocus; override; 24 | procedure CreateParams(var Params: TCreateParams); override; 25 | end; 26 | 27 | implementation 28 | 29 | {$R *.lfm} 30 | 31 | { TfrmTaskCommandEditor } 32 | 33 | procedure TfrmTaskCommandEditor.edCommandKeyDown(Sender: TObject; var Key: Word; 34 | Shift: TShiftState); 35 | begin 36 | case Key of 37 | VK_ESCAPE: Self.Hide(); 38 | VK_RETURN: if ssCtrl in Shift then ApplyChanges(); 39 | end; 40 | end; 41 | 42 | function TfrmTaskCommandEditor.GetEditorValue: string; 43 | begin 44 | Exit(edCommand.Text); 45 | end; 46 | 47 | procedure TfrmTaskCommandEditor.SetEditorValue(AText: string); 48 | begin 49 | edCommand.Text := AText; 50 | end; 51 | 52 | procedure TfrmTaskCommandEditor.SetFocus; 53 | begin 54 | edCommand.SetFocus(); 55 | end; 56 | 57 | procedure TfrmTaskCommandEditor.CreateParams(var Params: TCreateParams); 58 | begin 59 | inherited CreateParams(Params); 60 | Params.Style := Params.Style or WS_SIZEBOX; 61 | end; 62 | 63 | end. 64 | 65 | -------------------------------------------------------------------------------- /pg_timetable_gui.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/pg_timetable_gui.ico -------------------------------------------------------------------------------- /pg_timetable_gui.lpi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <Scaled Value="True"/> 13 | <ResourceType Value="res"/> 14 | <UseXPManifest Value="True"/> 15 | <XPManifest> 16 | <DpiAware Value="True"/> 17 | </XPManifest> 18 | <Icon Value="0"/> 19 | </General> 20 | <BuildModes Count="3"> 21 | <Item1 Name="Default" Default="True"/> 22 | <Item2 Name="Debug"> 23 | <CompilerOptions> 24 | <Version Value="11"/> 25 | <PathDelim Value="\"/> 26 | <Target> 27 | <Filename Value="pg_timetable_gui"/> 28 | </Target> 29 | <SearchPaths> 30 | <IncludeFiles Value="$(ProjOutDir)"/> 31 | <OtherUnitFiles Value="forms"/> 32 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 33 | </SearchPaths> 34 | <Parsing> 35 | <SyntaxOptions> 36 | <IncludeAssertionCode Value="True"/> 37 | </SyntaxOptions> 38 | </Parsing> 39 | <CodeGeneration> 40 | <Checks> 41 | <IOChecks Value="True"/> 42 | <RangeChecks Value="True"/> 43 | <OverflowChecks Value="True"/> 44 | <StackChecks Value="True"/> 45 | </Checks> 46 | <VerifyObjMethodCallValidity Value="True"/> 47 | </CodeGeneration> 48 | <Linking> 49 | <Debugging> 50 | <DebugInfoType Value="dsDwarf2Set"/> 51 | <UseHeaptrc Value="True"/> 52 | <TrashVariables Value="True"/> 53 | <UseExternalDbgSyms Value="True"/> 54 | </Debugging> 55 | <Options> 56 | <Win32> 57 | <GraphicApplication Value="True"/> 58 | </Win32> 59 | </Options> 60 | </Linking> 61 | <Other> 62 | <CompilerMessages> 63 | <IgnoredMessages idx5024="True"/> 64 | </CompilerMessages> 65 | </Other> 66 | </CompilerOptions> 67 | </Item2> 68 | <Item3 Name="Release"> 69 | <CompilerOptions> 70 | <Version Value="11"/> 71 | <PathDelim Value="\"/> 72 | <Target> 73 | <Filename Value="pg_timetable_gui"/> 74 | </Target> 75 | <SearchPaths> 76 | <IncludeFiles Value="$(ProjOutDir)"/> 77 | <OtherUnitFiles Value="forms"/> 78 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 79 | </SearchPaths> 80 | <CodeGeneration> 81 | <SmartLinkUnit Value="True"/> 82 | <Optimizations> 83 | <OptimizationLevel Value="3"/> 84 | </Optimizations> 85 | </CodeGeneration> 86 | <Linking> 87 | <Debugging> 88 | <GenerateDebugInfo Value="False"/> 89 | </Debugging> 90 | <LinkSmart Value="True"/> 91 | <Options> 92 | <Win32> 93 | <GraphicApplication Value="True"/> 94 | </Win32> 95 | </Options> 96 | </Linking> 97 | </CompilerOptions> 98 | </Item3> 99 | </BuildModes> 100 | <PublishOptions> 101 | <Version Value="2"/> 102 | <UseFileFilters Value="True"/> 103 | </PublishOptions> 104 | <RunParams> 105 | <FormatVersion Value="2"/> 106 | </RunParams> 107 | <RequiredPackages Count="4"> 108 | <Item1> 109 | <PackageName Value="SynEdit"/> 110 | </Item1> 111 | <Item2> 112 | <PackageName Value="LazControls"/> 113 | </Item2> 114 | <Item3> 115 | <PackageName Value="FCL"/> 116 | </Item3> 117 | <Item4> 118 | <PackageName Value="LCL"/> 119 | </Item4> 120 | </RequiredPackages> 121 | <Units Count="11"> 122 | <Unit0> 123 | <Filename Value="pg_timetable_gui.lpr"/> 124 | <IsPartOfProject Value="True"/> 125 | </Unit0> 126 | <Unit1> 127 | <Filename Value="forms\fmmain.pas"/> 128 | <IsPartOfProject Value="True"/> 129 | <ComponentName Value="fmMain"/> 130 | <HasResources Value="True"/> 131 | <ResourceBaseClass Value="Form"/> 132 | <UnitName Value="fmMain"/> 133 | </Unit1> 134 | <Unit2> 135 | <Filename Value="udatamodule.pas"/> 136 | <IsPartOfProject Value="True"/> 137 | <ComponentName Value="dmPgEngine"/> 138 | <HasResources Value="True"/> 139 | <ResourceBaseClass Value="DataModule"/> 140 | <UnitName Value="uDataModule"/> 141 | </Unit2> 142 | <Unit3> 143 | <Filename Value="uobjects.pas"/> 144 | <IsPartOfProject Value="True"/> 145 | <UnitName Value="uObjects"/> 146 | </Unit3> 147 | <Unit4> 148 | <Filename Value=".gitignore"/> 149 | <IsPartOfProject Value="True"/> 150 | </Unit4> 151 | <Unit5> 152 | <Filename Value="LICENSE"/> 153 | <IsPartOfProject Value="True"/> 154 | </Unit5> 155 | <Unit6> 156 | <Filename Value="README.md"/> 157 | <IsPartOfProject Value="True"/> 158 | </Unit6> 159 | <Unit7> 160 | <Filename Value="forms\fmconnect.pas"/> 161 | <IsPartOfProject Value="True"/> 162 | <ComponentName Value="fmConnect"/> 163 | <HasResources Value="True"/> 164 | <ResourceBaseClass Value="Form"/> 165 | <UnitName Value="fmConnect"/> 166 | </Unit7> 167 | <Unit8> 168 | <Filename Value="forms\framebase.pas"/> 169 | <IsPartOfProject Value="True"/> 170 | <ComponentName Value="BaseFrame"/> 171 | <HasResources Value="True"/> 172 | <ResourceBaseClass Value="Frame"/> 173 | </Unit8> 174 | <Unit9> 175 | <Filename Value="forms\frametaskcommandeditor.pas"/> 176 | <IsPartOfProject Value="True"/> 177 | <ComponentName Value="frmTaskCommandEditor"/> 178 | <HasResources Value="True"/> 179 | <ResourceBaseClass Value="Frame"/> 180 | <UnitName Value="frameTaskCommandEditor"/> 181 | </Unit9> 182 | <Unit10> 183 | <Filename Value="forms\framecroneditor.pas"/> 184 | <IsPartOfProject Value="True"/> 185 | <ComponentName Value="frmCronEditor"/> 186 | <HasResources Value="True"/> 187 | <ResourceBaseClass Value="Frame"/> 188 | <UnitName Value="frameCronEditor"/> 189 | </Unit10> 190 | </Units> 191 | </ProjectOptions> 192 | <CompilerOptions> 193 | <Version Value="11"/> 194 | <PathDelim Value="\"/> 195 | <Target> 196 | <Filename Value="pg_timetable_gui"/> 197 | </Target> 198 | <SearchPaths> 199 | <IncludeFiles Value="$(ProjOutDir)"/> 200 | <OtherUnitFiles Value="forms"/> 201 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 202 | </SearchPaths> 203 | <Linking> 204 | <Options> 205 | <Win32> 206 | <GraphicApplication Value="True"/> 207 | </Win32> 208 | </Options> 209 | </Linking> 210 | <Other> 211 | <CompilerMessages> 212 | <IgnoredMessages idx5024="True"/> 213 | </CompilerMessages> 214 | </Other> 215 | </CompilerOptions> 216 | <Debugging> 217 | <Exceptions Count="3"> 218 | <Item1> 219 | <Name Value="EAbort"/> 220 | </Item1> 221 | <Item2> 222 | <Name Value="ECodetoolError"/> 223 | </Item2> 224 | <Item3> 225 | <Name Value="EFOpenError"/> 226 | </Item3> 227 | </Exceptions> 228 | </Debugging> 229 | </CONFIG> 230 | -------------------------------------------------------------------------------- /pg_timetable_gui.lpr: -------------------------------------------------------------------------------- 1 | program pg_timetable_gui; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses {$IFDEF UNIX} {$IFDEF UseCThreads} cthreads, {$ENDIF} {$ENDIF} 6 | Interfaces, // this includes the LCL widgetset 7 | Forms, 8 | lazcontrols, 9 | fmMain, fmConnect, 10 | uDataModule, 11 | uObjects, frameTaskCommandEditor, frameCronEditor; 12 | 13 | {$R *.res} 14 | 15 | begin 16 | RequireDerivedFormResource := True; 17 | Application.Scaled := True; 18 | Application.Initialize; 19 | Application.CreateForm(TfmMain, MainForm); 20 | Application.CreateForm(TdmPgEngine, dmPgEngine); 21 | Application.Run; 22 | end. 23 | -------------------------------------------------------------------------------- /pg_timetable_gui.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/pg_timetable_gui.res -------------------------------------------------------------------------------- /res/control-play-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/control-play-disabled.png -------------------------------------------------------------------------------- /res/control-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/control-play.png -------------------------------------------------------------------------------- /res/nav-add-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-add-disabled.png -------------------------------------------------------------------------------- /res/nav-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-add.png -------------------------------------------------------------------------------- /res/nav-cancel-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-cancel-disabled.png -------------------------------------------------------------------------------- /res/nav-cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-cancel.png -------------------------------------------------------------------------------- /res/nav-delete-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-delete-disabled.png -------------------------------------------------------------------------------- /res/nav-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-delete.png -------------------------------------------------------------------------------- /res/nav-down-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-down-disabled.png -------------------------------------------------------------------------------- /res/nav-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-down.png -------------------------------------------------------------------------------- /res/nav-edit-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-edit-disabled.png -------------------------------------------------------------------------------- /res/nav-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-edit.png -------------------------------------------------------------------------------- /res/nav-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-next.png -------------------------------------------------------------------------------- /res/nav-post-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-post-disabled.png -------------------------------------------------------------------------------- /res/nav-post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-post.png -------------------------------------------------------------------------------- /res/nav-prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-prev.png -------------------------------------------------------------------------------- /res/nav-refresh-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-refresh-disabled.png -------------------------------------------------------------------------------- /res/nav-refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-refresh.png -------------------------------------------------------------------------------- /res/nav-up-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-up-disabled.png -------------------------------------------------------------------------------- /res/nav-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/nav-up.png -------------------------------------------------------------------------------- /res/pg_timetable_gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/pg_timetable_gui.png -------------------------------------------------------------------------------- /res/shut-down-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/shut-down-disabled.png -------------------------------------------------------------------------------- /res/shut-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/shut-down.png -------------------------------------------------------------------------------- /res/sort-asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/sort-asc.png -------------------------------------------------------------------------------- /res/sort-desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/sort-desc.png -------------------------------------------------------------------------------- /res/tab-chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/tab-chain.png -------------------------------------------------------------------------------- /res/tab-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/tab-dashboard.png -------------------------------------------------------------------------------- /res/tab-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/tab-log.png -------------------------------------------------------------------------------- /res/tab-task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/tab-task.png -------------------------------------------------------------------------------- /res/task-builtin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/task-builtin.png -------------------------------------------------------------------------------- /res/task-program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/task-program.png -------------------------------------------------------------------------------- /res/task-sql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pg_timetable_gui/21455b62a36c45dfeaa622611397a804412eb349/res/task-sql.png -------------------------------------------------------------------------------- /udatamodule.lfm: -------------------------------------------------------------------------------- 1 | object dmPgEngine: TdmPgEngine 2 | OldCreateOrder = False 3 | Height = 524 4 | HorizontalOffset = 1284 5 | VerticalOffset = 619 6 | Width = 668 7 | PPI = 144 8 | object dsChains: TDataSource 9 | DataSet = qryChains 10 | Left = 108 11 | Top = 48 12 | end 13 | object connMain: TPQConnection 14 | Connected = False 15 | LoginPrompt = True 16 | OnLogin = connMainLogin 17 | DatabaseName = 'timetable' 18 | KeepConnection = False 19 | Password = 'somestrong' 20 | Transaction = transChains 21 | UserName = 'scheduler' 22 | OnLog = connMainLog 23 | LogEvents = [detPrepare, detExecute, detCommit, detParamValue, detActualSQL] 24 | Left = 105 25 | Top = 312 26 | end 27 | object qryChains: TSQLQuery 28 | PacketRecords = -1 29 | IndexName = 'DEFAULT_ORDER' 30 | MaxIndexesCount = 20 31 | FieldDefs = < 32 | item 33 | Name = 'chain_id' 34 | DataType = ftLargeint 35 | Precision = -1 36 | end 37 | item 38 | Name = 'chain_name' 39 | DataType = ftWideString 40 | Precision = -1 41 | end 42 | item 43 | Name = 'run_at' 44 | DataType = ftMemo 45 | Precision = -1 46 | end 47 | item 48 | Name = 'max_instances' 49 | DataType = ftInteger 50 | Precision = -1 51 | end 52 | item 53 | Name = 'live' 54 | DataType = ftBoolean 55 | Precision = -1 56 | end 57 | item 58 | Name = 'self_destruct' 59 | DataType = ftBoolean 60 | Precision = -1 61 | end 62 | item 63 | Name = 'exclusive_execution' 64 | DataType = ftBoolean 65 | Precision = -1 66 | end 67 | item 68 | Name = 'client_name' 69 | DataType = ftMemo 70 | Precision = -1 71 | end 72 | item 73 | Name = 'timeout' 74 | DataType = ftInteger 75 | Precision = -1 76 | end> 77 | AfterClose = qryChainsAfterClose 78 | AfterDelete = qryChainsAfterDelete 79 | AfterInsert = qryChainsAfterInsert 80 | AfterPost = qryAfterPost 81 | BeforeDelete = qryChainsBeforeDelete 82 | Database = connMain 83 | Transaction = transChains 84 | SQL.Strings = ( 85 | 'SELECT' 86 | ' chain_id,' 87 | ' chain_name,' 88 | ' COALESCE(run_at, ''* * * * *'') as run_at,' 89 | ' max_instances,' 90 | ' live,' 91 | ' self_destruct,' 92 | ' exclusive_execution,' 93 | ' client_name,' 94 | ' timeout' 95 | 'FROM timetable.chain' 96 | 'ORDER BY chain_id' 97 | ) 98 | InsertSQL.Strings = ( 99 | 'INSERT INTO timetable.chain (' 100 | #9'"chain_name",' 101 | #9'"run_at",' 102 | #9'"max_instances",' 103 | #9'"live",' 104 | #9'"self_destruct",' 105 | #9'"exclusive_execution",' 106 | #9'"client_name",' 107 | #9'"timeout"' 108 | ') VALUES (' 109 | #9':"chain_name",' 110 | #9':"run_at",' 111 | #9':"max_instances",' 112 | #9':"live",' 113 | #9':"self_destruct",' 114 | #9':"exclusive_execution",' 115 | #9':"client_name",' 116 | #9':"timeout"' 117 | ')' 118 | ) 119 | UpdateSQL.Strings = ( 120 | 'UPDATE timetable.chain' 121 | 'SET' 122 | #9'"chain_name"=:"chain_name",' 123 | #9'"run_at"=:"run_at",' 124 | #9'"max_instances"=:"max_instances",' 125 | #9'"live"=:"live",' 126 | #9'"self_destruct"=:"self_destruct",' 127 | #9'"exclusive_execution"=:"exclusive_execution",' 128 | #9'"client_name"=:"client_name",' 129 | ' "timeout"=:"timeout"' 130 | 'WHERE' 131 | #9'"chain_id"= :"OLD_chain_id"' 132 | ) 133 | DeleteSQL.Strings = ( 134 | 'DELETE FROM timetable.chain' 135 | 'WHERE chain_id = :"old_chain_id"' 136 | ) 137 | Options = [sqoKeepOpenOnCommit, sqoAutoCommit] 138 | Params = <> 139 | ParamCheck = False 140 | Macros = <> 141 | ParseSQL = False 142 | UsePrimaryKeyAsKey = False 143 | Left = 105 144 | Top = 177 145 | end 146 | object transChains: TSQLTransaction 147 | Active = False 148 | Database = connMain 149 | Options = [stoUseImplicit] 150 | Left = 192 151 | Top = 312 152 | end 153 | object qryTasks: TSQLQuery 154 | PacketRecords = -1 155 | IndexName = 'DEFAULT_ORDER' 156 | MaxIndexesCount = 4 157 | FieldDefs = <> 158 | AfterInsert = qryTasksAfterInsert 159 | AfterOpen = qryTasksAfterOpen 160 | AfterPost = qryAfterPost 161 | AfterRefresh = qryTasksAfterOpen 162 | BeforePost = qryTasksBeforePost 163 | Database = connMain 164 | Transaction = transChains 165 | SQL.Strings = ( 166 | 'SELECT' 167 | ' task_order,' 168 | ' task_name,' 169 | ' chain_id,' 170 | ' task_id,' 171 | ' command,' 172 | ' kind,' 173 | ' run_as,' 174 | ' ignore_error,' 175 | ' autonomous,' 176 | ' database_connection' 177 | 'FROM' 178 | ' timetable.task' 179 | 'WHERE' 180 | ' chain_id = :"chain_id"' 181 | 'ORDER BY' 182 | ' task_order ASC' 183 | ) 184 | InsertSQL.Strings = ( 185 | 'INSERT INTO timetable.task(' 186 | ' task_order,' 187 | ' chain_id,' 188 | ' task_name,' 189 | ' command,' 190 | ' kind,' 191 | ' run_as,' 192 | ' ignore_error,' 193 | ' autonomous,' 194 | ' database_connection' 195 | ') VALUES (' 196 | ' :"task_order",' 197 | ' :"chain_id",' 198 | ' :"task_name",' 199 | ' :"command",' 200 | ' :"kind",' 201 | ' :"run_as",' 202 | ' :"ignore_error",' 203 | ' :"autonomous",' 204 | ' :"database_connection"' 205 | ')' 206 | ) 207 | UpdateSQL.Strings = ( 208 | 'UPDATE timetable.task SET (' 209 | ' task_order,' 210 | ' task_name,' 211 | ' command,' 212 | ' kind,' 213 | ' run_as,' 214 | ' ignore_error,' 215 | ' autonomous,' 216 | ' database_connection' 217 | ' ) = (' 218 | ' :"task_order",' 219 | ' :"task_name",' 220 | ' :"command",' 221 | ' :"kind",' 222 | ' :"run_as",' 223 | ' :"ignore_error",' 224 | ' :"autonomous",' 225 | ' :"database_connection"' 226 | ' )' 227 | 'WHERE task_id = :"task_id"' 228 | ) 229 | DeleteSQL.Strings = ( 230 | 'SELECT timetable.delete_task(:"task_id")' 231 | ) 232 | Options = [sqoKeepOpenOnCommit, sqoAutoApplyUpdates, sqoAutoCommit, sqoRefreshUsingSelect] 233 | Params = < 234 | item 235 | DataType = ftLargeint 236 | Name = 'chain_id' 237 | ParamType = ptInput 238 | end> 239 | Macros = <> 240 | ParseSQL = False 241 | DataSource = dsChains 242 | Left = 192 243 | Top = 177 244 | end 245 | object dsTasks: TDataSource 246 | DataSet = qryTasks 247 | Left = 192 248 | Top = 48 249 | end 250 | end 251 | -------------------------------------------------------------------------------- /udatamodule.pas: -------------------------------------------------------------------------------- 1 | unit uDataModule; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, DB, PQConnection, SQLDB; 9 | 10 | type 11 | 12 | { TdmPgEngine } 13 | 14 | TdmPgEngine = class(TDataModule) 15 | dsTasks: TDataSource; 16 | dsChains: TDataSource; 17 | connMain: TPQConnection; 18 | qryChains: TSQLQuery; 19 | qryTasks: TSQLQuery; 20 | transChains: TSQLTransaction; 21 | procedure connMainLog(Sender: TSQLConnection; EventType: TDBEventType; 22 | const Msg: String); 23 | procedure connMainLogin(Sender: TObject; Username, Password: string); 24 | procedure qryChainsAfterClose(DataSet: TDataSet); 25 | procedure qryChainsAfterDelete(DataSet: TDataSet); 26 | procedure qryChainsAfterInsert(DataSet: TDataSet); 27 | procedure qryAfterPost(DataSet: TDataSet); 28 | procedure qryChainsBeforeDelete(DataSet: TDataSet); 29 | procedure qryTasksAfterInsert(DataSet: TDataSet); 30 | procedure qryTasksAfterOpen(DataSet: TDataSet); 31 | procedure qryTasksBeforePost(DataSet: TDataSet); 32 | private 33 | const 34 | DEFAULT_ORDER_DELTA: double = 10.0; 35 | var 36 | FLastTaskOrder: double; 37 | public 38 | procedure Connect; 39 | procedure Disconnect; 40 | function IsCronValueValid(const S: string): boolean; 41 | function IsConnected: boolean; 42 | function SelectSQL(const sql: string; params: array of string; out Output: string): boolean; 43 | procedure MoveTaskUp(const ATaskID: integer); 44 | procedure MoveTaskDown(const ATaskID: integer); 45 | function IsTaskDeleteAllowed: boolean; 46 | end; 47 | 48 | var 49 | dmPgEngine: TdmPgEngine; 50 | 51 | implementation 52 | 53 | uses uObjects, fmMain, fmConnect, Dialogs, System.UITypes; 54 | 55 | {$R *.lfm} 56 | 57 | { TdmPgEngine } 58 | 59 | procedure TdmPgEngine.connMainLog(Sender: TSQLConnection; 60 | EventType: TDBEventType; const Msg: String); 61 | const et: array[TDBEventType] of string = ('detCustom', 'detPrepare', 'detExecute', 62 | 'detFetch', 'detCommit', 'detRollBack', 'detParamValue', 'detActualSQL'); 63 | begin 64 | with MainForm.mmLog.Lines do 65 | begin 66 | Append(Format('[%s:] %s' + LineEnding, [et[EventType], Msg])) 67 | end; 68 | end; 69 | 70 | procedure TdmPgEngine.connMainLogin(Sender: TObject; Username, Password: string); 71 | begin 72 | if not fmConnect.EditDatabase(Sender as TPQConnection) then Abort(); 73 | end; 74 | 75 | procedure TdmPgEngine.qryChainsAfterClose(DataSet: TDataSet); 76 | begin 77 | with DataSet as TSQLQuery do 78 | IndexName := ''; 79 | end; 80 | 81 | procedure TdmPgEngine.qryChainsAfterDelete(DataSet: TDataSet); 82 | begin 83 | (DataSet as TSQLQuery).ApplyUpdates; 84 | end; 85 | 86 | procedure TdmPgEngine.qryChainsAfterInsert(DataSet: TDataSet); 87 | begin 88 | with DataSet do 89 | begin 90 | FieldByName('live').AsBoolean := True; 91 | FieldByName('self_destruct').AsBoolean := False; 92 | FieldByName('exclusive_execution').AsBoolean := False; 93 | FieldByName('run_at').AsString := '* * * * *'; 94 | FieldByName('timeout').AsInteger := 0; 95 | end; 96 | end; 97 | 98 | procedure TdmPgEngine.qryAfterPost(DataSet: TDataSet); 99 | var 100 | B: TBookmark; 101 | Q: TSQLQuery; 102 | begin 103 | Q := DataSet as TSQLQuery; 104 | Q.IndexName := ''; 105 | B := DataSet.GetBookmark; 106 | try 107 | Q.ApplyUpdates; 108 | DataSet.Refresh; 109 | except 110 | on E: EDatabaseError do 111 | begin 112 | MessageDlg('Database Error', E.Message, mtError, [mbOK], 0); 113 | Q.CancelUpdates; 114 | end; 115 | end; 116 | DataSet.GotoBookmark(B); 117 | if Q = qryChains then 118 | fmMain.MainForm.UpdateSortIndication(nil); 119 | end; 120 | 121 | procedure TdmPgEngine.qryChainsBeforeDelete(DataSet: TDataSet); 122 | begin 123 | if MessageDlg('Delete confirmation', 124 | 'Are you sure you want delete current chain?', mtWarning, [mbOK, mbCancel], 0) = mrCancel then 125 | Abort(); 126 | end; 127 | 128 | procedure TdmPgEngine.qryTasksAfterInsert(DataSet: TDataSet); 129 | begin 130 | with DataSet do 131 | begin 132 | FieldByName('chain_id').AsLargeInt := qryChains.FieldByName('chain_id').AsLargeInt; 133 | FieldByName('task_order').AsFloat := FLastTaskOrder + DEFAULT_ORDER_DELTA; 134 | FieldByName('kind').AsString := 'SQL'; 135 | FieldByName('ignore_error').AsBoolean := False; 136 | FieldByName('autonomous').AsBoolean := False; 137 | end; 138 | end; 139 | 140 | procedure TdmPgEngine.qryTasksAfterOpen(DataSet: TDataSet); 141 | begin 142 | DataSet.Last(); 143 | FLastTaskOrder := DataSet.FieldByName('task_order').AsInteger; 144 | end; 145 | 146 | procedure TdmPgEngine.qryTasksBeforePost(DataSet: TDataSet); 147 | begin 148 | if DataSet.FieldByName('command').IsNull then Abort(); 149 | end; 150 | 151 | procedure TdmPgEngine.Connect; 152 | begin 153 | qryChains.Open; 154 | qryTasks.Open; 155 | qryChains.First; 156 | end; 157 | 158 | procedure TdmPgEngine.Disconnect; 159 | begin 160 | connMain.Close(True); 161 | end; 162 | 163 | function TdmPgEngine.IsCronValueValid(const S: string): boolean; 164 | var 165 | Q: TSQLQuery; 166 | begin 167 | Result := True; 168 | Q := TSQLQuery.Create(nil); 169 | try 170 | Q.DataBase := connMain; 171 | Q.Transaction := connMain.Transaction; 172 | Q.SQL.Text := 'SELECT CAST(:cron AS timetable.cron)'; 173 | Q.ParamByName('cron').AsString := S; 174 | try 175 | Q.Open; 176 | except 177 | Exit(False); 178 | end; 179 | Q.Close; 180 | finally 181 | FreeAndNil(Q); 182 | end; 183 | end; 184 | 185 | function TdmPgEngine.IsConnected: boolean; 186 | begin 187 | Result := qryChains.Active and qryTasks.Active; 188 | end; 189 | 190 | function TdmPgEngine.SelectSQL(const sql: string; params: array of string; out Output: string): boolean; 191 | var 192 | Q: TSQLQuery; 193 | i: Integer; 194 | begin 195 | Result := True; 196 | Output := ''; 197 | Q := TSQLQuery.Create(nil); 198 | try 199 | Q.DataBase := connMain; 200 | Q.Transaction := connMain.Transaction; 201 | Q.SQL.Text := sql; 202 | for i := Low(params) to High(params) do 203 | Q.Params[i].AsString := params[i]; 204 | try 205 | Q.Open; 206 | while not Q.EOF do 207 | begin 208 | Output := Output + Q.Fields[0].AsString + LineEnding; 209 | Q.Next; 210 | end; 211 | except 212 | on E: Exception do 213 | begin 214 | Result := False; 215 | if E is EPQDatabaseError then 216 | Output := EPQDatabaseError(E).MESSAGE_PRIMARY 217 | else 218 | Output := E.Message; 219 | end; 220 | end; 221 | Q.Close; 222 | finally 223 | FreeAndNil(Q); 224 | end; 225 | end; 226 | 227 | procedure TdmPgEngine.MoveTaskUp(const ATaskID: integer); 228 | begin 229 | connMain.ExecuteDirect(Format('SELECT timetable.move_task_up(%d)', [ATaskID])); 230 | end; 231 | 232 | procedure TdmPgEngine.MoveTaskDown(const ATaskID: integer); 233 | begin 234 | connMain.ExecuteDirect(Format('SELECT timetable.move_task_down(%d)', [ATaskID])); 235 | end; 236 | 237 | function TdmPgEngine.IsTaskDeleteAllowed: boolean; 238 | begin 239 | Result := not qryTasks.BOF and not qryTasks.EOF; 240 | end; 241 | 242 | end. 243 | -------------------------------------------------------------------------------- /uobjects.pas: -------------------------------------------------------------------------------- 1 | unit uObjects; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils; 9 | 10 | type 11 | 12 | { TTask } 13 | 14 | TCommandKind = (ckSQL, ckPROGRAM, ckBUILTIN); 15 | 16 | TTask = class(TCollectionItem) 17 | private 18 | FTaskID: integer; 19 | FKind: TCommandKind; 20 | FName: string; 21 | FScript: string; 22 | function GetKind: string; 23 | procedure SetKind(AValue: string); 24 | procedure SetName(AValue: string); 25 | procedure SetScript(AValue: string); 26 | published 27 | property TaskID: integer read FTaskID write FTaskID; 28 | property Name: string read FName write SetName; 29 | property Script: string read FScript write SetScript; 30 | property Kind: string read GetKind write SetKind; 31 | end; 32 | 33 | { TChain } 34 | 35 | TChain = class(TCollectionItem) 36 | private 37 | FChainID: integer; 38 | FClientName: string; 39 | FExclusive: boolean; 40 | FLive: boolean; 41 | FMaxInstances: integer; 42 | FName: string; 43 | FRunAt: string; 44 | FSelfDestruct: boolean; 45 | FTasks: TCollection; 46 | procedure SetTasks(AValue: TCollection); 47 | public 48 | constructor Create(ACollection: TCollection); override; 49 | published 50 | property ChainID: integer read FChainID write FChainID; 51 | property Tasks: TCollection read FTasks write SetTasks; 52 | property Name: string read FName write FName; 53 | property RunAt: string read FRunAt write FRunAt; 54 | property MaxInstances: integer read FMaxInstances write FMaxInstances; 55 | property Live: boolean read FLive write FLive; 56 | property SelfDestruct: boolean read FSelfDestruct write FSelfDestruct; 57 | property Exclusive: boolean read FExclusive write FExclusive; 58 | property ClientName: string read FClientName write FClientName; 59 | end; 60 | 61 | 62 | 63 | const 64 | BuiltinCommands: array of string = ('NoOp', 'Download', 'Log'); 65 | 66 | implementation 67 | 68 | uses TypInfo; 69 | 70 | { TTask } 71 | procedure TChain.SetTasks(AValue: TCollection); 72 | begin 73 | if FTasks=AValue then Exit; 74 | FTasks:=AValue; 75 | end; 76 | 77 | constructor TChain.Create(ACollection: TCollection); 78 | begin 79 | inherited Create(ACollection); 80 | FTasks := TCollection.Create(TTask); 81 | end; 82 | 83 | { TCommand } 84 | 85 | procedure TTask.SetName(AValue: string); 86 | begin 87 | if FName = AValue then 88 | Exit; 89 | FName := AValue; 90 | end; 91 | 92 | function TTask.GetKind: string; 93 | begin 94 | Result := Copy(GetEnumName(TypeInfo(TCommandKind), Ord(FKind)), 3, MaxInt); 95 | end; 96 | 97 | procedure TTask.SetKind(AValue: string); 98 | begin 99 | FKind := TCommandKind(GetEnumValue(TypeInfo(TCommandKind), 'ck' + AValue)); 100 | end; 101 | 102 | procedure TTask.SetScript(AValue: string); 103 | begin 104 | if FScript = AValue then 105 | Exit; 106 | FScript := AValue; 107 | end; 108 | 109 | end. 110 | --------------------------------------------------------------------------------