Merge branch 'master' of github.com:openvk/openvk

This commit is contained in:
veselcraft 2021-11-04 00:23:42 +03:00
commit c049d5c0f3
19 changed files with 198 additions and 110 deletions

View file

@ -1,67 +1,72 @@
h1. <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo.png" alt="openvk" title="openvk" width="15%">OpenVK h1. <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo.png" alt="openvk" title="openvk" width="15%">OpenVK
*OpenVK* is an attempt to create a simple CMS that -cosplays- imitates old VK. Code provided here is not stable yet. *OpenVK* is an attempt to create a simple CMS that -cosplays- imitates old VK. Code provided here is not stable yet.
VKontakte belongs to Pavel Durov and VK Group. VKontakte belongs to Pavel Durov and VK Group.
To be honest, we don't even know whether it even works. However, this version is maintained and we will be happy to accept your bugreports "in our bug-tracker":https://github.com/openvk/openvk/projects/1. You should also be able to submit them using "ticketing system":https://openvk.su/support?act=new (you will need an OVK account for this). To be honest, we don't even know whether it even works. However, this version is maintained and we will be happy to accept your bugreports "in our bug-tracker":https://github.com/openvk/openvk/projects/1. You should also be able to submit them using "ticketing system":https://openvk.su/support?act=new (you will need an OVK account for this).
h2. When's the release? h2. When's the release?
Please use the master branch, as it has the most changes. Please use the master branch, as it has the most changes.
Updating the source code is done with this command: @git pull --recurse-submodules@ Updating the source code is done with this command: @git pull --recurse-submodules@
h2. Instances h2. Instances
* *"openvk.su":https://openvk.su/* * *"openvk.su":https://openvk.su/*
* "openvk.zavsc.pw":http://openvk.zavsc.pw/ * "openvk.zavsc.pw":http://openvk.zavsc.pw/
h2. Can I create my own OpenVK instance? h2. Can I create my own OpenVK instance?
Yes! And you're very welcome to. Yes! And you're very welcome to.
However, OVK makes use of Chandler Application Server. This software requires extensions, that may not be provided by your hosting provider (namely, sodium and yaml. this extensions are available on most of ISPManager hostings). However, OVK makes use of Chandler Application Server. This software requires extensions, that may not be provided by your hosting provider (namely, sodium and yaml. this extensions are available on most of ISPManager hostings).
Simply put, we would recommend you to use latest CentOS (running on your own VDS/Dedicated) with latest PHP from EPEL. We used to have this configuration on our "main instance":https://openvk.su/ and thus will be able to provide quicker support for OVK running in this environment. Simply put, we would recommend you to use latest CentOS (running on your own VDS/Dedicated) with latest PHP from EPEL. We used to have this configuration on our "main instance":https://openvk.su/ and thus will be able to provide quicker support for OVK running in this environment.
If you want, you can add your instance to the list above so that people can register there. If you want, you can add your instance to the list above so that people can register there.
h3. Installation procedure h3. Installation procedure
"samukhin":https://github.com/samukhin is working on a Docker container that contains a microblog version of OpenVK, see "#76":https://github.com/openvk/openvk/pull/76 for details. (Experimental) "samukhin":https://github.com/samukhin is working on a Docker container that contains a microblog version of OpenVK, see "#76":https://github.com/openvk/openvk/pull/76 for details. (Experimental)
# Install PHP 7, web-server, Composer, Node.js, Yarn and "Chandler":https://github.com/openvk/chandler # Install PHP 7, web-server, Composer, Node.js, Yarn and "Chandler":https://github.com/openvk/chandler
PHP 8 has *not* yet been tested, so you should not expect it to work. PHP 8 has *not* yet been tested, so you should not expect it to work.
# Install "commitcaptcha":https://github.com/openvk/commitcaptcha and OpenVK as Chandler extensions like this: # Install "commitcaptcha":https://github.com/openvk/commitcaptcha and OpenVK as Chandler extensions like this:
@git clone --recursive https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk@ @git clone --recursive https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk@
@git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha@ @git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha@
# And enable them: # And enable them:
@ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/@ @ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/@
@ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/@ @ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/@
# Import @install/init-static-db.sql@ to *same database* you installed Chandler to # Import @install/init-static-db.sql@ to *same database* you installed Chandler to
# Import @install/init-event-db.sql@ to *separate database* # Import @install/init-event-db.sql@ to *separate database*
# Copy @openvk-example.yml@ to @openvk.yml@ and change options # Copy @openvk-example.yml@ to @openvk.yml@ and change options
# Run @composer install@ in OpenVK directory # Run @composer install@ in OpenVK directory
# Move to @Web/static/js@ and execute @yarn install@ # Move to @Web/static/js@ and execute @yarn install@
# Set @openvk@ as your root app in @chandler.yml@ # Set @openvk@ as your root app in @chandler.yml@
*Note*: If OVK submodules were not downloaded beforehand (i.e. @--recursive@ was not used during cloning), this command *must be* executed in the @openvk@ folder: @git submodule update --init@ *Note*: If OVK submodules were not downloaded beforehand (i.e. @--recursive@ was not used during cloning), this command *must be* executed in the @openvk@ folder: @git submodule update --init@
Once you are done, you can login as a system administrator on the network itself (no registration required): Once you are done, you can login as a system administrator on the network itself (no registration required):
* *Login*: admin@localhost.localdomain6 * *Login*: admin@localhost.localdomain6
* *Password*: admin * *Password*: admin
It is recommended to change the password before using the built-in account. It is recommended to change the password before using the built-in account.
h3. If my website uses OpenVK, should I publish it's sources? h3. If my website uses OpenVK, should I publish it's sources?
You are encouraged to do so. We don't enforce this though. You can keep your sources to yourself (unless you distribute your OpenVK distro to other people). You are encouraged to do so. We don't enforce this though. You can keep your sources to yourself (unless you distribute your OpenVK distro to other people).
You also not required to publish source texts of your themepacks and plugins. You also not required to publish source texts of your themepacks and plugins.
h2. Where can I get assistance? h2. Where can I get assistance?
You may reach out to us via: You may reach out to us via:
* "Bug-tracker":https://github.com/openvk/openvk/projects/1 * "Bug-tracker":https://github.com/openvk/openvk/projects/1
* "Ticketing system":https://openvk.su/support?act=new * "Ticketing system":https://openvk.su/support?act=new
* Telegram chat: Go to "our channel":https://t.me/openvkch and open discussion in our channel menu. * Telegram chat: Go to "our channel":https://t.me/openvkch and open discussion in our channel menu.
* "Reddit":https://www.reddit.com/r/openvk/ * "Reddit":https://www.reddit.com/r/openvk/
* "Discussions":https://github.com/openvk/openvk/discussions * "Discussions":https://github.com/openvk/openvk/discussions
*Attention*: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), *please use contact us directly*: *Attention*: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), *please use contact us directly*:
* *Head of OpenVK Security Commitee*: stingray@jill.pl or "@id155":https://t.me/id155 * *Head of OpenVK Security Commitee*: stingray@jill.pl or "@id155":https://t.me/id155
* *Backend developer*: "@saddyteirusu":https://t.me/saddyteirusu * *Backend developer*: "@saddyteirusu":https://t.me/saddyteirusu
Codeberg repository clone:
<a href="https://codeberg.org/OpenVK/openvk">
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
</a>

