Skip to content

ProfiWiki API Documentation

docker

Created on 2023-04-01

@author: wf

ProfiWikiContainer

a profiwiki docker container wrapper

Source code in profiwiki/docker.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
class ProfiWikiContainer:
    """
    a profiwiki docker container wrapper
    """

    def __init__(self, dc: DockerContainer):
        """
        Args:
            dc(DockerContainer): the to wrap
        """
        self.dc = dc

    def log_action(self, action: str):
        """
        log the given action

        Args:
            action(str): the d
        """
        if self.dc:
            print(f"{action} {self.dc.kind} {self.dc.name}", flush=True)
        else:
            print(f"{action}", flush=True)

    def upload(self, text: str, path: str):
        """
        upload the given text to the given path
        """
        with tempfile.NamedTemporaryFile() as tmp:
            self.log_action(f"uploading {tmp.name} as {path} to ")
            with open(tmp.name, "w") as text_file:
                text_file.write(text)
            self.dc.container.copy_to(tmp.name, path)

    def killremove(self, volumes: bool = False):
        """
        kill and remove me

        Args:
            volumes(bool): if True remove anonymous volumes associated with the container, default=True (to avoid e.g. passwords to get remembered / stuck
        """
        if self.dc:
            self.log_action("killing and removing")
            self.dc.container.kill()
            self.dc.container.remove(volumes=volumes)

    def start_cron(self):
        """
        Starting periodic command scheduler: cron.
        """
        self.dc.container.execute(["/usr/sbin/service", "cron", "start"], tty=True)

    def install_plantuml(self):
        """
        install plantuml to this container
        """
        script = """#!/bin/bash
# install plantuml
# WF 2023-05-01
apt-get update
apt-get install -y plantuml
"""
        # https://gabrieldemarmiesse.github.io/python-on-whales/docker_objects/containers/
        script_path = "/root/install_plantuml.sh"
        self.install_and_run_script(script, script_path)
        pass

    def install_and_run_script(self, script: str, script_path: str):
        """
        install and run the given script

        Args:
            script(str): the source code of the script
            script_path(str): the path to copy the script to and then execute
        """
        self.upload(script, script_path)
        # make executable
        self.dc.container.execute(["chmod", "+x", script_path])
        self.dc.container.execute([script_path], tty=True)

    def install_fontawesome(self):
        """
        install fontawesome to this container
        """
        script = """#!/bin/bash
# install fontawesome
# WF 2023-01-25
version=6.4.0
#version=5.15.4
#version=4.7.0
zip_url=https://github.com/FortAwesome/Font-Awesome/releases/download/$version/fontawesome-free-$version-desktop.zip
#https://github.com/FortAwesome/Font-Awesome/releases/download/5.15.4/fontawesome-free-5.15.4-desktop.zip

cd /var/www
#curl https://use.fontawesome.com/releases/$version/fontawesome-free-$version-web.zip -o fontawesome.zip
curl --location $zip_url -o fontawesome.zip
unzip -o fontawesome.zip
ln -s -f fontawesome-free-$version-desktop fontawesome
chown -R www-data.www-data fontawesome
cd fontawesome
ln -s svgs/solid svg
cat << EOS > /etc/apache2/conf-available/font-awesome.conf
Alias /font-awesome /var/www/fontawesome
<Directory /var/www/fontawesome>
  Options Indexes FollowSymLinks MultiViews
  Require all granted
</Directory>
EOS
a2enconf font-awesome
"""
        script_path = "/root/install_fontawesome"
        self.install_and_run_script(script, script_path)
        try:
            self.dc.container.execute(["service", "apache2", "restart"])
        except DockerException as e:
            # we expect a SIGTERM
            if not e.return_code == 143:
                raise e
        pass

__init__(dc)

Parameters:

Name Type Description Default
dc(DockerContainer)

the to wrap

required
Source code in profiwiki/docker.py
18
19
20
21
22
23
def __init__(self, dc: DockerContainer):
    """
    Args:
        dc(DockerContainer): the to wrap
    """
    self.dc = dc

install_and_run_script(script, script_path)

install and run the given script

Parameters:

Name Type Description Default
script(str)

the source code of the script

required
script_path(str)

the path to copy the script to and then execute

required
Source code in profiwiki/docker.py
80
81
82
83
84
85
86
87
88
89
90
91
def install_and_run_script(self, script: str, script_path: str):
    """
    install and run the given script

    Args:
        script(str): the source code of the script
        script_path(str): the path to copy the script to and then execute
    """
    self.upload(script, script_path)
    # make executable
    self.dc.container.execute(["chmod", "+x", script_path])
    self.dc.container.execute([script_path], tty=True)

install_fontawesome()

install fontawesome to this container

Source code in profiwiki/docker.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    def install_fontawesome(self):
        """
        install fontawesome to this container
        """
        script = """#!/bin/bash
# install fontawesome
# WF 2023-01-25
version=6.4.0
#version=5.15.4
#version=4.7.0
zip_url=https://github.com/FortAwesome/Font-Awesome/releases/download/$version/fontawesome-free-$version-desktop.zip
#https://github.com/FortAwesome/Font-Awesome/releases/download/5.15.4/fontawesome-free-5.15.4-desktop.zip

cd /var/www
#curl https://use.fontawesome.com/releases/$version/fontawesome-free-$version-web.zip -o fontawesome.zip
curl --location $zip_url -o fontawesome.zip
unzip -o fontawesome.zip
ln -s -f fontawesome-free-$version-desktop fontawesome
chown -R www-data.www-data fontawesome
cd fontawesome
ln -s svgs/solid svg
cat << EOS > /etc/apache2/conf-available/font-awesome.conf
Alias /font-awesome /var/www/fontawesome
<Directory /var/www/fontawesome>
  Options Indexes FollowSymLinks MultiViews
  Require all granted
</Directory>
EOS
a2enconf font-awesome
"""
        script_path = "/root/install_fontawesome"
        self.install_and_run_script(script, script_path)
        try:
            self.dc.container.execute(["service", "apache2", "restart"])
        except DockerException as e:
            # we expect a SIGTERM
            if not e.return_code == 143:
                raise e
        pass

install_plantuml()

install plantuml to this container

Source code in profiwiki/docker.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    def install_plantuml(self):
        """
        install plantuml to this container
        """
        script = """#!/bin/bash
# install plantuml
# WF 2023-05-01
apt-get update
apt-get install -y plantuml
"""
        # https://gabrieldemarmiesse.github.io/python-on-whales/docker_objects/containers/
        script_path = "/root/install_plantuml.sh"
        self.install_and_run_script(script, script_path)
        pass

