Skip to content

Commit

Permalink
Merge branch 'develop', bump version to 0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
machitgarha committed Sep 28, 2022
2 parents d766863 + ca8803b commit 4aeae6f
Show file tree
Hide file tree
Showing 109 changed files with 10,472 additions and 0 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Build release files

on:
push:
tags: "*"
release:
types: [created]

jobs:
build-zip:
name: Build plugin ZIP file
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "7.2"

- name: Install Composer dependencies
uses: ramsey/composer-install@v2
with:
composer-options: "--no-dev --optimize-autoloader"

- name: Build ZIP file
run: ./build-aux/build-zip.sh

- name: Upload release files
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build/limesurvey-rest-api.zip
asset_name: limesurvey-rest-api.zip
tag: ${{ github.ref }}
prerelease: true
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/vendor/

/build/*
!/build/.gitkeep
355 changes: 355 additions & 0 deletions .phan/config.php

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# LimeSurvey REST API

A [LimeSurvey](https://github.com/LimeSurvey/LimeSurvey) plugin providing RESTful API. It's a (yet-incomplete) replacement for LimeSurvey's built-in JSON RPC interface.

## Features

The main features are:

- 🤩 **Simple and intuitive.** Start using and see.

- 🖹 **Properly documented.** With the help of a proper OpenAPI spec, an up-to-date documentation is always in front of your eyes. It should help you even if you're not familiar with LS core.

- 🔍 **Strong validation.** Plus automatic spec-based validation (which is cached for performance) thanks to [openapi-psr7-validator](https://github.com/thephpleague/openapi-psr7-validator), it relies on validations done by the core (and providing extra validations when needed). It also supports the LS permission model.

- 📦 **Docker-ready.** It auto-registers and activates itself on LimeSurvey, e.g. making it easy to be used in [limesurvey-docker](https://github.com/adamzammit/limesurvey-docker).

## Requirements

- LimeSurvey >= 5.3
- PHP >= 7.2

## How to Install?

Download the latest ZIP file from [Releases](https://github.com/machitgarha/limesurvey-rest-api/releases). Then in LimeSurvey, from the top bar, click on "Configuration", go to "Plugins", click on "Upload and install" at the top, and select the downloaded file.

### Updating

Repeat the steps above to update the plugin, with one more step: You have to deactivate and activate the plugin for the cache to be cleared.

## Documentation

You can generate the documentation using any OpenAPI documentation generator. We recommend using [redoc-cli](https://github.com/Redocly/redoc).

Example of generating the docs using it:

```sh
redoc-cli build spec/openapi.yaml -o build/docs.html
```

Running this, you can use the HTML file (`build/docs.html`) as documentation.

## What is Implemented?

This plugin has limited functionality, i.e. it doesn't provide an endpoint for all actions. It however provides the required functionality for filling survey answers.

More precisely, you can log in as a user, get the information of surveys, their questions and question groups, and send responses for surveys. Note that, for responses, all question types are supported.

## Contributions, Please! :)

I kindly ask you that, if you have time and the ability to complete unimplemented parts, or fix or implement one of the [issues](https://github.com/machitgarha/limesurvey-rest-api/issues), then start doing so, we'll appreciate your effort for sure (and everyone will benefit from it).

## License

[GPLv2](./LICENSE.md)
9 changes: 9 additions & 0 deletions RestApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

require __DIR__ . "/vendor/autoload.php";

use MAChitgarha\LimeSurveyRestApi\Plugin;

class RestApi extends Plugin
{
}
31 changes: 31 additions & 0 deletions build-aux/build-zip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/sh

# Config
pluginName="RestApi"
outputFile="build/limesurvey-rest-api.zip"

# Exit on error
set -e

mainRepoDir="$(dirname "$(realpath "$0/..")")"
# The resulting file path
zipFilePath="$mainRepoDir/$outputFile"

cd "$mainRepoDir"

# Composer dev dependencies are huge.
echo "Removing Composer dev dependencies..."
composer --no-dev install > /dev/null 2>&1

# Remove previous zip file to prevent extra removed files to remain there
if [[ -e "$zipFilePath" ]]; then
rm "$zipFilePath"
fi

# Create the zip file in the current directory
zip -r -y "$zipFilePath" "spec/" "src/" "vendor/" "config.xml" "LICENSE.md" "$pluginName.php" > /dev/null

echo "Zip file created successfully."

echo "Re-adding Composer dev dependencies..."
composer install > /dev/null 2>&1
33 changes: 33 additions & 0 deletions build-aux/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM php:7.2-alpine

ARG PLUGIN_SOURCE_DIR=/plugin-src
ARG PLUGIN_VOLUME_DIR=/plugin

ENV PLUGIN_SOURCE_DIR=$PLUGIN_SOURCE_DIR \
PLUGIN_VOLUME_DIR=$PLUGIN_VOLUME_DIR

ENV LIMESURVEY_DB_HOST=mysql \
LIMESURVEY_DB_NAME=limesurvey \
LIMESURVEY_DB_USER=root \
LIMESURVEY_DB_PASSWORD='' \
LIMESURVEY_TABLE_PREFIX='' \
MYSQL_SSL_CA_CONTENTS=''

RUN \
# TODO: Maybe get the Composer phar file ourselves?
apk add composer && \
# To insert plugin data to plugins database
docker-php-ext-install mysqli

# Supposing the build context is the repo root, i.e. run it with -f when building
COPY . "$PLUGIN_SOURCE_DIR"

RUN mv "$PLUGIN_SOURCE_DIR/build-aux/docker/entrypoint.sh" /usr/bin

RUN cd "$PLUGIN_SOURCE_DIR" && \
# Make sure to use PHP 7.2, not the one installed by APK (e.g. 7.3)
/usr/local/bin/php /usr/bin/composer install --no-dev

VOLUME ["$PLUGIN_VOLUME_DIR"]

ENTRYPOINT ["entrypoint.sh"]
26 changes: 26 additions & 0 deletions build-aux/docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/sh

# Exit on error
set -eu

insertPluginToDatabase() {
"$PLUGIN_SOURCE_DIR"/build-aux/docker/wait-for "$LIMESURVEY_DB_HOST:3306" -t 30 -- \
echo "MySQL service detected!" && \
php "$PLUGIN_SOURCE_DIR"/build-aux/docker/register-plugin.php \
"$LIMESURVEY_DB_HOST" \
"$LIMESURVEY_DB_USER" \
"$LIMESURVEY_DB_PASSWORD" \
"$LIMESURVEY_DB_NAME" \
"$LIMESURVEY_TABLE_PREFIX" \
"$MYSQL_SSL_CA_CONTENTS"
}

main() {
cp -a "$PLUGIN_SOURCE_DIR"/* "$PLUGIN_VOLUME_DIR"

insertPluginToDatabase

sleep infinity
}

main
141 changes: 141 additions & 0 deletions build-aux/docker/register-plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php
/**
* Inserts the plugin record to the database.
*/