View file

@ -204,6 +204,11 @@ class User extends RowModel
return $this->getRecord()->shortcode; return $this->getRecord()->shortcode;
} }
function getAlert(): ?string
{
return $this->getRecord()->alert;
}
function getBanReason(): ?string function getBanReason(): ?string
{ {
return $this->getRecord()->block_reason; return $this->getRecord()->block_reason;
@ -214,10 +219,10 @@ class User extends RowModel
return $this->getRecord()->type; return $this->getRecord()->type;
} }
function getCoins(): int function getCoins(): float
{ {
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"]) if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
return 0; return 0.0;
return $this->getRecord()->coins; return $this->getRecord()->coins;
} }
@ -563,6 +568,11 @@ class User extends RowModel
return !is_null($this->getBanReason()); return !is_null($this->getBanReason());
} }
function isOnline(): bool
{
return time() - $this->getRecord()->online <= 300;
}
function prefersNotToSeeRating(): bool function prefersNotToSeeRating(): bool
{ {
return !((bool) $this->getRecord()->show_rating); return !((bool) $this->getRecord()->show_rating);

View file

@ -30,18 +30,14 @@ final class UserPresenter extends OpenVKPresenter
if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $user->getShortCode()) if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $user->getShortCode())
$this->redirect("/" . $user->getShortCode(), static::REDIRECT_TEMPORARY_PRESISTENT); $this->redirect("/" . $user->getShortCode(), static::REDIRECT_TEMPORARY_PRESISTENT);
$then = date_create("@" . $user->getOnline()->timestamp());
$now = date_create();
$diff = date_diff($now, $then);
$this->template->albums = (new Albums)->getUserAlbums($user); $this->template->albums = (new Albums)->getUserAlbums($user);
$this->template->albumsCount = (new Albums)->getUserAlbumsCount($user); $this->template->albumsCount = (new Albums)->getUserAlbumsCount($user);
$this->template->videos = (new Videos)->getByUser($user, 1, 2); $this->template->videos = (new Videos)->getByUser($user, 1, 2);
$this->template->videosCount = (new Videos)->getUserVideosCount($user); $this->template->videosCount = (new Videos)->getUserVideosCount($user);
$this->template->notes = (new Notes)->getUserNotes($user, 1, 4); $this->template->notes = (new Notes)->getUserNotes($user, 1, 4);
$this->template->notesCount = (new Notes)->getUserNotesCount($user); $this->template->notesCount = (new Notes)->getUserNotesCount($user);
$this->template->user = $user; $this->template->user = $user;
$this->template->diff = $diff;
} }
} }

