Kirby CMS Git Widget

The Git Widget

The panel for Kirby CMS is, in my personal opinion, one of its best features. The UI is really easy to use, and pleasantly customizable. One issue that I have found, though, is there's no easy way to automatically update the Git repository for your project when changes are made in the panel if it is possible for merge conflicts to occur. When would this happen? Take my company website for example. Employees here are constantly updating the content on the website, and we like to keep the git repository up-to-date, so we push the changes each time they occur in the panel. However, there are times when a developer will make changes to some content from their local machine rather than from the server. If they do so, and then an employee attempts to change that same content, a merge conflict will occur. It's then a pretty big pain to manually go in and fix this merge conflict on the server.

In one of the more recent versions of Kirby came the release of a new feature called "Kirby hooks." These hooks are essentially functions that watch for changes in the panel and execute when they occur. Check out the documentation for Kirby hooks here.

With Kirby hooks, it's pretty simple to execute some shell commands that do a git add, git commit, git push every time the panel is updated, but that creates a potential problem – how do you handle merge conflicts? Also fairly new to Kirby CMS are custom widgets, which are essentially snippets of PHP and HTML that you can place on your Kirby dashboard. Using these two features I've created a "Git Widget" which checks for merge conflicts after some Kirby hooks are executed, and then allows the user to solve those merge conflicts, all in the browser. Here's how I did it.

Setting Up the Kirby Hooks

As per the Kirby documentation, you can place Kirby hooks in a plugin file or right in the config.php file. For our Git Widget, we're going to place the hooks inside the config.php file. Inside config.php, we will paste the following lines of code:

kirby()->hook('panel.page.create', function($page) { UpdateRepo(); });
kirby()->hook('panel.page.update', function($page) { UpdateRepo(); });
kirby()->hook('panel.page.delete', function($page) { UpdateRepo(); });
kirby()->hook('panel.page.sort', function($page) { UpdateRepo(); });
kirby()->hook('panel.page.hide', function($page) { UpdateRepo(); });
kirby()->hook('panel.page.move', function($page) { UpdateRepo(); });

function UpdateRepo(){
    git_update();
}

Each of the first six lines is using a Kirby hook to register a panel event to some action. For example, the first three hooks are saying, "When a new page is created, a page is updated, or a page is deleted in the panel, execute the 'UpdateRepo' function." The UpdateRepo function then calls another function, git_update, which will be located in our plugins folder. Let's write that function.

The Git Plugin

According to the documentation, Kirby plugin files should be located inside a folder located in the "plugins" directory that shares the same name with the plugin file itself. So, we'll make a new directory in the plugins folder called "git" and create a new file in the git directory called "git.php". Inside git.php, we'll include our git_update function that our Kirby hooks are calling. The git.php file will look something like this:

<?php
function git_update() {
    $index_dir = kirby()->roots()->index();
    chdir($index_dir);

    shell_exec("git config user.email 'your_email_here'");
    shell_exec("git config user.name 'your_name_here'");
    shell_exec("git config push.default simple");
    shell_exec("git add -A");
    shell_exec("git commit -m 'automatic commit from updated panel'");

    $pull_message = shell_exec("git pull");

    exec("git push", $out, $status);

    site()->update(array(
        'pull_message' => $pull_message,
        'push_status' => $status
    ));
}
?>

It's pretty straightforward, but I'll break down what this function is doing. First it changes the working directory to be the root directory of the project folder, and then executes some shell commands which set up the git configuration for your user (Note that this file will want to be added to a .gitignore so each user of the application can have their own user info here). It then attempts to do a git pull/git push to the git repository. It stores the output from these commands in two variables, $pull_message and $status. It then updates the global site variables "pull_message" and "push_status" with those two variables. We now have two global variables that contain the output from our attempted git pull and git push commands that will tell us whether or not a merge conflict has occurred. We can access these variables from the panel inside a custom widget. Let's build that widget.

The Git Widget

If you've never made any Kirby widgets before, you'll need to make the widgets folder. If so, create a new directory inside your "site" folder called "widgets". Inside this new directory, make a sub-directory called "gitwidget" and create two files in the sub-directory called "gitwidget.php" and "gittemplate.php". The file gitwidget.php file will be where we search our global site variables to determine whether or not any merge conflicts have occurred, and get the filenames in which they occurred. The file gittemplate.php will search these files for the merge conflicts and display them in a custom widget which will be located in your panel dashboard.

The code for these two files is a bit too long for this blog post, so I've put them in a public repository here.

Under normal circumstances, when there are no merge conflicts, the git widget will appear on your dashboard and look like this:

gitwidget_good

However, if a merge conflict were to occur, then the widget would inform the user of the conflict and display it, like this:

gitwidget_bad

When would this occur? If there are multiple users editing content in the Kirby panel for a project and they save their changes one after the other, a merge conflict arises. There are several Kirby CMS Automatic Git widgets available as of recently, but I am not sure any of them actually informs the user if a merge conflict has occurred and helps them fix it.

With this git widget, if a merge conflict occurs, the user can click on the "Click here to fix" button to be directed to a new page. Once there, they can directly edit the lines from the files in which the merge conflicts occurred. That page will look something like this:

gittemplate

Of course, we'll need the code for this page, which I've provided in the same Github repository as the git widget code, located here. In summary, the page contains a form that allows the user to manually edit the merge conflicts and then a PHP script which executes upon submission of the form. The script visits each of the conflicted files and makes the changes as per the user's form edits, and then calls the git_update function from before to attempt to push the fixed file changes. If all goes well then the dashboard should display a pleasant green message saying that there are no merge conflicts.

Conclusion

That's all, folks. If you want to see the widget in action, you can clone the repository for this blog here and edit the user info so you can open up the panel yourself. If you have any questions don't hesitate to leave a comment below.


blog comments powered by Disqus