python3-cyberfusion-nextcloud-support 1.1.1.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cyberfusion/NextCloudSupport/__init__.py +1 -0
- cyberfusion/NextCloudSupport/_occ.py +37 -0
- cyberfusion/NextCloudSupport/app.py +143 -0
- cyberfusion/NextCloudSupport/exceptions.py +31 -0
- cyberfusion/NextCloudSupport/instance.py +392 -0
- cyberfusion/NextCloudSupport/user.py +16 -0
- python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/METADATA +39 -0
- python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/RECORD +10 -0
- python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/WHEEL +5 -0
- python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Empty file to turn this into a package."""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Functions to run `occ` commands."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from cyberfusion.Common import find_executable
|
|
7
|
+
from cyberfusion.NextCloudSupport.exceptions import CommandFailedError
|
|
8
|
+
|
|
9
|
+
PHP_BIN = find_executable("php")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run_command(command: List[str], cwd: str) -> str:
|
|
13
|
+
"""Run command and get output."""
|
|
14
|
+
command = [
|
|
15
|
+
PHP_BIN,
|
|
16
|
+
"-d",
|
|
17
|
+
"memory_limit=512M",
|
|
18
|
+
"occ",
|
|
19
|
+
"--no-interaction",
|
|
20
|
+
] + command
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
return subprocess.run(
|
|
24
|
+
command,
|
|
25
|
+
check=True,
|
|
26
|
+
stdout=subprocess.PIPE,
|
|
27
|
+
stderr=subprocess.PIPE,
|
|
28
|
+
text=True,
|
|
29
|
+
cwd=cwd,
|
|
30
|
+
).stdout.rstrip()
|
|
31
|
+
except subprocess.CalledProcessError as e:
|
|
32
|
+
raise CommandFailedError(
|
|
33
|
+
return_code=e.returncode,
|
|
34
|
+
stdout=e.stdout,
|
|
35
|
+
stderr=e.stderr,
|
|
36
|
+
command=command,
|
|
37
|
+
)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""App."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import tarfile
|
|
6
|
+
from typing import TYPE_CHECKING, Optional, Tuple
|
|
7
|
+
|
|
8
|
+
from cyberfusion.Common import download_from_url
|
|
9
|
+
from cyberfusion.NextCloudSupport._occ import run_command
|
|
10
|
+
from cyberfusion.NextCloudSupport.exceptions import AppNotInstalledError
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
13
|
+
from cyberfusion.NextCloudSupport.instance import Instance
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class App:
|
|
17
|
+
"""Represents app."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
instance: "Instance",
|
|
22
|
+
name: str,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Set attributes."""
|
|
25
|
+
self.instance = instance
|
|
26
|
+
self.name = name
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def install(
|
|
30
|
+
instance: "Instance",
|
|
31
|
+
name: Optional[str] = None,
|
|
32
|
+
url: Optional[str] = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Install app by name or URL.
|
|
35
|
+
|
|
36
|
+
For custom versions: NextCloud does not natively support installing
|
|
37
|
+
specific versions of apps (default is latest). To work around this,
|
|
38
|
+
install the app by URL, pointing to the archive containing the
|
|
39
|
+
needed version.
|
|
40
|
+
|
|
41
|
+
Note that installing apps from a specific URL is not officially
|
|
42
|
+
supported, and the way we do it is undocumented, and therefore
|
|
43
|
+
a hack.
|
|
44
|
+
"""
|
|
45
|
+
if name and url:
|
|
46
|
+
raise ValueError("Specify either name or URL")
|
|
47
|
+
|
|
48
|
+
if name:
|
|
49
|
+
run_command(["app:install", name], instance.path)
|
|
50
|
+
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
with tarfile.open(download_from_url(url)) as f:
|
|
54
|
+
name = os.path.commonpath(f.getnames())
|
|
55
|
+
|
|
56
|
+
f.extractall(path=os.path.join(instance.path, "apps"))
|
|
57
|
+
|
|
58
|
+
run_command(
|
|
59
|
+
["app:enable", name],
|
|
60
|
+
instance.path,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def is_enabled(self) -> bool:
|
|
65
|
+
"""Get if app is enabled."""
|
|
66
|
+
return self.name in self.instance.raw_app_list["enabled"]
|
|
67
|
+
|
|
68
|
+
def enable(self) -> None:
|
|
69
|
+
"""Enable app."""
|
|
70
|
+
run_command(
|
|
71
|
+
["app:enable", self.name],
|
|
72
|
+
self.instance.path,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self.instance.refresh_raw_app_list()
|
|
76
|
+
|
|
77
|
+
def disable(self) -> None:
|
|
78
|
+
"""Disable app."""
|
|
79
|
+
run_command(
|
|
80
|
+
["app:disable", self.name],
|
|
81
|
+
self.instance.path,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
self.instance.refresh_raw_app_list()
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def version(self) -> str:
|
|
88
|
+
"""Get version."""
|
|
89
|
+
for app_name, version in (
|
|
90
|
+
self.instance.raw_app_list["enabled"]
|
|
91
|
+
| self.instance.raw_app_list["disabled"]
|
|
92
|
+
).items():
|
|
93
|
+
if app_name != self.name:
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
# Sometimes, NextCloud suffixes the version by another version number.
|
|
97
|
+
# It's unclear why or when, but we don't want it.
|
|
98
|
+
# Code: https://github.com/nextcloud/server/blob/72b6db40435ce0407d0aafa626945d4f2380460f/core/Command/App/ListApps.php#L91
|
|
99
|
+
|
|
100
|
+
return version.split(" ")[0]
|
|
101
|
+
|
|
102
|
+
raise AppNotInstalledError
|
|
103
|
+
|
|
104
|
+
def remove(self) -> None:
|
|
105
|
+
"""Remove app."""
|
|
106
|
+
run_command(
|
|
107
|
+
["app:remove", self.name],
|
|
108
|
+
self.instance.path,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
self.instance.refresh_raw_app_list()
|
|
112
|
+
|
|
113
|
+
def update(self) -> Tuple[str, str]:
|
|
114
|
+
"""Update app."""
|
|
115
|
+
old_version = self.version
|
|
116
|
+
|
|
117
|
+
run_command(
|
|
118
|
+
["app:update", self.name],
|
|
119
|
+
self.instance.path,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
self.instance.refresh_raw_app_list()
|
|
123
|
+
self.instance.refresh_raw_app_update_list()
|
|
124
|
+
|
|
125
|
+
new_version = self.version
|
|
126
|
+
|
|
127
|
+
return old_version, new_version
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def available_version(self) -> Optional[str]:
|
|
131
|
+
"""Get version that app can be updated to."""
|
|
132
|
+
for line in self.instance.raw_app_update_list:
|
|
133
|
+
match = re.fullmatch("^(.*) new version available: (.*)$", line)
|
|
134
|
+
|
|
135
|
+
if not match:
|
|
136
|
+
continue
|
|
137
|
+
|
|
138
|
+
if match.group(1) != self.name:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
return match.group(2)
|
|
142
|
+
|
|
143
|
+
return None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Exceptions."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DirectoryNotEmptyError(Exception):
|
|
8
|
+
"""Directory is not empty."""
|
|
9
|
+
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AppNotInstalledError(Exception):
|
|
14
|
+
"""App should be installed, but is not."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class CommandFailedError(Exception):
|
|
21
|
+
"""Command failed."""
|
|
22
|
+
|
|
23
|
+
command: List[str]
|
|
24
|
+
return_code: int
|
|
25
|
+
stdout: str
|
|
26
|
+
stderr: str
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def streams(self) -> str:
|
|
30
|
+
"""Combine output streams."""
|
|
31
|
+
return f"Stdout:\n\n{self.stdout}\n\nStderr:\n\n{self.stderr}"
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""Instance."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import zipfile
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
from typing import List, Optional, Tuple, Union
|
|
12
|
+
|
|
13
|
+
from cyberfusion.Common import download_from_url
|
|
14
|
+
from cyberfusion.NextCloudSupport._occ import PHP_BIN, run_command
|
|
15
|
+
from cyberfusion.NextCloudSupport.app import App
|
|
16
|
+
from cyberfusion.NextCloudSupport.exceptions import (
|
|
17
|
+
AppNotInstalledError,
|
|
18
|
+
CommandFailedError,
|
|
19
|
+
DirectoryNotEmptyError,
|
|
20
|
+
)
|
|
21
|
+
from cyberfusion.NextCloudSupport.user import User
|
|
22
|
+
|
|
23
|
+
URL_ZIP_NEXTCLOUD = "https://download.nextcloud.com/server/releases/latest.zip"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SSLMode(StrEnum):
|
|
27
|
+
"""SSL modes."""
|
|
28
|
+
|
|
29
|
+
NONE = "none"
|
|
30
|
+
SSL = "ssl"
|
|
31
|
+
TLS = "tls"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MailAccountAuthMethod(StrEnum):
|
|
35
|
+
"""Auth methods for mail accounts."""
|
|
36
|
+
|
|
37
|
+
PASSWORD = "password"
|
|
38
|
+
XOAUTH2 = "xoauth2"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DatabaseType(StrEnum):
|
|
42
|
+
"""Database types."""
|
|
43
|
+
|
|
44
|
+
SQLITE = "sqlite"
|
|
45
|
+
MYSQL = "mysql"
|
|
46
|
+
PGSQL = "pgsql"
|
|
47
|
+
OCI = "oci"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Instance:
|
|
51
|
+
"""Represents NextCloud instance."""
|
|
52
|
+
|
|
53
|
+
def __init__(self, path: str) -> None:
|
|
54
|
+
"""Set attributes."""
|
|
55
|
+
self.path = path
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def download(destination_path: str, zip_path: Optional[str] = None) -> None:
|
|
59
|
+
"""Download NextCloud to path.
|
|
60
|
+
|
|
61
|
+
If zip_path is not set, NextCloud is downloaded from their website.
|
|
62
|
+
"""
|
|
63
|
+
if os.listdir(destination_path):
|
|
64
|
+
raise DirectoryNotEmptyError
|
|
65
|
+
|
|
66
|
+
# Downlaod ZIP from NextCloud if not specified
|
|
67
|
+
|
|
68
|
+
if zip_path is None:
|
|
69
|
+
zip_path = download_from_url(
|
|
70
|
+
URL_ZIP_NEXTCLOUD,
|
|
71
|
+
root_directory=destination_path,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Extract ZIP
|
|
75
|
+
|
|
76
|
+
with zipfile.ZipFile(zip_path, "r") as z:
|
|
77
|
+
z.extractall(destination_path)
|
|
78
|
+
|
|
79
|
+
# Move files from nextcloud/ to destination directory
|
|
80
|
+
|
|
81
|
+
temp_directory = os.path.join(
|
|
82
|
+
destination_path,
|
|
83
|
+
"nextcloud",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
for file_ in os.listdir(temp_directory):
|
|
87
|
+
shutil.move(os.path.join(temp_directory, file_), destination_path)
|
|
88
|
+
|
|
89
|
+
os.rmdir(temp_directory)
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def install(
|
|
93
|
+
path: str,
|
|
94
|
+
*,
|
|
95
|
+
database_host: str,
|
|
96
|
+
database_name: str,
|
|
97
|
+
database_username: str,
|
|
98
|
+
database_password: str,
|
|
99
|
+
admin_user: str,
|
|
100
|
+
admin_password: str,
|
|
101
|
+
database_type: DatabaseType = DatabaseType.MYSQL,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Install downloaded NextCloud instance.
|
|
104
|
+
|
|
105
|
+
NextCloud must be downloaded before calling this method.
|
|
106
|
+
"""
|
|
107
|
+
run_command(
|
|
108
|
+
[
|
|
109
|
+
"maintenance:install",
|
|
110
|
+
"--database",
|
|
111
|
+
database_type,
|
|
112
|
+
"--database-host",
|
|
113
|
+
database_host,
|
|
114
|
+
"--database-name",
|
|
115
|
+
database_name,
|
|
116
|
+
"--database-user",
|
|
117
|
+
database_username,
|
|
118
|
+
"--database-pass",
|
|
119
|
+
database_password,
|
|
120
|
+
"--admin-user",
|
|
121
|
+
admin_user,
|
|
122
|
+
"--admin-pass",
|
|
123
|
+
admin_password,
|
|
124
|
+
"--data-dir",
|
|
125
|
+
os.path.join(path, "data"),
|
|
126
|
+
],
|
|
127
|
+
path,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def get_app(self, name: str) -> App:
|
|
131
|
+
"""Get installed app by name."""
|
|
132
|
+
for app in self.installed_apps:
|
|
133
|
+
if name != app.name:
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
return app
|
|
137
|
+
|
|
138
|
+
raise AppNotInstalledError
|
|
139
|
+
|
|
140
|
+
def get_system_config(
|
|
141
|
+
self, name: str
|
|
142
|
+
) -> Union[
|
|
143
|
+
str,
|
|
144
|
+
int,
|
|
145
|
+
float,
|
|
146
|
+
bool,
|
|
147
|
+
]:
|
|
148
|
+
"""Get system config value by name."""
|
|
149
|
+
output = run_command(
|
|
150
|
+
[
|
|
151
|
+
"config:system:get",
|
|
152
|
+
name,
|
|
153
|
+
],
|
|
154
|
+
self.path,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if output.isdigit():
|
|
158
|
+
return int(output)
|
|
159
|
+
|
|
160
|
+
if output == "true":
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
if output == "false":
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
return float(output)
|
|
168
|
+
except ValueError:
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
return output
|
|
172
|
+
|
|
173
|
+
def set_system_config(
|
|
174
|
+
self,
|
|
175
|
+
name: str,
|
|
176
|
+
value: Union[str, int, float, bool],
|
|
177
|
+
index: Optional[int] = None,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Set system config value.
|
|
180
|
+
|
|
181
|
+
Index must be set when manipulating arrays, as it corresponds to the
|
|
182
|
+
array item.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
# Set type
|
|
186
|
+
|
|
187
|
+
type_ = "string"
|
|
188
|
+
|
|
189
|
+
if isinstance(value, int):
|
|
190
|
+
type_ = "integer"
|
|
191
|
+
|
|
192
|
+
if isinstance(value, float):
|
|
193
|
+
type_ = "float"
|
|
194
|
+
|
|
195
|
+
if isinstance(value, bool):
|
|
196
|
+
type_ = "boolean"
|
|
197
|
+
|
|
198
|
+
# Set value
|
|
199
|
+
|
|
200
|
+
_value = str(value)
|
|
201
|
+
|
|
202
|
+
if isinstance(value, bool):
|
|
203
|
+
_value = _value.lower()
|
|
204
|
+
|
|
205
|
+
# Set command
|
|
206
|
+
|
|
207
|
+
command = [
|
|
208
|
+
"config:system:set",
|
|
209
|
+
name,
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
if index is not None:
|
|
213
|
+
command.append(str(index))
|
|
214
|
+
|
|
215
|
+
command.extend(["--value", _value, "--type", type_])
|
|
216
|
+
|
|
217
|
+
# Run command
|
|
218
|
+
|
|
219
|
+
run_command(command, self.path)
|
|
220
|
+
|
|
221
|
+
def update(self) -> Tuple[str, str]:
|
|
222
|
+
"""Update NextCloud."""
|
|
223
|
+
old_version = self.version
|
|
224
|
+
|
|
225
|
+
command = [PHP_BIN, "updater/updater.phar", "--no-interaction"]
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
subprocess.run(
|
|
229
|
+
command,
|
|
230
|
+
check=True,
|
|
231
|
+
stdout=subprocess.PIPE,
|
|
232
|
+
stderr=subprocess.PIPE,
|
|
233
|
+
cwd=self.path,
|
|
234
|
+
)
|
|
235
|
+
except subprocess.CalledProcessError as e:
|
|
236
|
+
raise CommandFailedError(
|
|
237
|
+
return_code=e.returncode,
|
|
238
|
+
stdout=e.stdout,
|
|
239
|
+
stderr=e.stderr,
|
|
240
|
+
command=command,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
new_version = self.version
|
|
244
|
+
|
|
245
|
+
return old_version, new_version
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def available_version(self) -> Optional[str]:
|
|
249
|
+
"""Get version that instance can be updated to."""
|
|
250
|
+
lines = run_command(
|
|
251
|
+
[
|
|
252
|
+
"update:check",
|
|
253
|
+
],
|
|
254
|
+
self.path,
|
|
255
|
+
).splitlines()
|
|
256
|
+
|
|
257
|
+
for line in lines:
|
|
258
|
+
match = re.fullmatch(
|
|
259
|
+
"^Nextcloud (.*) is available. Get more information on how to update at (.*).$",
|
|
260
|
+
line,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if not match:
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
return match.group(1)
|
|
267
|
+
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def version(self) -> str:
|
|
272
|
+
"""Get version."""
|
|
273
|
+
return self.get_system_config("version") # type: ignore[return-value]
|
|
274
|
+
|
|
275
|
+
def create_mail_account(
|
|
276
|
+
self,
|
|
277
|
+
*,
|
|
278
|
+
user_id: str,
|
|
279
|
+
name: str,
|
|
280
|
+
email_address: str,
|
|
281
|
+
imap_hostname: str,
|
|
282
|
+
imap_port: int,
|
|
283
|
+
imap_ssl_mode: SSLMode,
|
|
284
|
+
imap_username: str,
|
|
285
|
+
imap_password: str,
|
|
286
|
+
smtp_host: str,
|
|
287
|
+
smtp_port: int,
|
|
288
|
+
smtp_ssl_mode: SSLMode,
|
|
289
|
+
smtp_username: str,
|
|
290
|
+
smtp_password: str,
|
|
291
|
+
auth_method: MailAccountAuthMethod,
|
|
292
|
+
) -> None:
|
|
293
|
+
"""Create mail account."""
|
|
294
|
+
run_command(
|
|
295
|
+
[
|
|
296
|
+
"mail:account:create",
|
|
297
|
+
user_id,
|
|
298
|
+
name,
|
|
299
|
+
email_address,
|
|
300
|
+
imap_hostname,
|
|
301
|
+
str(imap_port),
|
|
302
|
+
imap_ssl_mode,
|
|
303
|
+
imap_username,
|
|
304
|
+
imap_password,
|
|
305
|
+
smtp_host,
|
|
306
|
+
str(smtp_port),
|
|
307
|
+
smtp_ssl_mode,
|
|
308
|
+
smtp_username,
|
|
309
|
+
smtp_password,
|
|
310
|
+
auth_method,
|
|
311
|
+
],
|
|
312
|
+
self.path,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def users(self) -> List[User]:
|
|
317
|
+
"""Get users."""
|
|
318
|
+
result = []
|
|
319
|
+
|
|
320
|
+
output = json.loads(
|
|
321
|
+
run_command(
|
|
322
|
+
[
|
|
323
|
+
"user:list",
|
|
324
|
+
"--output",
|
|
325
|
+
"json",
|
|
326
|
+
],
|
|
327
|
+
self.path,
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
for id_, name in output.items():
|
|
332
|
+
user = User(self, id_, name)
|
|
333
|
+
|
|
334
|
+
result.append(user)
|
|
335
|
+
|
|
336
|
+
return result
|
|
337
|
+
|
|
338
|
+
def refresh_raw_app_list(self) -> None:
|
|
339
|
+
"""Clear the raw app list cache."""
|
|
340
|
+
try:
|
|
341
|
+
del self.raw_app_list
|
|
342
|
+
except AttributeError:
|
|
343
|
+
pass
|
|
344
|
+
|
|
345
|
+
def refresh_raw_app_update_list(self) -> None:
|
|
346
|
+
"""Clear the raw app list cache."""
|
|
347
|
+
try:
|
|
348
|
+
del self.raw_app_update_list
|
|
349
|
+
except AttributeError:
|
|
350
|
+
pass
|
|
351
|
+
|
|
352
|
+
@cached_property
|
|
353
|
+
def raw_app_update_list(self) -> List[str]:
|
|
354
|
+
"""Get raw app list output."""
|
|
355
|
+
return run_command(
|
|
356
|
+
[
|
|
357
|
+
"app:update",
|
|
358
|
+
"--showonly",
|
|
359
|
+
],
|
|
360
|
+
self.path,
|
|
361
|
+
).splitlines()
|
|
362
|
+
|
|
363
|
+
@cached_property
|
|
364
|
+
def raw_app_list(self) -> dict:
|
|
365
|
+
"""Get raw app list output."""
|
|
366
|
+
return json.loads(
|
|
367
|
+
run_command(
|
|
368
|
+
[
|
|
369
|
+
"app:list",
|
|
370
|
+
"--output",
|
|
371
|
+
"json",
|
|
372
|
+
],
|
|
373
|
+
self.path,
|
|
374
|
+
)
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def installed_apps(self) -> List[App]:
|
|
379
|
+
"""Get installed apps."""
|
|
380
|
+
result = []
|
|
381
|
+
|
|
382
|
+
for name, _ in (
|
|
383
|
+
self.raw_app_list["enabled"] | self.raw_app_list["disabled"]
|
|
384
|
+
).items():
|
|
385
|
+
app = App(
|
|
386
|
+
self,
|
|
387
|
+
name,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
result.append(app)
|
|
391
|
+
|
|
392
|
+
return result
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""User."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
6
|
+
from cyberfusion.NextCloudSupport.instance import Instance
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class User:
|
|
10
|
+
"""Represents user."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, instance: "Instance", id_: str, name: str) -> None:
|
|
13
|
+
"""Set attributes."""
|
|
14
|
+
self.instance = instance
|
|
15
|
+
self.id = id_
|
|
16
|
+
self.name = name
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: python3-cyberfusion-nextcloud-support
|
|
3
|
+
Version: 1.1.1.2.2
|
|
4
|
+
Summary: Library for NextCloud.
|
|
5
|
+
Author-email: Cyberfusion <support@cyberfusion.io>
|
|
6
|
+
Project-URL: Source, https://github.com/CyberfusionIO/python3-cyberfusion-nextcloud-support
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: python3-cyberfusion-common ~=2.10
|
|
9
|
+
|
|
10
|
+
# python3-cyberfusion-nextcloud-support
|
|
11
|
+
|
|
12
|
+
Library for NextCloud.
|
|
13
|
+
|
|
14
|
+
# Install
|
|
15
|
+
|
|
16
|
+
## PyPI
|
|
17
|
+
|
|
18
|
+
Run the following command to install the package from PyPI:
|
|
19
|
+
|
|
20
|
+
pip3 install python3-cyberfusion-nextcloud-support
|
|
21
|
+
|
|
22
|
+
Next, install the following software:
|
|
23
|
+
|
|
24
|
+
* PHP (see https://docs.nextcloud.com/server/latest/admin_manual/installation/php_configuration.html)
|
|
25
|
+
|
|
26
|
+
## Debian
|
|
27
|
+
|
|
28
|
+
Run the following commands to build a Debian package:
|
|
29
|
+
|
|
30
|
+
mk-build-deps -i -t 'apt -o Debug::pkgProblemResolver=yes --no-install-recommends -y'
|
|
31
|
+
dpkg-buildpackage -us -uc
|
|
32
|
+
|
|
33
|
+
# Configure
|
|
34
|
+
|
|
35
|
+
No configuration is supported.
|
|
36
|
+
|
|
37
|
+
# Usage
|
|
38
|
+
|
|
39
|
+
See code.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
cyberfusion/NextCloudSupport/__init__.py,sha256=8JByOElHmHLFiNx9xTHIU6dy5AjPQQmpPLFq5IR4bpM,46
|
|
2
|
+
cyberfusion/NextCloudSupport/_occ.py,sha256=46gDht7KKwNlDJ46YUrDdm4PQxYV0JtlzLyOTYx_ZGE,908
|
|
3
|
+
cyberfusion/NextCloudSupport/app.py,sha256=IIH7N9F8i-vFnajXxw3BJO_5i9U5VxQ7rOqtcWqo-Q0,3901
|
|
4
|
+
cyberfusion/NextCloudSupport/exceptions.py,sha256=--rrdtxwwCUmuukRV7sNIIughMxRHv9dD3s_p2lTtNY,569
|
|
5
|
+
cyberfusion/NextCloudSupport/instance.py,sha256=oCuoyvKEJ7pbOV5oQE1ad5aK1kOSKCptwaydVW67L-s,9351
|
|
6
|
+
cyberfusion/NextCloudSupport/user.py,sha256=CSSBzONswPu6IfCtl3vQ3dsLxpeF97bLvBcyeqTuCPw,375
|
|
7
|
+
python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/METADATA,sha256=cX4nwx6ifGTtemnx3FLlK4R5BkuL3jOzgezB_SzJhT0,945
|
|
8
|
+
python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
|
9
|
+
python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/top_level.txt,sha256=ss011q9S6SL_KIIyq7iujFmIYa0grSjlnInO7cDkeag,12
|
|
10
|
+
python3_cyberfusion_nextcloud_support-1.1.1.2.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cyberfusion
|