View file

@ -48,7 +48,10 @@ final class WallPresenter extends OpenVKPresenter
if(is_null($this->user)) if(is_null($this->user))
$canPost = false; $canPost = false;
else if($user > 0) else if($user > 0)
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity); if(!$owner->isBanned())
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
else
$this->flashFail("err", tr("error"), "Ошибка доступа");
else if($user < 0) else if($user < 0)
if($owner->canBeModifiedBy($this->user->identity)) if($owner->canBeModifiedBy($this->user->identity))
$canPost = true; $canPost = true;
@ -165,7 +168,10 @@ final class WallPresenter extends OpenVKPresenter
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1)) $wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
?? $this->flashFail("err", "Не удалось опубликовать пост", "Такого пользователя не существует."); ?? $this->flashFail("err", "Не удалось опубликовать пост", "Такого пользователя не существует.");
if($wall > 0) if($wall > 0)
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity); if(!$wallOwner->isBanned())
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
else
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену.");
else if($wall < 0) else if($wall < 0)
if($wallOwner->canBeModifiedBy($this->user->identity)) if($wallOwner->canBeModifiedBy($this->user->identity))
$canPost = true; $canPost = true;
@ -245,10 +251,11 @@ final class WallPresenter extends OpenVKPresenter
$this->logPostView($post, $wall); $this->logPostView($post, $wall);
$this->template->post = $post; $this->template->post = $post;
if ($post->getTargetWall() > 0) if ($post->getTargetWall() > 0) {
{
$this->template->wallOwner = (new Users)->get($post->getTargetWall()); $this->template->wallOwner = (new Users)->get($post->getTargetWall());
$this->template->isWallOfGroup = false; $this->template->isWallOfGroup = false;
if($this->template->wallOwner->isBanned())
$this->flashFail("err", tr("error"), "Ошибка доступа");
} else { } else {
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall())); $this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
$this->template->isWallOfGroup = true; $this->template->isWallOfGroup = true;

View file

@ -251,7 +251,7 @@
<a href="/language" class="link">{_footer_choose_language}</a> <a href="/language" class="link">{_footer_choose_language}</a>
<a href="/privacy" class="link">{_footer_privacy}</a> <a href="/privacy" class="link">{_footer_privacy}</a>
</div> </div>
<p>OpenVK <a href="/about:openvk2">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p> <p>OpenVK <a href="/about:openvk">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p>
<p n:ifcontent="ifcontent"> <p n:ifcontent="ifcontent">
{php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]} {php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]}
</p> </p>

View file

