mirror of
https://github.com/nextcloud/nextcloudpi.git
synced 2026-01-09 06:32:00 -03:30
Add ncp-community.sh and admin section to ncp-app
Signed-off-by: Tobias K <6317548+theCalcaholic@users.noreply.github.com>
This commit is contained in:
parent
50c09041dc
commit
3958883e62
@ -16,7 +16,9 @@ git clone -b "$BRANCH" --depth 20 -q --bare https://github.com/nextcloud/nextclo
|
||||
cd "$TEMPDIR" || exit 1
|
||||
VER=$( git describe --always --tags | grep -oP "v\d+\.\d+\.\d+" )
|
||||
|
||||
[[ -f "/usr/local/etc/instance.cfg" ]] && {
|
||||
canary="$(get_app_param ncp-community.sh CANARY)"
|
||||
|
||||
[[ "$canary" != "yes" ]] && [[ -f "/usr/local/etc/instance.cfg" ]] && {
|
||||
cohorte_id="$(jq .cohorteId /usr/local/etc/instance.cfg)"
|
||||
[[ -f "./staged_rollouts/${VER}.txt" ]] && {
|
||||
grep "^${cohorte_id}$" "./staged_rollouts/${VER}.txt" || {
|
||||
|
||||
@ -77,7 +77,6 @@ fi
|
||||
cat > /usr/local/etc/instance.cfg <<EOF
|
||||
{
|
||||
"cohorteId": ${cohorte_id},
|
||||
"canary": false
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
7
bin/ncp/CONFIG/ncp-community.sh
Normal file
7
bin/ncp/CONFIG/ncp-community.sh
Normal file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configure various settings for community participation
|
||||
|
||||
install() { :; }
|
||||
|
||||
configure() { :; }
|
||||
@ -212,6 +212,29 @@ function find_app_param_num()
|
||||
|
||||
}
|
||||
|
||||
function get_app_params() {
|
||||
local script="${1?}"
|
||||
local cfg_file="${CFGDIR}/${script%.sh}.cfg"
|
||||
[[ -f "$cfg_file" ]] && {
|
||||
local cfg="$( cat "$cfg_file" )"
|
||||
local param_count="$(jq ".params | length" <<<"$cfg")"
|
||||
local i=0
|
||||
local json="{"$'\n'
|
||||
while [[ $i -lt $param_count ]]
|
||||
do
|
||||
param_id="$(jq -r ".params[$i].id" <<<"$cfg")"
|
||||
param_val="$(jq -r ".params[$i].value" <<<"$cfg")"
|
||||
json="${json} \"${param_id}\": \"${param_val}\","$'\n'
|
||||
i=$((i+1))
|
||||
done
|
||||
json="${json}}"
|
||||
echo "$json"
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
install_template() {
|
||||
local template="${1?}"
|
||||
local target="${2?}"
|
||||
|
||||
35
etc/ncp-config.d/ncp-community.cfg
Normal file
35
etc/ncp-config.d/ncp-community.cfg
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"id": "ncp-community",
|
||||
"name": "NCP Community",
|
||||
"title": "NCP Community Settings",
|
||||
"description": "Configure various NCP community options",
|
||||
"info": "",
|
||||
"infotitle": "",
|
||||
"params": [
|
||||
{
|
||||
"id": "CANARY",
|
||||
"name": "Enable canary (testing) channel for updates",
|
||||
"value": "no",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"id": "ADMIN_NOTIFICATIONS",
|
||||
"name": "Enable notifications about changes in NCP",
|
||||
"value": "yes",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"id": "USAGE_SURVEYS",
|
||||
"name": "Help me improve NCP by participating in occasional usage surveys",
|
||||
"value": "no",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"id": "NOTIFICATION_ACCOUNTS",
|
||||
"name": "Limit notifications to these accounts",
|
||||
"value": "",
|
||||
"suggest": "Comma separated list of nextcloud accounts",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -84,14 +84,6 @@ rm /.ncp-image
|
||||
cd -
|
||||
rm -rf "${TEMPDIR}"
|
||||
|
||||
cohorte_id=$((RANDOM % 100))
|
||||
cat <<EOF > /usr/local/etc/instance.cfg
|
||||
{
|
||||
"cohorteId": ${cohorte_id},
|
||||
"canary": false
|
||||
}
|
||||
EOF
|
||||
|
||||
IP="$(get_ip)"
|
||||
|
||||
echo "Done.
|
||||
|
||||
7
ncp-app/.eslintrc.js
Normal file
7
ncp-app/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
module.exports = {
|
||||
extends: [
|
||||
'@nextcloud',
|
||||
]
|
||||
}
|
||||
3
ncp-app/.gitattributes
vendored
Normal file
3
ncp-app/.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
/js/* binary
|
||||
9
ncp-app/.gitignore
vendored
Normal file
9
ncp-app/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
.idea
|
||||
*.iml
|
||||
/vendor/
|
||||
/build/
|
||||
node_modules/
|
||||
/.php_cs.cache
|
||||
js/*hot-update.*
|
||||
20
ncp-app/.php_cs-fixer.dist.php
Normal file
20
ncp-app/.php_cs-fixer.dist.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
require_once './vendor/autoload.php';
|
||||
|
||||
use Nextcloud\CodingStandard\Config;
|
||||
|
||||
$config = new Config();
|
||||
$config
|
||||
->getFinder()
|
||||
->ignoreVCSIgnored(true)
|
||||
->notPath('build')
|
||||
->notPath('l10n')
|
||||
->notPath('src')
|
||||
->notPath('vendor')
|
||||
->in(__DIR__);
|
||||
return $config;
|
||||
164
ncp-app/Makefile
Normal file
164
ncp-app/Makefile
Normal file
@ -0,0 +1,164 @@
|
||||
# SPDX-FileCopyrightText: Bernhard Posselt <dev@bernhard-posselt.com>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# Generic Makefile for building and packaging a Nextcloud app which uses npm and
|
||||
# Composer.
|
||||
#
|
||||
# Dependencies:
|
||||
# * make
|
||||
# * which
|
||||
# * curl: used if phpunit and composer are not installed to fetch them from the web
|
||||
# * tar: for building the archive
|
||||
# * npm: for building and testing everything JS
|
||||
#
|
||||
# If no composer.json is in the app root directory, the Composer step
|
||||
# will be skipped. The same goes for the package.json which can be located in
|
||||
# the app root or the js/ directory.
|
||||
#
|
||||
# The npm command by launches the npm build script:
|
||||
#
|
||||
# npm run build
|
||||
#
|
||||
# The npm test command launches the npm test script:
|
||||
#
|
||||
# npm run test
|
||||
#
|
||||
# The idea behind this is to be completely testing and build tool agnostic. All
|
||||
# build tools and additional package managers should be installed locally in
|
||||
# your project, since this won't pollute people's global namespace.
|
||||
#
|
||||
# The following npm scripts in your package.json install and update the bower
|
||||
# and npm dependencies and use gulp as build system (notice how everything is
|
||||
# run from the node_modules folder):
|
||||
#
|
||||
# "scripts": {
|
||||
# "test": "node node_modules/gulp-cli/bin/gulp.js karma",
|
||||
# "prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
|
||||
# "build": "node node_modules/gulp-cli/bin/gulp.js"
|
||||
# },
|
||||
|
||||
app_name=$(notdir $(CURDIR))
|
||||
build_tools_directory=$(CURDIR)/build/tools
|
||||
source_build_directory=$(CURDIR)/build/artifacts/source
|
||||
source_package_name=$(source_build_directory)/$(app_name)
|
||||
appstore_build_directory=$(CURDIR)/build/artifacts
|
||||
appstore_package_name=$(appstore_build_directory)/$(app_name)
|
||||
npm=$(shell which npm 2> /dev/null)
|
||||
composer=$(shell which composer 2> /dev/null)
|
||||
|
||||
all: build
|
||||
|
||||
# Fetches the PHP and JS dependencies and compiles the JS. If no composer.json
|
||||
# is present, the composer step is skipped, if no package.json or js/package.json
|
||||
# is present, the npm step is skipped
|
||||
.PHONY: build
|
||||
build:
|
||||
ifneq (,$(wildcard $(CURDIR)/composer.json))
|
||||
make composer
|
||||
endif
|
||||
ifneq (,$(wildcard $(CURDIR)/package.json))
|
||||
make npm
|
||||
endif
|
||||
ifneq (,$(wildcard $(CURDIR)/js/package.json))
|
||||
make npm
|
||||
endif
|
||||
cp js-src/*.js js/
|
||||
|
||||
# Installs and updates the composer dependencies. If composer is not installed
|
||||
# a copy is fetched from the web
|
||||
.PHONY: composer
|
||||
composer:
|
||||
ifeq (, $(composer))
|
||||
@echo "No composer command available, downloading a copy from the web"
|
||||
mkdir -p $(build_tools_directory)
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
mv composer.phar $(build_tools_directory)
|
||||
php $(build_tools_directory)/composer.phar install --prefer-dist
|
||||
else
|
||||
composer install --prefer-dist
|
||||
endif
|
||||
|
||||
# Installs npm dependencies
|
||||
.PHONY: npm
|
||||
npm:
|
||||
ifeq (,$(wildcard $(CURDIR)/package.json))
|
||||
cd js && $(npm) run build
|
||||
else
|
||||
npm run build
|
||||
endif
|
||||
|
||||
# Removes the appstore build
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf ./build
|
||||
|
||||
# Same as clean but also removes dependencies installed by composer, bower and
|
||||
# npm
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
rm -rf vendor
|
||||
rm -rf node_modules
|
||||
rm -rf js/vendor
|
||||
rm -rf js/node_modules
|
||||
|
||||
# Builds the source and appstore package
|
||||
.PHONY: dist
|
||||
dist:
|
||||
make source
|
||||
make appstore
|
||||
|
||||
# Builds the source package
|
||||
.PHONY: source
|
||||
source:
|
||||
rm -rf $(source_build_directory)
|
||||
mkdir -p $(source_build_directory)
|
||||
tar cvzf $(source_package_name).tar.gz \
|
||||
--exclude-vcs \
|
||||
--exclude="../$(app_name)/build" \
|
||||
--exclude="../$(app_name)/js/node_modules" \
|
||||
--exclude="../$(app_name)/node_modules" \
|
||||
--exclude="../$(app_name)/*.log" \
|
||||
--exclude="../$(app_name)/js/*.log" \
|
||||
../$(app_name) \
|
||||
|
||||
# Builds the source package for the app store, ignores php tests, js tests
|
||||
# and build related folders that are unnecessary for an appstore release
|
||||
.PHONY: appstore
|
||||
appstore:
|
||||
rm -rf $(appstore_build_directory)
|
||||
mkdir -p $(appstore_build_directory)
|
||||
tar cvzf $(appstore_package_name).tar.gz \
|
||||
--exclude-vcs \
|
||||
--exclude="../$(app_name)/build" \
|
||||
--exclude="../$(app_name)/tests" \
|
||||
--exclude="../$(app_name)/Makefile" \
|
||||
--exclude="../$(app_name)/*.log" \
|
||||
--exclude="../$(app_name)/phpunit*xml" \
|
||||
--exclude="../$(app_name)/composer.*" \
|
||||
--exclude="../$(app_name)/node_modules" \
|
||||
--exclude="../$(app_name)/js/node_modules" \
|
||||
--exclude="../$(app_name)/js/tests" \
|
||||
--exclude="../$(app_name)/js/test" \
|
||||
--exclude="../$(app_name)/js/*.log" \
|
||||
--exclude="../$(app_name)/js/package.json" \
|
||||
--exclude="../$(app_name)/js/bower.json" \
|
||||
--exclude="../$(app_name)/js/karma.*" \
|
||||
--exclude="../$(app_name)/js/protractor.*" \
|
||||
--exclude="../$(app_name)/package.json" \
|
||||
--exclude="../$(app_name)/bower.json" \
|
||||
--exclude="../$(app_name)/karma.*" \
|
||||
--exclude="../$(app_name)/protractor\.*" \
|
||||
--exclude="../$(app_name)/.*" \
|
||||
--exclude="../$(app_name)/js/.*" \
|
||||
--exclude="../$(app_name)/webpack.config.js" \
|
||||
--exclude="../$(app_name)/stylelint.config.js" \
|
||||
--exclude="../$(app_name)/CHANGELOG.md" \
|
||||
--exclude="../$(app_name)/README.md" \
|
||||
--exclude="../$(app_name)/package-lock.json" \
|
||||
--exclude="../$(app_name)/LICENSES" \
|
||||
../$(app_name) \
|
||||
|
||||
.PHONY: test
|
||||
test: composer
|
||||
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.xml
|
||||
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
|
||||
@ -2,17 +2,17 @@
|
||||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||
<id>nextcloudpi</id>
|
||||
<name>NextCloudPi</name>
|
||||
<name>NextcloudPi</name>
|
||||
<summary>Nextcloud management tools</summary>
|
||||
<description><![CDATA[NextCloudPi features a preconfigured Nextcloud instance and a complete set of tools around it for easy management.]]></description>
|
||||
<version>0.0.1</version>
|
||||
<version>0.0.2</version>
|
||||
<licence>agpl</licence>
|
||||
<author mail="nachoparker@ownyourbits.com" homepage="https://ownyourbits.com">nachoparker</author>
|
||||
<namespace>NextCloudPi</namespace>
|
||||
<namespace>NextcloudPi</namespace>
|
||||
<category>tools</category>
|
||||
<bugs>https://github.com/nextcloud/nextcloudpi/issues</bugs>
|
||||
<dependencies>
|
||||
<nextcloud min-version="14" max-version="27"/>
|
||||
<nextcloud min-version="22" max-version="27"/>
|
||||
</dependencies>
|
||||
<navigations>
|
||||
<navigation>
|
||||
@ -20,4 +20,7 @@
|
||||
<route>nextcloudpi.page.index</route>
|
||||
</navigation>
|
||||
</navigations>
|
||||
<settings>
|
||||
<admin>OCA\NextcloudPi\Settings\AdminSettings</admin>
|
||||
</settings>
|
||||
</info>
|
||||
|
||||
@ -2,13 +2,14 @@
|
||||
/**
|
||||
* Create your routes in here. The name is the lowercase name of the controller
|
||||
* without the controller part, the stuff after the hash is the method.
|
||||
* e.g. page#index -> OCA\NextCloudPi\Controller\PageController->index()
|
||||
* e.g. page#index -> OCA\NextcloudPi\Controller\PageController->index()
|
||||
*
|
||||
* The controller class has to be registered in the application.php file since
|
||||
* it's instantiated in there
|
||||
*/
|
||||
return [
|
||||
'routes' => [
|
||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||
]
|
||||
'routes' => [
|
||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'settings#save', 'url' => '/api/settings', 'verb' => 'POST']
|
||||
]
|
||||
];
|
||||
|
||||
5
ncp-app/babel.config.js
Normal file
5
ncp-app/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
const babelConfig = require('@nextcloud/babel-config')
|
||||
|
||||
module.exports = babelConfig
|
||||
38
ncp-app/composer.json
Normal file
38
ncp-app/composer.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "nextcloud/nextcloudpi",
|
||||
"description": "NextcloudPi App",
|
||||
"type": "project",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tobias Knöppler"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"sabre/dav": "^4.1",
|
||||
"sabre/xml": "^2.2",
|
||||
"symfony/event-dispatcher": "^5.3.11",
|
||||
"christophwurst/nextcloud": "dev-master@dev",
|
||||
"psalm/phar": "^4.10",
|
||||
"nextcloud/coding-standard": "^1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "find nc-app/nextcloudpi -name \\\\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
|
||||
"cs:check": "php-cs-fixer fix --dry-run --diff",
|
||||
"cs:fix": "php-cs-fixer fix",
|
||||
"psalm": "psalm.phar --threads=1",
|
||||
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
|
||||
"psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
|
||||
"psalm:clear": "psalm.phar --clear-cache && psalm --clear-global-cache",
|
||||
"psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true
|
||||
},
|
||||
"platform": {
|
||||
"php": "7.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
2839
ncp-app/composer.lock
generated
Normal file
2839
ncp-app/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
ncp-app/css/admin.css
Normal file
38
ncp-app/css/admin.css
Normal file
@ -0,0 +1,38 @@
|
||||
#nextcloudpi li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: start;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
#nextcloudpi li div:first-of-type {
|
||||
width: 10em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#nextcloudpi li * {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
#nextcloudpi hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#nextcloudpi .error-message {
|
||||
padding: 1em;
|
||||
color: red;
|
||||
border-width: 1px;
|
||||
border-color: red;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
#nextcloudpi input[name="notificationAccounts"] {
|
||||
width: 27em;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: .5em;
|
||||
border-top-width: 1px;
|
||||
border-top-style: dotted;
|
||||
border-top-color: var(--color-main-text);
|
||||
}
|
||||
6
ncp-app/js-src/script.js
Normal file
6
ncp-app/js-src/script.js
Normal file
@ -0,0 +1,6 @@
|
||||
// open the NCP web panel
|
||||
|
||||
var url = window.location.protocol + '//' + window.location.hostname + ':4443';
|
||||
|
||||
if ( !window.open( url, '_blank' ) ) // try to open in a new tab first
|
||||
window.location.href = url;
|
||||
2
ncp-app/js/nextcloudpi-admin.js
Normal file
2
ncp-app/js/nextcloudpi-admin.js
Normal file
File diff suppressed because one or more lines are too long
1
ncp-app/js/nextcloudpi-admin.js.map
Normal file
1
ncp-app/js/nextcloudpi-admin.js.map
Normal file
File diff suppressed because one or more lines are too long
16
ncp-app/lib/AppInfo/Application.php
Normal file
16
ncp-app/lib/AppInfo/Application.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\NextcloudPi\AppInfo;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
|
||||
class Application extends App {
|
||||
public const APP_ID = 'nextcloudpi';
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(self::APP_ID);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace OCA\NextCloudPi\Controller;
|
||||
namespace OCA\NextcloudPi\Controller;
|
||||
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
|
||||
46
ncp-app/lib/Controller/SettingsController.php
Normal file
46
ncp-app/lib/Controller/SettingsController.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace OCA\NextcloudPi\Controller;
|
||||
|
||||
use OCA\NextcloudPi\Exceptions\InvalidSettingsException;
|
||||
use OCA\NextcloudPi\Exceptions\SaveSettingsException;
|
||||
use OCA\NextcloudPi\Service\SettingsService;
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Controller;
|
||||
|
||||
class SettingsController extends Controller {
|
||||
|
||||
/** @var SettingsService */
|
||||
private $service;
|
||||
|
||||
|
||||
/**
|
||||
* SettingsController constructor
|
||||
* @param SettingsService $service
|
||||
*/
|
||||
public function __construct(SettingsService $service) {
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @NoCSRFRequired
|
||||
* @CORS
|
||||
*
|
||||
* @param array $settings
|
||||
*/
|
||||
public function save(array $settings): JSONResponse {
|
||||
try {
|
||||
$this->service->saveSettings($settings);
|
||||
return new JSONResponse([]);
|
||||
} catch(InvalidSettingsException $e) {
|
||||
return new JSONResponse(["error" => $e->getMessage()], Http::STATUS_BAD_REQUEST);
|
||||
} catch(SaveSettingsException $e) {
|
||||
return new JSONResponse(["error" => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
11
ncp-app/lib/Exceptions/InvalidSettingsException.php
Normal file
11
ncp-app/lib/Exceptions/InvalidSettingsException.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\NextcloudPi\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidSettingsException extends Exception {
|
||||
}
|
||||
|
||||
?>
|
||||
11
ncp-app/lib/Exceptions/SaveSettingsException.php
Normal file
11
ncp-app/lib/Exceptions/SaveSettingsException.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\NextcloudPi\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SaveSettingsException extends Exception {
|
||||
}
|
||||
|
||||
?>
|
||||
96
ncp-app/lib/Service/SettingsService.php
Normal file
96
ncp-app/lib/Service/SettingsService.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\NextcloudPi\Service;
|
||||
|
||||
use OCA\NextcloudPi\Exceptions\InvalidSettingsException;
|
||||
use OCA\NextcloudPi\Exceptions\SaveSettingsException;
|
||||
|
||||
class SettingsService {
|
||||
|
||||
|
||||
/**
|
||||
* @param $name string of the config
|
||||
* @param array $defaults Default value to use if the config can't be loaded
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig(string $name, array $defaults): array
|
||||
{
|
||||
[$ret, $config_str, $stderr] = $this->runCommand( "bash -c \"sudo /home/www/ncp-app-bridge.sh config $name\"");
|
||||
$config = null;
|
||||
if ($ret == 0) {
|
||||
$config = json_decode($config_str, true);
|
||||
}
|
||||
if ($config == null) {
|
||||
error_log("Failed to retrieve ncp config (exit code: $ret)");
|
||||
return $defaults;
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
/**
|
||||
* @param $name string of the config
|
||||
* @param string $defaults Default value to use if the file can't be loaded
|
||||
* @return string
|
||||
*/
|
||||
public function getFileContent(string $name, string $defaults): string
|
||||
{
|
||||
[$ret, $file_contents, $stderr] = $this->runCommand( "bash -c \"sudo /home/www/ncp-app-bridge.sh file $name\"");
|
||||
if ($ret != 0) {
|
||||
return $defaults;
|
||||
}
|
||||
return $file_contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSettingsException
|
||||
* @throws SaveSettingsException
|
||||
*/
|
||||
public function saveSettings(array $settings) {
|
||||
$parseBool = function ($val): string {
|
||||
return $val ? "yes" : "no";
|
||||
};
|
||||
$identityFn = function ($val) {
|
||||
return $val;
|
||||
};
|
||||
|
||||
$settings_map = [
|
||||
"CANARY" => ["ncp-community", "CANARY", $parseBool],
|
||||
"ADMIN_NOTIFICATIONS" => ["ncp-community", "ADMIN_NOTIFICATIONS", $parseBool],
|
||||
"USAGE_SURVEYS" => ["ncp-community", "USAGE_SURVEYS", $parseBool],
|
||||
"NOTIFICATION_ACCOUNTS" => ["ncp-community", "NOTIFICATION_ACCOUNTS", $identityFn]
|
||||
];
|
||||
|
||||
foreach ($settings as $k => $value) {
|
||||
[$cfgName, $fieldName, $fn] = $settings_map[$k];
|
||||
if ($cfgName == null || $fieldName == null) {
|
||||
throw new InvalidSettingsException("key error for '$k'");
|
||||
}
|
||||
$parsed = $fn($value);
|
||||
$cmd = "bash -c \"sudo /home/www/ncp-app-bridge.sh config '$cfgName' '$fieldName=$parsed'\"";
|
||||
[$ret, $stdout, $stderr] = $this->runCommand($cmd);
|
||||
if ($ret !== 0) {
|
||||
throw new SaveSettingsException(
|
||||
"Failed to save NCP settings '$cfgName/$fieldName': \n error output from command:\n\n$cmd"
|
||||
. str_replace("\n", "\n> ", $stderr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function runCommand(string $cmd): array {
|
||||
$descriptorSpec = [
|
||||
0 => ["pipe", "r"],
|
||||
1 => ["pipe", "w"],
|
||||
2 => ["pipe", "w"]
|
||||
];
|
||||
|
||||
$proc = proc_open($cmd, $descriptorSpec, $pipes, "/home/www-data", null);
|
||||
$stdout = stream_get_contents($pipes[1]);
|
||||
fclose($pipes[1]);
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[2]);
|
||||
return [proc_close($proc), $stdout, $stderr];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
59
ncp-app/lib/Settings/AdminSettings.php
Normal file
59
ncp-app/lib/Settings/AdminSettings.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace OCA\NextcloudPi\Settings;
|
||||
|
||||
use OCA\NextcloudPi\Service\SettingsService;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\Settings\ISettings;
|
||||
|
||||
class AdminSettings implements ISettings {
|
||||
|
||||
/** @var SettingsService */
|
||||
private $service;
|
||||
|
||||
|
||||
/**
|
||||
* AdminSettings constructor
|
||||
* @param SettingsService $service
|
||||
*/
|
||||
public function __construct(SettingsService $service) {
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function getForm() {
|
||||
$ncp_config = $this->service->getConfig("ncp",
|
||||
["nextcloud_version" => "unknown", "php_version" => "unknown", "release" => "unknown"]);
|
||||
$community_config = $this->service->getConfig("ncp-community",
|
||||
[
|
||||
"CANARY" => 'no',
|
||||
"USAGE_SURVEYS" => 'no',
|
||||
"ADMIN_NOTIFICATIONS" => 'no',
|
||||
"NOtIFICATION_ACCOUNTS" => ""
|
||||
]);
|
||||
$ncp_version = trim($this->service->getFileContent("ncp-version", "unknown"));
|
||||
|
||||
return new TemplateResponse('nextcloudpi', 'admin', [
|
||||
'community' => $community_config,
|
||||
'ncp' => $ncp_config,
|
||||
'ncp_version' => $ncp_version
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSection() {
|
||||
return "server";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPriority() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
?>
|
||||
BIN
ncp-app/ncp.tar.gz
Normal file
BIN
ncp-app/ncp.tar.gz
Normal file
Binary file not shown.
16325
ncp-app/package-lock.json
generated
Normal file
16325
ncp-app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
ncp-app/package.json
Normal file
43
ncp-app/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "nextcloudpi",
|
||||
"description": "NextcloudPi App",
|
||||
"version": "0.0.1",
|
||||
"author": "Tobias Knöppler <tobias@knoeppler.net>",
|
||||
"contributors": [],
|
||||
"bugs": {
|
||||
"url": "https://github.com/nextcloud/nextcloudpi"
|
||||
},
|
||||
"license": "agpl",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack --node-env production --progress",
|
||||
"dev": "webpack --node-env development --progress",
|
||||
"watch": "webpack --node-env development --progress --watch",
|
||||
"serve": "webpack --node-env development serve --progress",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"lint:fix": "eslint --ext .js,.vue src --fix",
|
||||
"stylelint": "stylelint css/*.css css/*.scss ../../src/**/*.scss src/**/*.vue",
|
||||
"stylelint:fix": "stylelint css/*.css css/*.scss ../../src/**/*.scss src/**/*.vue --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextcloud/axios": "^1.10.0",
|
||||
"@nextcloud/dialogs": "^3.1.4",
|
||||
"@nextcloud/router": "^2.0.0",
|
||||
"@nextcloud/vue": "^5.4.0",
|
||||
"vue": "^2.7.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @nextcloud/browserslist-config"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^16.0.0",
|
||||
"npm": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nextcloud/babel-config": "^1.0.0",
|
||||
"@nextcloud/browserslist-config": "^2.2.0",
|
||||
"@nextcloud/eslint-config": "^8.0.0",
|
||||
"@nextcloud/stylelint-config": "^2.1.2",
|
||||
"@nextcloud/webpack-vue-config": "^5.2.1"
|
||||
}
|
||||
}
|
||||
38
ncp-app/psalm.xml
Normal file
38
ncp-app/psalm.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="4"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
errorBaseline="tests/psalm-baseline.xml"
|
||||
>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
<projectFiles>
|
||||
<directory name="lib" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
<extraFiles>
|
||||
<directory name="vendor" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor/phpunit/php-code-coverage" />
|
||||
<directory name="vendor/psalm" />
|
||||
</ignoreFiles>
|
||||
</extraFiles>
|
||||
<issueHandlers>
|
||||
<UndefinedDocblockClass>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="OC\AppFramework\OCS\BaseResponse"/>
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Schema" />
|
||||
<referencedClass name="Doctrine\DBAL\Schema\SchemaException" />
|
||||
<referencedClass name="Doctrine\DBAL\Driver\Statement" />
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Table" />
|
||||
</errorLevel>
|
||||
</UndefinedDocblockClass>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
66
ncp-app/src/main-admin.js
Normal file
66
ncp-app/src/main-admin.js
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import {generateFilePath, generateUrl} from '@nextcloud/router'
|
||||
import axios from '@nextcloud/axios'
|
||||
//
|
||||
// import Vue from 'vue'
|
||||
// import App from './App.vue'
|
||||
|
||||
// eslint-disable-next-line
|
||||
__webpack_public_path__ = generateFilePath(appName, '', 'js/')
|
||||
//
|
||||
// Vue.mixin({ methods: { t, n } })
|
||||
//
|
||||
// export default new Vue({
|
||||
// el: '#nextcloudpi',
|
||||
// render: h => h(App),
|
||||
// })
|
||||
|
||||
|
||||
async function saveSettings() {
|
||||
let settings = collectSettings();
|
||||
console.log("Saving nextcloudpi settings: ", settings);
|
||||
try {
|
||||
let response = await axios.post(generateUrl('/apps/nextcloudpi/api/settings'), {settings: settings})
|
||||
console.log("Saving ncp settings succeeded")
|
||||
return {success: true, error: null}
|
||||
} catch (e) {
|
||||
// console.log("axios failure: ", arguments)
|
||||
console.error(e)
|
||||
let errMsg = e.response.data.error;
|
||||
throw Error(`${errMsg ? errMsg : e.message} (HTTP ${e.response.status})`)
|
||||
}
|
||||
}
|
||||
|
||||
function collectSettings() {
|
||||
let settings = {};
|
||||
document.querySelectorAll("#nextcloudpi input").forEach(element => {
|
||||
if (element.type === "checkbox") {
|
||||
settings[element.name] = element.checked;
|
||||
} else {
|
||||
settings[element.name] = element.value;
|
||||
}
|
||||
});
|
||||
return settings;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
console.log("Listening to ncp settings changes");
|
||||
let errorBox = document.querySelector("#nextcloudpi .error-message");
|
||||
document.querySelectorAll("#nextcloudpi input").forEach(element => {
|
||||
element.addEventListener("change", async () => {
|
||||
saveSettings()
|
||||
.then(() => {
|
||||
errorBox.classList.add("hidden");
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
errorBox.innerText = "Failed to save NextcloudPi settings: " + e.message;
|
||||
errorBox.classList.remove("hidden");
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
5
ncp-app/stylelint.config.js
Normal file
5
ncp-app/stylelint.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Nextcloud contributors
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
const stylelintConfig = require('@nextcloud/stylelint-config')
|
||||
|
||||
module.exports = stylelintConfig
|
||||
42
ncp-app/templates/admin.php
Normal file
42
ncp-app/templates/admin.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
script('nextcloudpi', 'nextcloudpi-admin');
|
||||
style('nextcloudpi', 'admin');
|
||||
?>
|
||||
<div id="nextcloudpi" class="section">
|
||||
<h2>NextcloudPi</h2>
|
||||
|
||||
<h3>System Info</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<div>NCP Version:</div><div><?php echo $_["ncp_version"] ?></div>
|
||||
</li>
|
||||
<li>
|
||||
<div>PHP Version:</div><div><?php echo $_["ncp"]["php_version"] ?></div>
|
||||
</li>
|
||||
<li>
|
||||
<div>Debian Release:</div><div><?php echo $_["ncp"]["release"] ?></div>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Settings</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<input name="canary" type="checkbox" <?php echo $_['community']['CANARY'] === 'yes' ? ' checked="checked"' : ''; ?>"/>
|
||||
<label for="canary">Enable updates from canary (testing) channel</label>
|
||||
</li>
|
||||
<li>
|
||||
<input name="adminNotifications" type="checkbox" <?php echo $_['community']['ADMIN_NOTIFICATIONS'] === 'yes' ? ' checked="checked"' : ''; ?>"/>
|
||||
<label for="adminNotifications">Enable notifications about relevant changes in NCP</label>
|
||||
</li>
|
||||
<li>
|
||||
<input name="adminNotifications" type="checkbox" <?php echo $_['community']['USAGE_SURVEYS'] === 'yes' ? ' checked="checked"' : ''; ?>"/>
|
||||
<label for="adminNotifications">Enable notifications for surveys that help to improve NCP</label>
|
||||
</li>
|
||||
<li>
|
||||
<div>Accounts to notify:</div>
|
||||
<input type="text" name="notificationAccounts"
|
||||
placeholder="comma separated list of accounts. Default is: all admins"
|
||||
value="<?php echo $_['community']['NOTIFICATION_ACCOUNTS']; ?>"/>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="error-message hidden"></p>
|
||||
</div>
|
||||
12
ncp-app/webpack.config.js
Normal file
12
ncp-app/webpack.config.js
Normal file
@ -0,0 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Tobias Knöppler <tobias@knoeppler.net>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
const path = require('path')
|
||||
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||
|
||||
module.exports = {...webpackConfig,
|
||||
...{
|
||||
entry: {
|
||||
admin: path.join(__dirname, 'src/main-admin')
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,7 +136,7 @@ HTML;
|
||||
<div id="header-left">
|
||||
<a href="https://nextcloudpi.com" id="nextcloudpi" target="_blank" tabindex="1">
|
||||
<div class="logo-icon">
|
||||
<h1 class="hidden-visually">NextCloudPi</h1>
|
||||
<h1 class="hidden-visually">NextcloudPi</h1>
|
||||
</div>
|
||||
</a>
|
||||
<a id=versionlink target="_blank" href="https://github.com/nextcloud/nextcloudpi/blob/master/changelog.md">
|
||||
|
||||
56
ncp.sh
56
ncp.sh
@ -176,7 +176,61 @@ grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
|
||||
tar $pigz -tf "$file" data &>/dev/null
|
||||
EOF
|
||||
chmod 700 /home/www/ncp-backup-launcher.sh
|
||||
echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /sbin/halt, /sbin/reboot" >> /etc/sudoers
|
||||
|
||||
cat > /home/www/ncp-app-bridge.sh <<'EOF'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
|
||||
action="${1?}"
|
||||
[[ "$action" == "config" ]] && {
|
||||
config_type="${2?}"
|
||||
arg="${3}"
|
||||
|
||||
[[ -z "$arg" ]] || {
|
||||
key="${arg%=*}"
|
||||
val="${arg#*=}"
|
||||
}
|
||||
|
||||
if [[ "$config_type" == "ncp" ]]
|
||||
then
|
||||
config_path="/usr/local/etc/ncp.cfg"
|
||||
elif [[ "$config_type" == "ncp-community" ]]
|
||||
then
|
||||
. /usr/local/etc/library.sh
|
||||
[[ -z "${key}" ]] || {
|
||||
set_app_param ncp-community.sh "${key}" "${val}"
|
||||
get_app_params ncp-community.sh
|
||||
exit 0
|
||||
}
|
||||
get_app_params ncp-community.sh
|
||||
exit $?
|
||||
else
|
||||
echo "ERROR: Invalid config name '${config_type}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ -z "${key}" ]] || {
|
||||
cfg="$(jq ".${key} = \"${val}\"" <"$config_path")"
|
||||
echo "$cfg" > "$config_path"
|
||||
}
|
||||
cat "$config_path"
|
||||
exit 0
|
||||
}
|
||||
|
||||
[[ "$action" == "file" ]] && {
|
||||
file="${2?}"
|
||||
if [[ "$file" == "ncp-version" ]]
|
||||
then
|
||||
cat /usr/local/etc/ncp-version
|
||||
else
|
||||
echo "ERROR: Invalid file '${file}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
EOF
|
||||
chmod 700 /home/www/ncp-app-bridge.sh
|
||||
echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /home/www/ncp-app-bridge.sh, /sbin/halt, /sbin/reboot" >> /etc/sudoers
|
||||
|
||||
# NCP AUTO TRUSTED DOMAIN
|
||||
mkdir -p /usr/lib/systemd/system
|
||||
|
||||
@ -151,7 +151,8 @@ chmod 770 /var/www/ncp-web
|
||||
|
||||
# install NC app
|
||||
rm -rf /var/www/ncp-app
|
||||
cp -r ncp-app /var/www/
|
||||
mkdir -p /var/www/ncp-app
|
||||
cp -r ncp-app/{appinfo,css,img,js,lib,templates} /var/www/ncp-app/
|
||||
|
||||
# install ncp-previewgenerator
|
||||
rm -rf /var/www/ncp-previewgenerator
|
||||
|
||||
@ -5,7 +5,63 @@
|
||||
cat > /usr/local/etc/instance.cfg <<EOF
|
||||
{
|
||||
"cohorteId": ${cohorte_id},
|
||||
"canary": false
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
cat > /home/www/ncp-app-bridge.sh <<'EOF'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
|
||||
action="${1?}"
|
||||
[[ "$action" == "config" ]] && {
|
||||
config_type="${2?}"
|
||||
arg="${3}"
|
||||
|
||||
[[ -z "$arg" ]] || {
|
||||
key="${arg%=*}"
|
||||
val="${arg#*=}"
|
||||
}
|
||||
|
||||
if [[ "$config_type" == "ncp" ]]
|
||||
then
|
||||
config_path="/usr/local/etc/ncp.cfg"
|
||||
elif [[ "$config_type" == "ncp-community" ]]
|
||||
then
|
||||
. /usr/local/etc/library.sh
|
||||
[[ -z "${key}" ]] || {
|
||||
set_app_param ncp-community.sh "${key}" "${val}"
|
||||
get_app_params ncp-community.sh
|
||||
exit 0
|
||||
}
|
||||
get_app_params ncp-community.sh
|
||||
exit $?
|
||||
else
|
||||
echo "ERROR: Invalid config name '${config_type}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ -z "${key}" ]] || {
|
||||
cfg="$(jq ".${key} = \"${val}\"" <"$config_path")"
|
||||
echo "$cfg" > "$config_path"
|
||||
}
|
||||
cat "$config_path"
|
||||
exit 0
|
||||
}
|
||||
|
||||
[[ "$action" == "file" ]] && {
|
||||
file="${2?}"
|
||||
if [[ "$file" == "ncp-version" ]]
|
||||
then
|
||||
cat /usr/local/etc/ncp-version
|
||||
else
|
||||
echo "ERROR: Invalid file '${file}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
EOF
|
||||
chmod 700 /home/www/ncp-app-bridge.sh
|
||||
sed -i 's|www-data ALL = NOPASSWD: .*|www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /home/www/ncp-app-bridge.sh, /sbin/halt, /sbin/reboot|' /etc/sudoers
|
||||
|
||||
ncc upgrade
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user