function error(string $message)
{
file_put_contents('php://stderr', $message . PHP_EOL, FILE_APPEND);
exit(1);
}

function makeTempSslCaFile(string $contents)
{
$file = tmpfile();
if ($file === false) {
error('Cannot create temporary file to store MySQL SSL CA');
}

fwrite($file, $contents);
return $file;
}

function mysqlConnect(array $argv)
{
$mysql = mysqli_init();

$sslCaFile = empty($argv[6]) ? null : makeTempSslCaFile($argv[6]);
if ($sslCaFile !== null) {
$mysql->ssl_set(null, null, stream_get_meta_data($sslCaFile)['uri'], null, null);
}

[$host, $socket] = explode(':', $argv[1], 2);
$port = 0;
if (is_numeric($socket)) {
$port = (int) $socket;
$socket = null;
}

$successful = $mysql->real_connect(
$host,
$argv[2],
$argv[3],
'',
$port,
$socket,
MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT
);

if (!$successful) {
error(
"Cannot connect to MySQL: {$mysql->connect_error} " .
"(error code {$mysql->connect_error})"
);
}

return $mysql;
}

function isDatabaseExists(\mysqli $mysql, string $databaseName): bool
{
$result = $mysql->query(<<<SQL
SHOW DATABASES LIKE '$databaseName'
SQL
);

return $result->num_rows === 1;
}

function makeTableName(string $tablePrefix): string
{
if (!preg_match('/^[a-z0-9_$]*$/i', $tablePrefix)) {
error('Table prefix contains invalid characters');
}

return $tablePrefix . 'plugins';
}

function isTableExists(\mysqli $mysql, string $tableName): bool
{
$result = $mysql->query(<<<SQL
SHOW TABLES LIKE '$tableName'
SQL
);

return $result->num_rows === 1;
}

function isPluginAlreadyInserted(\mysqli $mysql, string $tableName): bool
{
$result = $mysql->query(<<<SQL
SELECT `id` FROM `$tableName` WHERE `name` = 'RestApi'
SQL
);

return $result->num_rows > 0;
}

function insertPlugin(\mysqli $mysql, string $tableName): bool
{
return $mysql->query(<<<SQL
INSERT INTO `$tableName`(`name`, `plugin_type`, `active`)
VALUES ('RestApi', 'upload', 1)
SQL
);
}

function main(array $argv)
{
echo 'Registering the plugin...' . PHP_EOL;

$mysql = mysqlConnect($argv);

$databaseName = $mysql->real_escape_string($argv[4]);

while (!isDatabaseExists($mysql, $databaseName)) {
echo 'Waiting for the database to be created...' . PHP_EOL;
sleep(3);
}

$mysql->select_db($databaseName);

$pluginsTableName = makeTableName($argv[5]);

while (!isTableExists($mysql, $pluginsTableName)) {
echo "Waiting for the $pluginsTableName table to be created..." . PHP_EOL;
sleep(3);
}

if (isPluginAlreadyInserted($mysql, $pluginsTableName)) {
echo 'Plugin already registered, nothing to do.' . PHP_EOL;
return;
}

if (!insertPlugin($mysql, $pluginsTableName)) {
error("Cannot insert a new record to $pluginsTableName table!");
}

echo 'Plugin registered and activated successfully...!' . PHP_EOL;
}

main($argv);
Loading

0 comments on commit 4aeae6f

Please sign in to comment.