killremove(volumes=False)

kill and remove me

Parameters:

Name Type Description Default
volumes(bool)

if True remove anonymous volumes associated with the container, default=True (to avoid e.g. passwords to get remembered / stuck

required
Source code in profiwiki/docker.py
47
48
49
50
51
52
53
54
55
56
57
def killremove(self, volumes: bool = False):
    """
    kill and remove me

    Args:
        volumes(bool): if True remove anonymous volumes associated with the container, default=True (to avoid e.g. passwords to get remembered / stuck
    """
    if self.dc:
        self.log_action("killing and removing")
        self.dc.container.kill()
        self.dc.container.remove(volumes=volumes)

log_action(action)

log the given action

Parameters:

Name Type Description Default
action(str)

the d

required
Source code in profiwiki/docker.py
25
26
27
28
29
30
31
32
33
34
35
def log_action(self, action: str):
    """
    log the given action

    Args:
        action(str): the d
    """
    if self.dc:
        print(f"{action} {self.dc.kind} {self.dc.name}", flush=True)
    else:
        print(f"{action}", flush=True)

start_cron()

Starting periodic command scheduler: cron.

Source code in profiwiki/docker.py
59
60
61
62
63
def start_cron(self):
    """
    Starting periodic command scheduler: cron.
    """
    self.dc.container.execute(["/usr/sbin/service", "cron", "start"], tty=True)

upload(text, path)

upload the given text to the given path

Source code in profiwiki/docker.py
37
38
39
40
41
42
43
44
45
def upload(self, text: str, path: str):
    """
    upload the given text to the given path
    """
    with tempfile.NamedTemporaryFile() as tmp:
        self.log_action(f"uploading {tmp.name} as {path} to ")
        with open(tmp.name, "w") as text_file:
            text_file.write(text)
        self.dc.container.copy_to(tmp.name, path)

patch

Created on 2023-04-09

@author: wf

Patch

A class for patch a text file

Source code in profiwiki/patch.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class Patch:
    """
    A class for patch a text file
    """

    def __init__(self, file_path: str):
        """
        Initializes a Patch instance with the file path file to be patched.

        Args:
            file_path (str): The file path of the PHP file to be patched.
        """
        self.lines = []
        self.file_path = file_path
        # https://stackoverflow.com/a/3277516/1497139
        with open(self.file_path, "r", encoding="UTF-8") as file:
            while line := file.readline():
                self.lines.append(line.rstrip())

    def save(self):
        """
        save my lines
        """
        with open(self.file_path, "w") as f:
            for line in self.lines:
                f.write(f"{line}\n")

    def patch_mediawiki_config_var(self, var_name: str, var_value: str) -> None:
        """
        Patches a MediaWiki configuration variable in the PHP file with the given name and value.

        Args:
            var_name (str): The name of the configuration variable to be patched.
            var_value (str): The new value to be set for the configuration variable.

        Returns:
            None
        """
        # Define the regex pattern to match the configuration variable
        pattern = r"\$wg" + re.escape(var_name) + r"\s*=\s*[^\n]+"

        # Define the replacement string with the updated value
        replacement = f"$wg{var_name} = {var_value};"

        # Use fileinput to replace the matched line in the file
        for i, line in enumerate(self.lines):
            new_line = re.sub(pattern, replacement, line)
            self.lines[i] = new_line

    def add_text(self, text: str, avoid_duplication: bool = True):
        """
        Adds text avoiding duplication if specified

        Args:
            text (str): the text to add
            avoid_duplication(bool): if True avoid duplication of existing lines
        """
        new_lines = text.split("\n")
        for new_line in new_lines:
            do_add = True
            if avoid_duplication:
                do_add = not new_line in self.lines
            if do_add:
                self.lines.append(new_line)

__init__(file_path)

Initializes a Patch instance with the file path file to be patched.

Parameters:

Name Type Description Default
file_path str

The file path of the PHP file to be patched.

required
Source code in profiwiki/patch.py
15
16
17
18
19
20
21
22
23
24
25
26
27
def __init__(self, file_path: str):
    """
    Initializes a Patch instance with the file path file to be patched.

    Args:
        file_path (str): The file path of the PHP file to be patched.
    """
    self.lines = []
    self.file_path = file_path
    # https://stackoverflow.com/a/3277516/1497139
    with open(self.file_path, "r", encoding="UTF-8") as file:
        while line := file.readline():
            self.lines.append(line.rstrip())

add_text(text, avoid_duplication=True)

Adds text avoiding duplication if specified

Parameters:

Name Type Description Default
text str

the text to add

required
avoid_duplication(bool)

if True avoid duplication of existing lines

required
Source code in profiwiki/patch.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def add_text(self, text: str, avoid_duplication: bool = True):
    """
    Adds text avoiding duplication if specified

    Args:
        text (str): the text to add
        avoid_duplication(bool): if True avoid duplication of existing lines
    """
    new_lines = text.split("\n")
    for new_line in new_lines:
        do_add = True
        if avoid_duplication:
            do_add = not new_line in self.lines
        if do_add:
            self.lines.append(new_line)

patch_mediawiki_config_var(var_name, var_value)

Patches a MediaWiki configuration variable in the PHP file with the given name and value.

Parameters:

Name Type Description Default
var_name str

The name of the configuration variable to be patched.

required
var_value str

The new value to be set for the configuration variable.

required

Returns:

Type Description
None

None

Source code in profiwiki/patch.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def patch_mediawiki_config_var(self, var_name: str, var_value: str) -> None:
    """
    Patches a MediaWiki configuration variable in the PHP file with the given name and value.

    Args:
        var_name (str): The name of the configuration variable to be patched.
        var_value (str): The new value to be set for the configuration variable.

    Returns:
        None
    """
    # Define the regex pattern to match the configuration variable
    pattern = r"\$wg" + re.escape(var_name) + r"\s*=\s*[^\n]+"

    # Define the replacement string with the updated value
    replacement = f"$wg{var_name} = {var_value};"

    # Use fileinput to replace the matched line in the file
    for i, line in enumerate(self.lines):
        new_line = re.sub(pattern, replacement, line)
        self.lines[i] = new_line

save()

save my lines

Source code in profiwiki/patch.py
29
30
31
32
33
34
35
def save(self):
    """
    save my lines
    """
    with open(self.file_path, "w") as f:
        for line in self.lines:
            f.write(f"{line}\n")

profiwiki_cmd

Created on 2023-04-01

@author: wf

ProfiWikiCmd

ProfiWiki command line

Source code in profiwiki/profiwiki_cmd.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
class ProfiWikiCmd:
    """
    ProfiWiki command line
    """

    def get_arg_parser(
        self, config: MwClusterConfig, description: str, version_msg: str
    ) -> ArgumentParser:
        """
        Setup command line argument parser

        Args:
            config(MwClusterConfig): the mediawiki cluster configuration
            description(str): the description
            version_msg(str): the version message

        Returns:
            ArgumentParser: the argument parser
        """
        # script_path=Path(__file__)
        parser = ArgumentParser(
            description=description, formatter_class=RawDescriptionHelpFormatter
        )
        config.addArgs(parser)
        parser.add_argument(
            "--about",
            help="show about info [default: %(default)s]",
            action="store_true",
        )
        parser.add_argument(
            "--apache", help="generate apache configuration", action="store_true"
        )
        parser.add_argument(
            "--all", help="do all necessary steps for a full setup", action="store_true"
        )
        parser.add_argument("--bash", help="bash into container", action="store_true")
        parser.add_argument("--create", action="store_true", help="create the wiki")
        parser.add_argument("--check", action="store_true", help="check the wiki")
        parser.add_argument(
            "--update",
            action="store_true",
            help="start the update script -e.g. to fix SMW key",
        )
        parser.add_argument("--cron", action="store_true", help="start cron service")
        parser.add_argument(
            "--down",
            action="store_true",
            help="shutdown the wiki [default: %(default)s]",
        )
        parser.add_argument(
            "--patch",
            action="store_true",
            help="apply LocalSettings.php patches [default: %(default)s]",
        )
        parser.add_argument(
            "--list",
            action="store_true",
            help="list the available profi wikis [default: %(default)s]",
        )
        parser.add_argument(
            "-fa", "--fontawesome", action="store_true", help="install fontawesome"
        )
        parser.add_argument(
            "-wuc", "--wikiuser_check", action="store_true", help="check wikiuser"
        )
        parser.add_argument(
            "-pu", "--plantuml", action="store_true", help="install plantuml"
        )
        parser.add_argument(
            "-i", "--info", help="show system info", action="store_true"
        )
        parser.add_argument("-V", "--version", action="version", version=version_msg)
        # debug args
        parser.add_argument("--debugServer", help="remote debug Server")
        parser.add_argument(
            "--debugPort", type=int, help="remote debug Port", default=5678
        )
        parser.add_argument(
            "--debugPathMapping",
            nargs="+",
            help="remote debug Server path mapping - needs two arguments 1st: remotePath 2nd: local Path",
        )
        return parser

    def optional_debug(self, args):
        """
        start the remote debugger if the arguments specify so

        Args:
            args: The command line arguments
        """
        if args.debugServer:
            import pydevd
            import pydevd_file_utils

            print(args.debugPathMapping, flush=True)
            if args.debugPathMapping:
                if len(args.debugPathMapping) == 2:
                    remotePath = args.debugPathMapping[
                        0
                    ]  # path on the remote debugger side
                    localPath = args.debugPathMapping[
                        1
                    ]  # path on the local machine where the code runs
                    MY_PATHS_FROM_ECLIPSE_TO_PYTHON = [
                        (remotePath, localPath),
                    ]
                    pydevd_file_utils.setup_client_server_paths(
                        MY_PATHS_FROM_ECLIPSE_TO_PYTHON
                    )  # os.environ["PATHS_FROM_ECLIPSE_TO_PYTHON"]='[["%s", "%s"]]' % (remotePath,localPath)  # print("trying to debug with PATHS_FROM_ECLIPSE_TO_PYTHON=%s" % os.environ["PATHS_FROM_ECLIPSE_TO_PYTHON"]);

            pydevd.settrace(
                args.debugServer,
                port=args.debugPort,
                stdoutToServer=True,
                stderrToServer=True,
            )
            print("command line args are: %s" % str(sys.argv))
            pass

get_arg_parser(config, description, version_msg)

Setup command line argument parser

Parameters:

Name Type Description Default
config(MwClusterConfig)

the mediawiki cluster configuration

required
description(str)

the description

required
version_msg(str)

the version message

required

Returns:

Name Type Description
ArgumentParser ArgumentParser

the argument parser

Source code in profiwiki/profiwiki_cmd.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def get_arg_parser(
    self, config: MwClusterConfig, description: str, version_msg: str
) -> ArgumentParser:
    """
    Setup command line argument parser

    Args:
        config(MwClusterConfig): the mediawiki cluster configuration
        description(str): the description
        version_msg(str): the version message

    Returns:
        ArgumentParser: the argument parser
    """
    # script_path=Path(__file__)
    parser = ArgumentParser(
        description=description, formatter_class=RawDescriptionHelpFormatter
    )
    config.addArgs(parser)
    parser.add_argument(
        "--about",
        help="show about info [default: %(default)s]",
        action="store_true",
    )
    parser.add_argument(
        "--apache", help="generate apache configuration", action="store_true"
    )
    parser.add_argument(
        "--all", help="do all necessary steps for a full setup", action="store_true"
    )
    parser.add_argument("--bash", help="bash into container", action="store_true")
    parser.add_argument("--create", action="store_true", help="create the wiki")
    parser.add_argument("--check", action="store_true", help="check the wiki")
    parser.add_argument(
        "--update",
        action="store_true",
        help="start the update script -e.g. to fix SMW key",
    )
    parser.add_argument("--cron", action="store_true", help="start cron service")
    parser.add_argument(
        "--down",
        action="store_true",
        help="shutdown the wiki [default: %(default)s]",
    )
    parser.add_argument(
        "--patch",
        action="store_true",
        help="apply LocalSettings.php patches [default: %(default)s]",
    )
    parser.add_argument(
        "--list",
        action="store_true",
        help="list the available profi wikis [default: %(default)s]",
    )
    parser.add_argument(
        "-fa", "--fontawesome", action="store_true", help="install fontawesome"
    )
    parser.add_argument(
        "-wuc", "--wikiuser_check", action="store_true", help="check wikiuser"
    )
    parser.add_argument(
        "-pu", "--plantuml", action="store_true", help="install plantuml"
    )
    parser.add_argument(
        "-i", "--info", help="show system info", action="store_true"
    )
    parser.add_argument("-V", "--version", action="version", version=version_msg)
    # debug args
    parser.add_argument("--debugServer", help="remote debug Server")
    parser.add_argument(
        "--debugPort", type=int, help="remote debug Port", default=5678
    )
    parser.add_argument(
        "--debugPathMapping",
        nargs="+",
        help="remote debug Server path mapping - needs two arguments 1st: remotePath 2nd: local Path",
    )
    return parser

optional_debug(args)

start the remote debugger if the arguments specify so

Parameters:

Name Type Description Default
args

The command line arguments

required
Source code in profiwiki/profiwiki_cmd.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def optional_debug(self, args):
    """
    start the remote debugger if the arguments specify so

    Args:
        args: The command line arguments
    """
    if args.debugServer:
        import pydevd
        import pydevd_file_utils

        print(args.debugPathMapping, flush=True)
        if args.debugPathMapping:
            if len(args.debugPathMapping) == 2:
                remotePath = args.debugPathMapping[
                    0
                ]  # path on the remote debugger side
                localPath = args.debugPathMapping[
                    1
                ]  # path on the local machine where the code runs
                MY_PATHS_FROM_ECLIPSE_TO_PYTHON = [
                    (remotePath, localPath),
                ]
                pydevd_file_utils.setup_client_server_paths(
                    MY_PATHS_FROM_ECLIPSE_TO_PYTHON
                )  # os.environ["PATHS_FROM_ECLIPSE_TO_PYTHON"]='[["%s", "%s"]]' % (remotePath,localPath)  # print("trying to debug with PATHS_FROM_ECLIPSE_TO_PYTHON=%s" % os.environ["PATHS_FROM_ECLIPSE_TO_PYTHON"]);

        pydevd.settrace(
            args.debugServer,
            port=args.debugPort,
            stdoutToServer=True,
            stderrToServer=True,
        )
        print("command line args are: %s" % str(sys.argv))
        pass

main(argv=None)

main program.

Source code in profiwiki/profiwiki_cmd.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def main(argv=None):  # IGNORE:C0111
    """main program."""

    if argv is None:
        argv = sys.argv[1:]

    program_name = "profiwiki"
    program_version = f"v{Version.version}"
    program_build_date = str(Version.date)
    program_version_message = f"{program_name} ({program_version},{program_build_date})"

    args = None
    try:
        pw = ProfiWiki()
        pw_cmd = ProfiWikiCmd()
        parser = pw_cmd.get_arg_parser(
            config=pw.config,
            description=Version.license,
            version_msg=program_version_message,
        )
        args = parser.parse_args(argv)
        if len(argv) < 1:
            parser.print_usage()
            sys.exit(1)
        if args.about:
            print(program_version_message)
            print(f"see {Version.doc_url}")
            webbrowser.open(Version.doc_url)
        pw_cmd.optional_debug(args)
        if args.info:
            info = pw.system_info()
            print(info)
        pw.work(args)

    except KeyboardInterrupt:
        ###
        # handle keyboard interrupt
        # ###
        return 1
    except Exception as e:
        if DEBUG:
            raise e
        indent = len(program_name) * " "
        sys.stderr.write(program_name + ": " + repr(e) + "\n")
        sys.stderr.write(indent + "  for help use --help")
        if args is None:
            print("args could not be parsed")
        elif args.debug:
            print(traceback.format_exc())
        return 2

profiwiki_core

Created on 2023-04-01

@author: wf

ProfiWiki

ProfiWiki

Source code in profiwiki/profiwiki_core.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
class ProfiWiki:
    """
    ProfiWiki
    """

    def __init__(
        self,
        prefix: str = "pw",
        smw_version="4.1.3",
        mw_version="1.39.8",
        port: int = 9079,
    ):
        """
        constructor
        """
        self.os_name = platform.system()
        self.os_uname = os.uname()
        self.os_release = platform.release()
        self.args = None
        self.config = MwClusterConfig()
        self.config.smw_version = smw_version
        self.config.random_password = False
        self.config.prefix = prefix
        self.config.base_port = port
        self.config.sql_port = port - 1
        self.config.port = port
        self.config.versions = [mw_version]
        self.config.version = mw_version
        self.config.container_base_name = "pw"
        self.config.extensionNameList = [
            "Admin Links",
            "Diagrams",
            "Graph",
            "Header Tabs",
            "ImageMap",
            "ImageLink",
            "MagicNoCache",
            "Maps9",
            "Mermaid",
            "MsUpload",
            "Nuke",
            "Page Forms",
            "ParserFunctions",
            "PDFEmbed",
            "Renameuser",
            "Replace Text",
            "Semantic Result Formats",
            "SyntaxHighlight",
            "Variables",
            "UserFunctions",
            "YouTube",
        ]
        self.config.logo = "https://wiki.bitplan.com/images/wiki/thumb/6/63/Profiwikiicon.png/96px-Profiwikiicon.png"
        self.config.__post_init__()
        self.mwCluster = None
        pass

    def system_info(self) -> str:
        """
        collect system information
        """
        info = f"""os: {self.os_name}"""
        if "Darwin" in info:
            release, _version, _machine = platform.mac_ver()
            info += f" MacOS {release}"
        else:
            info += f"{self.os_release}"
        return info

    def work(self, args):
        """
        work as instructed by the arguments

        Args:
            args(Namespace): the command line arguments
        """
        self.config.fromArgs(args)
        # make sure the wikiId is set from the container base name
        config_path = self.config.get_config_path()
        if os.path.isfile(config_path) and not self.config.forceRebuild:
            # reload the previous configuration e.g. based on container_name only
            previous_config = self.config.load(config_path)
            if self.config.verbose:
                print(f"ProfiWiki with previous configuration from {config_path}...")
            self.config = previous_config
        self.config.wikiId = self.config.container_base_name
        if args.bash:
            cmd = f"docker exec -it {self.config.container_base_name}-mw /bin/bash"
            print(cmd)
            return
        mwApp = self.getMwApp(withGenerate=args.forceRebuild)
        if self.config.verbose:
            print(
                f"ProfiWiki {mwApp.config.container_base_name} using port {mwApp.config.port} sqlport {mwApp.config.sql_port}"
            )
        if args.force_user:
            mwApp.createWikiUser(store=True)
        if args.all:
            self.create(mwApp, args.forceRebuild)
            pmw, _pdb = self.getProfiWikiContainers(mwApp)
            pmw.install_fontawesome()
            pmw.install_plantuml()
            self.patch(pmw)
            self.update(mwApp)
            pmw.start_cron()
        if args.wikiuser_check:
            self.check_wikiuser(mwApp)
        if args.apache:
            apache_config = self.apache_config(mwApp)
            print(apache_config)
        if args.create:
            self.create(mwApp, args.forceRebuild)
        if args.check:
            self.check(mwApp)
        if args.down:
            self.down(mwApp, args.forceRebuild)
        if args.list:
            self.list(mwApp)
        if args.plantuml or args.fontawesome or args.cron or args.patch:
            pmw, _pdb = self.getProfiWikiContainers(mwApp)
            if args.plantuml:
                pmw.install_plantuml()
            if args.fontawesome:
                pmw.install_fontawesome()
            if args.cron:
                pmw.start_cron()
            if args.patch:
                self.patch(pmw)
        if args.update:
            self.update(mwApp)

    def getMwCluster(self, withGenerate: bool = True) -> MediaWikiCluster:
        """
        get a mediawiki Cluster for my configuration

        Args:
            withGenerate(bool): if True regenerate the configuration files

        Returns:
            MediaWikiCluster: the MediaWiki Cluser
        """
        if self.mwCluster is not None:
            return self.mwCluster
        mwCluster = MediaWikiCluster(config=self.config)
        # generate
        mwCluster.createApps(withGenerate=withGenerate)
        self.mwCluster = mwCluster
        return mwCluster

    def getMwApp(self, withGenerate: bool = True):
        """
        get my mediawiki Docker application
        """
        mwCluster = self.getMwCluster(withGenerate)
        if not self.config.version in mwCluster.apps:
            raise Exception(
                f"Mediawiki version {self.config.version} missing {mwCluster.apps.keys()}"
            )
        mwApp = mwCluster.apps[self.config.version]
        return mwApp

    def getProfiWikiContainers(self, mwApp: DockerApplication):
        """
        get the two containers - for mediawiki and the database

        Args:
            mwApp(DockerApplication): the MediaWiki Docker Application

        Returns:
            Tuple(ProfiWikiContainer,ProfiWikiContainer): MediaWiki, Database
        """
        mw, db = mwApp.getContainers()
        pmw = ProfiWikiContainer(mw)
        pdb = ProfiWikiContainer(db)
        return pmw, pdb

    def patch(self, pwc: ProfiWikiContainer):
        """
        apply profi wiki patches to the given ProfiWikiContainer
        """
        if not pwc.dc:
            raise ("no container to apply patch")
        ls_path = "/var/www/html/LocalSettings.php"
        timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d")
        with tempfile.NamedTemporaryFile(
            mode="w", prefix="LocalSettings_", suffix=".php"
        ) as ls_file:
            pwc.log_action(f"patching {ls_file.name}")
            pwc.dc.container.copy_from(ls_path, ls_file.name)
            patch = Patch(file_path=ls_file.name)
            lines = f"""// modified by profiwiki
// use WikiEditor e.g. for MsUpload
wfLoadExtension( 'WikiEditor' );
# make this an intranet - comment out if you want this to be a public wiki
# The following permissions were set based on your choice in the installer
$wgGroupPermissions['*']['createaccount'] = false;
$wgGroupPermissions['*']['edit'] = false;
$wgGroupPermissions['*']['read'] = false;
# Allow properties in Templates
$smwgNamespacesWithSemanticLinks[NS_TEMPLATE] = true;
# WF 2015-01-20
# allow string functions - needed for Template:Link
$wgPFEnableStringFunctions=true;
// allow raw HTML
$wgRawHtml = true;
// allow images
$wgAllowImageTag=true;
// InstantCommons allows wiki to use images from https://commons.wikimedia.org
$wgUseInstantCommons = true;
// avoid showing (expected) deprecation warnings
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
# # add support for special properties
# see https://www.semantic-mediawiki.org/wiki/Help:$smwgPageSpecialProperties
# Modification date
$smwgPageSpecialProperties[] = '_MDAT';
# Creation date
$smwgPageSpecialProperties[] = '_CDAT';
# Is a new page
$smwgPageSpecialProperties[] = '_NEWP';
# Last editor is
$smwgPageSpecialProperties[] = '_LEDT';
# Media type
$smwgPageSpecialProperties[] = '_MEDIA';
# MIME type
$smwgPageSpecialProperties[] = '_MIME';
// https://www.mediawiki.org/wiki/Extension:UserFunctions
$wgUFEnabledPersonalDataFunctions = ['ip','nickname','realname','useremail','username',];
// allow user functions in main mediawiki space
$wgUFAllowedNamespaces[NS_MAIN] = true;
# increase query limit
$smwgQMaxLimit = 20000;
//Default width for the PDF object container.
$wgPdfEmbed['width'] = 800;
//Default height for the PDF object container.
$wgPdfEmbed['height'] = 1090;
//Allow user the usage of the tag
$wgGroupPermissions['*']['embed_pdf'] = true;
// config parameters for MsUpload
// https://www.mediawiki.org/wiki/Extension:MsUpload
$wgMSU_useDragDrop = true; // Should the drag & drop area be shown? (Not set by default)
$wgMSU_showAutoCat = true; // Files uploaded while editing a category page will be added to that category
$wgMSU_checkAutoCat = true; // Whether the checkbox for adding a category to a page is checked by default
$wgMSU_useMsLinks = false; // Insert links in Extension:MsLinks style?
$wgMSU_confirmReplace = true; // Show the "Replace file?" checkbox
$wgMSU_imgParams = '400px'; // Default image parameters, for example "thumb|200px"
$wgMSU_uploadsize = '100mb'; // Max upload size through MsUpload
// general parameters for MsUpload
$wgEnableWriteAPI = true; // Enable the API
$wgEnableUploads = true; // Enable uploads
$wgAllowJavaUploads = true; // Solves problem with Office 2007 and newer files (docx, xlsx, etc.)
$wgGroupPermissions['user']['upload'] = true; // Allow regular users to upload files
# add more file upload options
$wgGroupPermissions['user']['upload_by_url'] = true;
$wgAllowCopyUploads = true;
$wgCopyUploadsFromSpecialUpload = true;
# http://www.mediawiki.org/wiki/Manual:Configuring_file_uploads/de
$wgFileExtensions = array_merge($wgFileExtensions, array('doc', 'gcode',
'gpx','htm','html','jscad','jpg','pdf','ppt','docx', 'docxm','xlsx','xlsm','mp3','mp4','odp','otp','pptx', 'pptm','reqif','reqifz','rtf','rythm'
,'scad','sh','stl','svg','vcf','vim','uew','xls','xml','zip'));
# allow html
$wgVerifyMimeType=false;
# disable upload script checks ...
$wgDisableUploadScriptChecks = true;
"""
            patch.add_text(lines)
            patch.save()
            pwc.dc.container.copy_to(ls_file.name, ls_path)

    def update(self, mwApp):
        """
        run the update script
        """
        mwApp.execute("/root/update.sh")

    def check(self, mwApp):
        """
        check
        """
        mwApp.check()

    def create(self, mwApp, forceRebuild: bool = False):
        """
        create a profiwiki mediawiki
        """
        mwApp.start(forceRebuild=forceRebuild)

    def down(self, mwApp, forceRebuild: bool = False):
        """
        shut down the profiwiki base mediawiki
        """
        mwApp.down(forceRebuild=forceRebuild)

    def list(self, mwApp):
        """
        list the profi wikis
        """
        print(json.dumps(mwApp.config.as_dict(), indent=2))
        pass

    def check_wikiuser(self, mwApp: DockerApplication):
        """ """
        print(f"Checking WikiUser ... for {mwApp.config.container_base_name}")
        wikiUsers = WikiUser.getWikiUsers(lenient=True)
        if not mwApp.config.wikiId:
            print("no WikiId configured")
            return
        if not mwApp.config.wikiId in wikiUsers:
            print(f"no wikiUser for wikiId {mwApp.config.wikiId} found")
            return
        wikiUser = wikiUsers[mwApp.config.wikiId]
        if mwApp.config.password != wikiUser.getPassword():
            print(f"configured password is different then {mwApp.config.wikiId}")
        else:
            print(
                f"wikiUser for wikiId {mwApp.config.wikiId} is available and password as configured"
            )
        pass

    def apache_config(self, mwApp: DockerApplication) -> str:
        """
        get the apache configuration for the given mediawiki Docker application

        Args:
            mwApp(DockerApplication): the docker application to generate the configuration for
        """
        config = mwApp.config
        apache_config = f"""<VirtualHost *:80 >
    # The ServerName directive sets the request scheme, hostname and port that
    # the server uses to identify itself. This is used when creating
    # redirection URLs. In the context of virtual hosts, the ServerName
    # specifies what hostname must appear in the request's Host: header to
    # match this virtual host. For the default virtual host (this file) this
    # value is not decisive as it is used as a last resort host regardless.
    # However, you must set it for any further virtual host explicitly.
    ServerName {config.host}

    ServerAdmin webmaster@{config.host}
    #DocumentRoot /var/www/html

    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    # error, crit, alert, emerg.
    # It is also possible to configure the loglevel for particular
    # modules, e.g.
    #LogLevel info ssl:warn

    ErrorLog ${{APACHE_LOG_DIR}}/{config.container_base_name}_error.log
    CustomLog ${{APACHE_LOG_DIR}}/{config.container_base_name}_access.log combined

    # For most configuration files from conf-available/, which are
    # enabled or disabled at a global level, it is possible to
    # include a line for only one particular virtual host. For example the
    # following line enables the CGI configuration for this host only
    # after it has been globally disabled with "a2disconf".
    #Include conf-available/serve-cgi-bin.conf

    # Mediawiki installations
    ProxyPass / http://localhost:{config.port}/
    ProxyPassReverse / http://localhost:{config.port}/
</VirtualHost>"""
        return apache_config

__init__(prefix='pw', smw_version='4.1.3', mw_version='1.39.8', port=9079)

constructor

Source code in profiwiki/profiwiki_core.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def __init__(
    self,
    prefix: str = "pw",
    smw_version="4.1.3",
    mw_version="1.39.8",
    port: int = 9079,
):
    """
    constructor
    """
    self.os_name = platform.system()
    self.os_uname = os.uname()
    self.os_release = platform.release()
    self.args = None
    self.config = MwClusterConfig()
    self.config.smw_version = smw_version
    self.config.random_password = False
    self.config.prefix = prefix
    self.config.base_port = port
    self.config.sql_port = port - 1
    self.config.port = port
    self.config.versions = [mw_version]
    self.config.version = mw_version
    self.config.container_base_name = "pw"
    self.config.extensionNameList = [
        "Admin Links",
        "Diagrams",
        "Graph",
        "Header Tabs",
        "ImageMap",
        "ImageLink",
        "MagicNoCache",
        "Maps9",
        "Mermaid",
        "MsUpload",
        "Nuke",
        "Page Forms",
        "ParserFunctions",
        "PDFEmbed",
        "Renameuser",
        "Replace Text",
        "Semantic Result Formats",
        "SyntaxHighlight",
        "Variables",
        "UserFunctions",
        "YouTube",
    ]
    self.config.logo = "https://wiki.bitplan.com/images/wiki/thumb/6/63/Profiwikiicon.png/96px-Profiwikiicon.png"
    self.config.__post_init__()
    self.mwCluster = None
    pass

apache_config(mwApp)

get the apache configuration for the given mediawiki Docker application

Parameters:

Name Type Description Default
mwApp(DockerApplication)

the docker application to generate the configuration for

required
Source code in profiwiki/profiwiki_core.py
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    def apache_config(self, mwApp: DockerApplication) -> str:
        """
        get the apache configuration for the given mediawiki Docker application

        Args:
            mwApp(DockerApplication): the docker application to generate the configuration for
        """
        config = mwApp.config
        apache_config = f"""<VirtualHost *:80 >
    # The ServerName directive sets the request scheme, hostname and port that
    # the server uses to identify itself. This is used when creating
    # redirection URLs. In the context of virtual hosts, the ServerName
    # specifies what hostname must appear in the request's Host: header to
    # match this virtual host. For the default virtual host (this file) this
    # value is not decisive as it is used as a last resort host regardless.
    # However, you must set it for any further virtual host explicitly.
    ServerName {config.host}

    ServerAdmin webmaster@{config.host}
    #DocumentRoot /var/www/html

    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    # error, crit, alert, emerg.
    # It is also possible to configure the loglevel for particular
    # modules, e.g.
    #LogLevel info ssl:warn

    ErrorLog ${{APACHE_LOG_DIR}}/{config.container_base_name}_error.log
    CustomLog ${{APACHE_LOG_DIR}}/{config.container_base_name}_access.log combined

    # For most configuration files from conf-available/, which are
    # enabled or disabled at a global level, it is possible to
    # include a line for only one particular virtual host. For example the
    # following line enables the CGI configuration for this host only
    # after it has been globally disabled with "a2disconf".
    #Include conf-available/serve-cgi-bin.conf

    # Mediawiki installations
    ProxyPass / http://localhost:{config.port}/
    ProxyPassReverse / http://localhost:{config.port}/
</VirtualHost>"""
        return apache_config

check(mwApp)

check

Source code in profiwiki/profiwiki_core.py
296
297
298
299
300
def check(self, mwApp):
    """
    check
    """
    mwApp.check()

check_wikiuser(mwApp)

Source code in profiwiki/profiwiki_core.py
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
def check_wikiuser(self, mwApp: DockerApplication):
    """ """
    print(f"Checking WikiUser ... for {mwApp.config.container_base_name}")
    wikiUsers = WikiUser.getWikiUsers(lenient=True)
    if not mwApp.config.wikiId:
        print("no WikiId configured")
        return
    if not mwApp.config.wikiId in wikiUsers:
        print(f"no wikiUser for wikiId {mwApp.config.wikiId} found")
        return
    wikiUser = wikiUsers[mwApp.config.wikiId]
    if mwApp.config.password != wikiUser.getPassword():
        print(f"configured password is different then {mwApp.config.wikiId}")
    else:
        print(
            f"wikiUser for wikiId {mwApp.config.wikiId} is available and password as configured"
        )
    pass

create(mwApp, forceRebuild=False)

create a profiwiki mediawiki

Source code in profiwiki/profiwiki_core.py
302
303
304
305
306
def create(self, mwApp, forceRebuild: bool = False):
    """
    create a profiwiki mediawiki
    """
    mwApp.start(forceRebuild=forceRebuild)

down(mwApp, forceRebuild=False)

shut down the profiwiki base mediawiki

Source code in profiwiki/profiwiki_core.py
308
309
310
311
312
def down(self, mwApp, forceRebuild: bool = False):
    """
    shut down the profiwiki base mediawiki
    """
    mwApp.down(forceRebuild=forceRebuild)

getMwApp(withGenerate=True)

get my mediawiki Docker application

Source code in profiwiki/profiwiki_core.py
171
172
173
174
175
176
177
178
179
180
181
def getMwApp(self, withGenerate: bool = True):
    """
    get my mediawiki Docker application
    """
    mwCluster = self.getMwCluster(withGenerate)
    if not self.config.version in mwCluster.apps:
        raise Exception(
            f"Mediawiki version {self.config.version} missing {mwCluster.apps.keys()}"
        )
    mwApp = mwCluster.apps[self.config.version]
    return mwApp

getMwCluster(withGenerate=True)

get a mediawiki Cluster for my configuration

Parameters:

Name Type Description Default
withGenerate(bool)

if True regenerate the configuration files

required

Returns:

Name Type Description
MediaWikiCluster MediaWikiCluster

the MediaWiki Cluser

Source code in profiwiki/profiwiki_core.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def getMwCluster(self, withGenerate: bool = True) -> MediaWikiCluster:
    """
    get a mediawiki Cluster for my configuration

    Args:
        withGenerate(bool): if True regenerate the configuration files

    Returns:
        MediaWikiCluster: the MediaWiki Cluser
    """
    if self.mwCluster is not None:
        return self.mwCluster
    mwCluster = MediaWikiCluster(config=self.config)
    # generate
    mwCluster.createApps(withGenerate=withGenerate)
    self.mwCluster = mwCluster
    return mwCluster

getProfiWikiContainers(mwApp)

get the two containers - for mediawiki and the database

Parameters:

Name Type Description Default
mwApp(DockerApplication)

the MediaWiki Docker Application

required

Returns:

Name Type Description
Tuple (ProfiWikiContainer, ProfiWikiContainer)

MediaWiki, Database

Source code in profiwiki/profiwiki_core.py
183
184
185
186
187
188
189
190
191
192
193
194
195
196
def getProfiWikiContainers(self, mwApp: DockerApplication):
    """
    get the two containers - for mediawiki and the database

    Args:
        mwApp(DockerApplication): the MediaWiki Docker Application

    Returns:
        Tuple(ProfiWikiContainer,ProfiWikiContainer): MediaWiki, Database
    """
    mw, db = mwApp.getContainers()
    pmw = ProfiWikiContainer(mw)
    pdb = ProfiWikiContainer(db)
    return pmw, pdb

list(mwApp)

list the profi wikis

Source code in profiwiki/profiwiki_core.py
314
315
316
317
318
319
def list(self, mwApp):
    """
    list the profi wikis
    """
    print(json.dumps(mwApp.config.as_dict(), indent=2))
    pass

patch(pwc)

apply profi wiki patches to the given ProfiWikiContainer

Source code in profiwiki/profiwiki_core.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
    def patch(self, pwc: ProfiWikiContainer):
        """
        apply profi wiki patches to the given ProfiWikiContainer
        """
        if not pwc.dc:
            raise ("no container to apply patch")
        ls_path = "/var/www/html/LocalSettings.php"
        timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%d")
        with tempfile.NamedTemporaryFile(
            mode="w", prefix="LocalSettings_", suffix=".php"
        ) as ls_file:
            pwc.log_action(f"patching {ls_file.name}")
            pwc.dc.container.copy_from(ls_path, ls_file.name)
            patch = Patch(file_path=ls_file.name)
            lines = f"""// modified by profiwiki
// use WikiEditor e.g. for MsUpload
wfLoadExtension( 'WikiEditor' );
# make this an intranet - comment out if you want this to be a public wiki
# The following permissions were set based on your choice in the installer
$wgGroupPermissions['*']['createaccount'] = false;
$wgGroupPermissions['*']['edit'] = false;
$wgGroupPermissions['*']['read'] = false;
# Allow properties in Templates
$smwgNamespacesWithSemanticLinks[NS_TEMPLATE] = true;
# WF 2015-01-20
# allow string functions - needed for Template:Link
$wgPFEnableStringFunctions=true;
// allow raw HTML
$wgRawHtml = true;
// allow images
$wgAllowImageTag=true;
// InstantCommons allows wiki to use images from https://commons.wikimedia.org
$wgUseInstantCommons = true;
// avoid showing (expected) deprecation warnings
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
# # add support for special properties
# see https://www.semantic-mediawiki.org/wiki/Help:$smwgPageSpecialProperties
# Modification date
$smwgPageSpecialProperties[] = '_MDAT';
# Creation date
$smwgPageSpecialProperties[] = '_CDAT';
# Is a new page
$smwgPageSpecialProperties[] = '_NEWP';
# Last editor is
$smwgPageSpecialProperties[] = '_LEDT';
# Media type
$smwgPageSpecialProperties[] = '_MEDIA';
# MIME type
$smwgPageSpecialProperties[] = '_MIME';
// https://www.mediawiki.org/wiki/Extension:UserFunctions
$wgUFEnabledPersonalDataFunctions = ['ip','nickname','realname','useremail','username',];
// allow user functions in main mediawiki space
$wgUFAllowedNamespaces[NS_MAIN] = true;
# increase query limit
$smwgQMaxLimit = 20000;
//Default width for the PDF object container.
$wgPdfEmbed['width'] = 800;
//Default height for the PDF object container.
$wgPdfEmbed['height'] = 1090;
//Allow user the usage of the tag
$wgGroupPermissions['*']['embed_pdf'] = true;
// config parameters for MsUpload
// https://www.mediawiki.org/wiki/Extension:MsUpload
$wgMSU_useDragDrop = true; // Should the drag & drop area be shown? (Not set by default)
$wgMSU_showAutoCat = true; // Files uploaded while editing a category page will be added to that category
$wgMSU_checkAutoCat = true; // Whether the checkbox for adding a category to a page is checked by default
$wgMSU_useMsLinks = false; // Insert links in Extension:MsLinks style?
$wgMSU_confirmReplace = true; // Show the "Replace file?" checkbox
$wgMSU_imgParams = '400px'; // Default image parameters, for example "thumb|200px"
$wgMSU_uploadsize = '100mb'; // Max upload size through MsUpload
// general parameters for MsUpload
$wgEnableWriteAPI = true; // Enable the API
$wgEnableUploads = true; // Enable uploads
$wgAllowJavaUploads = true; // Solves problem with Office 2007 and newer files (docx, xlsx, etc.)
$wgGroupPermissions['user']['upload'] = true; // Allow regular users to upload files
# add more file upload options
$wgGroupPermissions['user']['upload_by_url'] = true;
$wgAllowCopyUploads = true;
$wgCopyUploadsFromSpecialUpload = true;
# http://www.mediawiki.org/wiki/Manual:Configuring_file_uploads/de
$wgFileExtensions = array_merge($wgFileExtensions, array('doc', 'gcode',
'gpx','htm','html','jscad','jpg','pdf','ppt','docx', 'docxm','xlsx','xlsm','mp3','mp4','odp','otp','pptx', 'pptm','reqif','reqifz','rtf','rythm'
,'scad','sh','stl','svg','vcf','vim','uew','xls','xml','zip'));
# allow html
$wgVerifyMimeType=false;
# disable upload script checks ...
$wgDisableUploadScriptChecks = true;
"""
            patch.add_text(lines)
            patch.save()
            pwc.dc.container.copy_to(ls_file.name, ls_path)

system_info()

collect system information

Source code in profiwiki/profiwiki_core.py
79
80
81
82
83
84
85
86
87
88
89
def system_info(self) -> str:
    """
    collect system information
    """
    info = f"""os: {self.os_name}"""
    if "Darwin" in info:
        release, _version, _machine = platform.mac_ver()
        info += f" MacOS {release}"
    else:
        info += f"{self.os_release}"
    return info

update(mwApp)

run the update script

Source code in profiwiki/profiwiki_core.py
290
291
292
293
294
def update(self, mwApp):
    """
    run the update script
    """
    mwApp.execute("/root/update.sh")

work(args)

work as instructed by the arguments

Parameters:

Name Type Description Default
args(Namespace)

the command line arguments

required
Source code in profiwiki/profiwiki_core.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def work(self, args):
    """
    work as instructed by the arguments

    Args:
        args(Namespace): the command line arguments
    """
    self.config.fromArgs(args)
    # make sure the wikiId is set from the container base name
    config_path = self.config.get_config_path()
    if os.path.isfile(config_path) and not self.config.forceRebuild:
        # reload the previous configuration e.g. based on container_name only
        previous_config = self.config.load(config_path)
        if self.config.verbose:
            print(f"ProfiWiki with previous configuration from {config_path}...")
        self.config = previous_config
    self.config.wikiId = self.config.container_base_name
    if args.bash:
        cmd = f"docker exec -it {self.config.container_base_name}-mw /bin/bash"
        print(cmd)
        return
    mwApp = self.getMwApp(withGenerate=args.forceRebuild)
    if self.config.verbose:
        print(
            f"ProfiWiki {mwApp.config.container_base_name} using port {mwApp.config.port} sqlport {mwApp.config.sql_port}"
        )
    if args.force_user:
        mwApp.createWikiUser(store=True)
    if args.all:
        self.create(mwApp, args.forceRebuild)
        pmw, _pdb = self.getProfiWikiContainers(mwApp)
        pmw.install_fontawesome()
        pmw.install_plantuml()
        self.patch(pmw)
        self.update(mwApp)
        pmw.start_cron()
    if args.wikiuser_check:
        self.check_wikiuser(mwApp)
    if args.apache:
        apache_config = self.apache_config(mwApp)
        print(apache_config)
    if args.create:
        self.create(mwApp, args.forceRebuild)
    if args.check:
        self.check(mwApp)
    if args.down:
        self.down(mwApp, args.forceRebuild)
    if args.list:
        self.list(mwApp)
    if args.plantuml or args.fontawesome or args.cron or args.patch:
        pmw, _pdb = self.getProfiWikiContainers(mwApp)
        if args.plantuml:
            pmw.install_plantuml()
        if args.fontawesome:
            pmw.install_fontawesome()
        if args.cron:
            pmw.start_cron()
        if args.patch:
            self.patch(pmw)
    if args.update:
        self.update(mwApp)

version

Created on 2023-04-01

@author: wf

Version

Bases: object

Version handling for ProfiWiki

Source code in profiwiki/version.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Version(object):
    """
    Version handling for ProfiWiki
    """

    name = "pyProfiWiki"
    description = """BITPlan's professional Semantic Mediawiki"""
    version = profiwiki.__version__
    date = "2023-04-01"
    updated = "2024-08-02"
    authors = "Wolfgang Fahl"
    doc_url = "https://wiki.bitplan.com/index.php/ProfiWiki"
    chat_url = "https://github.com/BITPlan/ProfiWiki/discussions"
    cm_url = "https://github.com/BITPlan/ProfiWiki"
    license = f"""Copyright 2015-2024 contributors. All rights reserved.
  Licensed under the Apache License 2.0
  http://www.apache.org/licenses/LICENSE-2.0
  Distributed on an "AS IS" basis without warranties
  or conditions of any kind, either express or implied."""
    longDescription = f"""{name} version {version}
{description}
  Created by {authors} on {date} last updated {updated}"""