@ -79,7 +79,7 @@
#ovkLogo { #ovkLogo {
float: right; float: right;
border: 0; border: 0;
width: 30px; height: 30px;
padding-top: 6px; padding-top: 6px;
position: relative; position: relative;
} }
@ -97,7 +97,7 @@
<tr class="h"> <tr class="h">
<td> <td>
<h1 class="p" style="float: left;">OpenVK {=OPENVK_VERSION}</h1> <h1 class="p" style="float: left;">OpenVK {=OPENVK_VERSION}</h1>
<img id="ovkLogo" src="/assets/packages/static/openvk/img/logo.svg" alt="OpenVK Logo" /> <img id="ovkLogo" src="/assets/packages/static/openvk/img/logo_full.svg" alt="OpenVK Logo" />
</td> </td>
</tr> </tr>
</tbody> </tbody>

View file

@ -34,10 +34,10 @@
<!-- DEBUG: ONLINE REPORT: static {$user->getOnline()->timestamp()}s adjusted {$user->getOnline()->timestamp() + 2505600}s real {time()}s --> <!-- DEBUG: ONLINE REPORT: static {$user->getOnline()->timestamp()}s adjusted {$user->getOnline()->timestamp() + 2505600}s real {time()}s -->
<div n:if="$user->getOnline()->timestamp() + 2505600 > time()" style="float:right;"> <div n:if="$user->getOnline()->timestamp() + 2505600 > time()" style="float:right;">
{if $diff->i <= 5} {if $user->isOnline()}
<span><b>{_online}</b></span> <span><b>{_online}</b></span>
{else} {else}
<span>{_was_online} {$user->getOnline()}</span> <span>{_was_online} {$user->getOnline()}</span>
{/if} {/if}
</div> </div>
<div n:if="$user->onlineStatus() == 2" style="float:right;"> <div n:if="$user->onlineStatus() == 2" style="float:right;">
@ -76,6 +76,9 @@
{/if} {/if}
{if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)} {if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
<a href="/admin/users/id{$user->getId()}" class="profile_link">
{_manage_user_action}
</a>
<a href="javascript:banUser()" class="profile_link"> <a href="javascript:banUser()" class="profile_link">
{_ban_user_action} {_ban_user_action}
</a> </a>
@ -349,7 +352,7 @@
<div class="right_big_block"> <div class="right_big_block">
<div class="page_info"> <div class="page_info">
<div n:if="!is_null($alert = $user->getAlert())" class="user-alert">{$alert}</div>
<div class="accountInfo clearFix"> <div class="accountInfo clearFix">
<div class="profileName"> <div class="profileName">
<h2>{$user->getFullName()}</h2> <h2>{$user->getFullName()}</h2>

View file

@ -7,12 +7,10 @@
<img <img
src="{$author->getAvatarURL()}" src="{$author->getAvatarURL()}"
width="{ifset $compact}25{else}50{/ifset}" /> width="{ifset $compact}25{else}50{/ifset}" />
{if !$post->isPostedOnBehalfOfGroup() && !$compact} {if !$post->isPostedOnBehalfOfGroup() && !$compact}
<span n:if="$author->getOnline()->timestamp() + 2505600 > time()" class="post-online"> <span n:if="$author->isOnline()" class="post-online">
{if $diff->i <= 5} {_online}
{_online} </span>
{/if}
</span>
{/if} {/if}
</td> </td>
<td width="100%" valign="top"> <td width="100%" valign="top">

View file

@ -7,13 +7,11 @@
<img <img
src="{$author->getAvatarURL()}" src="{$author->getAvatarURL()}"
width="50" /> width="50" />
{if !$post->isPostedOnBehalfOfGroup() && !$compact} {if !$post->isPostedOnBehalfOfGroup() && !$compact}
<span n:if="$author->getOnline()->timestamp() + 2505600 > time()" class="post-online"> <span n:if="$author->isOnline()" class="post-online">
{if $diff->i <= 5} {_online}
{_online} </span>
{/if} {/if}
</span>
{/if}
</td> </td>
<td width="100%" valign="top"> <td width="100%" valign="top">
<div class="post-author"> <div class="post-author">

View file

@ -1432,4 +1432,14 @@ body.scrolled .toTop:hover {
#ovkDraw .literally .lc-picker .toolbar-button:hover:not(.disabled), #ovkDraw .literally .horz-toolbar .square-toolbar-button:hover:not(.disabled) { #ovkDraw .literally .lc-picker .toolbar-button:hover:not(.disabled), #ovkDraw .literally .horz-toolbar .square-toolbar-button:hover:not(.disabled) {
border-color: #cdcdcd; border-color: #cdcdcd;
} }
.user-alert {
margin-left: 8px;
margin-bottom: 8px;
padding: 4px;
border: 1px solid #c3a476;
font-weight: 900;
background-color: #f3ddbd;
color: #58462a;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -125,9 +125,16 @@ isomorphic-fetch@^2.1.1:
whatwg-fetch ">=0.10.0" whatwg-fetch ">=0.10.0"
jquery-ui@^1.12.1: jquery-ui@^1.12.1:
version "1.12.1" version "1.13.0"
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.12.1.tgz#bcb4045c8dd0539c134bc1488cdd3e768a7a9e51" resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.0.tgz#ab5ac65f37ca093c51b3478c4097f55bbc008f36"
integrity sha1-vLQEXI3QU5wTS8FIjN0+dop6nlE= integrity sha512-Osf7ECXNTYHtKBkn9xzbIf9kifNrBhfywFEKxOeB/OVctVmLlouV9mfc2qXCp6uyO4Pn72PXKOnj09qXetopCw==
dependencies:
jquery ">=1.8.0 <4.0.0"
"jquery@>=1.8.0 <4.0.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
jquery@^2.1.0: jquery@^2.1.0:
version "2.2.4" version "2.2.4"
@ -280,9 +287,9 @@ umbrellajs@^3.1.0:
integrity sha512-3qichMg1Q6EetLweBAT0L55O2W6CJe9qyiSt1RBnf+bcOqwJ4R7e2PDcoIUrCsg+uRo3DXOvurWdklBu0ia7fg== integrity sha512-3qichMg1Q6EetLweBAT0L55O2W6CJe9qyiSt1RBnf+bcOqwJ4R7e2PDcoIUrCsg+uRo3DXOvurWdklBu0ia7fg==
underscore@>=1.8.3, underscore@^1.9.1: underscore@>=1.8.3, underscore@^1.9.1:
version "1.10.2" version "1.13.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg== integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==
whatwg-fetch@>=0.10.0: whatwg-fetch@>=0.10.0:
version "3.6.2" version "3.6.2"

View file

@ -4,7 +4,7 @@
# OpenVK AutoInstallation Script for FreeBSD 12 # /( )` # # OpenVK AutoInstallation Script for FreeBSD 12 # /( )` #
# ------------------------------------------------------------- # \ \___ / | # # ------------------------------------------------------------- # \ \___ / | #
# # /- _ `-/ ' # # # /- _ `-/ ' #
# This script installs OpenVK 2 on an empty FreeBSD 12 box. # (/\/ \ \ /\ # # This script installs OpenVK on an empty FreeBSD 12 box. # (/\/ \ \ /\ #
# Copyright (c) 2020 OpenVK contributors # / / | ` \ # # Copyright (c) 2020 OpenVK contributors # / / | ` \ #
# ------------------------------------------------------------- # O O ) / | # # ------------------------------------------------------------- # O O ) / | #
# # `-^--'`< ' # # # `-^--'`< ' #

View file

@ -0,0 +1 @@
ALTER TABLE `profiles` ADD `alert` TEXT NULL DEFAULT NULL AFTER `since`;

View file

@ -0,0 +1 @@
ALTER TABLE `profiles` CHANGE `coins` `coins` REAL(20) UNSIGNED NOT NULL DEFAULT '0';

@ -1 +1 @@
Subproject commit 0c50ecdd77ac6643c7e5a29dcfff8ad84706ec3f Subproject commit 12998df6df2a28489ba2e65a9f255af115eb83de

View file

@ -1,6 +1,6 @@
name: "OpenVK 2 Electric Boogalo" name: "OpenVK"
description: "Yet another OpenVK social network" description: "Not-yet-federated open source social network inspired by VK"
author: "OpenVK contributors" author: "OpenVK Team"
version: "2.0" version: "0.1.0-tp3"
init: "bootstrap.php" init: "bootstrap.php"