Compare commits

...

34 Commits

Author SHA1 Message Date
Boris 5adc180307 Pour que ça soit plus ou moins compatible en ligne 2022-11-18 01:42:39 +01:00
Boris 2a33815c9c je fais du sale (2/2) 2022-11-18 01:35:48 +01:00
Boris 5f8b116aea je fais du sale (1/2) 2022-11-18 01:35:24 +01:00
Boris d5980664c3 Optimisations d'UI pre-p0ka-et-Hugo 2022-11-18 00:42:33 +01:00
Boris 57360f9c85 lien TiddlyWiki 2022-11-17 17:47:12 +01:00
Boris 218abd321d .gitignore 2022-11-17 17:46:47 +01:00
Boris 2654a5d448 ajout du Minelife mockup 2022-11-17 17:04:49 +01:00
Boris 7e639542c1 .gitignore 2022-11-17 17:04:26 +01:00
Boris aace984e14 fuck GetText 2022-11-17 16:56:10 +01:00
Boris 302da5a2be Docker, Makefile, etc. 2022-11-17 16:53:45 +01:00
Boris ca8cb2950f Un peu de refactoring 2022-11-17 16:53:09 +01:00
Boris 045bf888c4 Embarquement du GEFCO est sur le porte-conteneurs.
Reviewed-on: La_Bureautique/zeg1jeux#4
2022-11-15 21:07:37 +01:00
Boris e6285a2bca Appécieeez moooooooiiiiii 2022-11-15 21:03:48 +01:00
Boris 01a0e2e0eb Le réfectoire était pas bon 2022-11-15 21:00:53 +01:00
Yann Autissier 4f1dad2ddd add docker files 2022-11-15 19:51:21 +00:00
Boris 57f635e02f C'est le maître des clefs 2022-11-15 20:49:57 +01:00
Boris 3d5f09c060 Pour vous, je vais faire une exception 2022-11-15 20:04:38 +01:00
Boris 363079c2e4 J'ère 2022-11-15 20:02:54 +01:00
Boris ab14667900 Jaklis = Silkaj 2022-11-15 20:01:37 +01:00
Boris 04d951a129 Ouch, c'était salé (mais pas poivré, loool) 2022-11-15 19:46:02 +01:00
Boris bf34c4afe8 URL en code, looool 2022-11-15 19:30:43 +01:00
Boris 779eed6a37 GetText fait chier putain 2022-11-15 19:22:00 +01:00
Boris 89d60c4b12 La fucking biourotique once again 2022-11-15 17:04:50 +01:00
Boris 8b0b245c34 La fucking biourotique 2022-11-15 17:02:53 +01:00
Boris ffca895018 On brade pi 2022-11-14 18:05:36 +01:00
Boris f2886bcc8d It is knooown… 2022-11-14 15:45:55 +01:00
Boris d7a6f9b49b Merge branch 'main' of https://git.p2p.legal/La_Bureautique/zeg1jeux into main 2022-11-14 15:10:22 +01:00
Boris 35c5d3bec8 oups 2022-11-14 13:11:31 +01:00
Boris 1205ecf37b Fix | fonction donneMoiSaPutaindeG1Pub() sur passerelle Libra 2022-11-14 12:53:10 +01:00
Boris 30a80cda2c Le beurre aux tiques 2022-11-07 03:36:05 +01:00
Boris 0926c2bdab Amélioration gestion des exceptions 2022-11-07 03:33:49 +01:00
Boris f61db7a385 Fix | fonction donneMoiSaPutaindeG1Pub() sur passerelle Libra 2022-11-07 01:20:25 +01:00
Boris e7841d40de La Bureautique se meurt 2022-10-31 21:40:42 +01:00
Boris c7c6598a52 La Bureautique refactorise ta mère 2022-10-31 20:12:51 +01:00
209 changed files with 5484 additions and 1198 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
.git*

1
.env.dist Normal file
View File

@ -0,0 +1 @@
DOCKER_SERVICE_80_TAGS=urlprefix-zeg1jeux.${APP_DOMAIN}

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
.env
cache/
tests/
minelife.html
vendors/keygen/__pycache__

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
MYOS ?= ../myos
MYOS_REPOSITORY ?= https://github.com/aynicos/myos
-include $(MYOS)/make/include.mk
$(MYOS):
-@git clone $(MYOS_REPOSITORY) $(MYOS)
ENV_VARS += DOCKER_INTERNAL_DOCKER_HOST

View File

@ -10,3 +10,6 @@ sudo apt install python3-gpg python3-jwcrypto
python3 -m pip install -U pgpy pynentry SecureBytes
```
```
sudo vendor/jaklis/setup.sh
```

View File

@ -8,7 +8,7 @@ error_reporting(E_ALL);
session_start();
define('DEFAULT_RADIUS', 50);
define('DEFAULT_SEARCH_RADIUS', 50);
define('TAILLE_SPRITE', 32);
define('DEMI_TAILLE_SPRITE', (TAILLE_SPRITE/2));
@ -18,6 +18,8 @@ define('SONAR_DURATION', 5);
define("LAT_ASTROPORT_ONE", 44.22484418236386);
define("LON_ASTROPORT_ONE", 1.6395813014177014);
define('MAX_NEARBY_PLACES', 15);
class Player {
private $pubkey;
@ -54,12 +56,6 @@ $players = [
new Player('8PTThXiUSwwuPoqQWw3tuAn4MpvzQzpKhs6LMuiozS7Z', 'kimamila')
];
if (!isset($_SESSION['radius'])) {
$_SESSION['radius'] = DEFAULT_RADIUS;
}
/*
=====================
@ -85,7 +81,7 @@ $games = [
]
];
$radiuses = [5, 10, 20, 50];
$radiuses = [5, 10, 20, 50, 100];
define('DEFAULT_GAME', 'spationaute');
@ -93,11 +89,16 @@ if (!isset($_SESSION['gameId'])) {
$_SESSION['gameId'] = DEFAULT_GAME;
}
define('GAME_JS_DIR', 'themes/'. $_SESSION['gameId'] . '/js/map');
define('DEFAULT_WEBPAGE_TITLE', 'La bureautique');
$javascripts = [
'header' => [],
'footer' => []
];
require_once('functions.php');

98
docker/Dockerfile Normal file
View File

@ -0,0 +1,98 @@
FROM seblucas/alpine-nginx-php as dist
LABEL maintainer aynic.os <support+docker@asycn.io>
ARG DOCKER_BUILD_DIR
ARG DOCKER_MACHINE=x86_64
ARG DOCKER_SYSTEM=Linux
RUN apk upgrade --no-cache \
&& apk add --no-cache \
bash \
ca-certificates \
gettext \
libc6-compat \
libsodium \
make \
py3-gpgme \
py3-pip
ARG IPFS_VERSION=0.16.0
RUN { OS="$(echo ${DOCKER_SYSTEM} |awk '{print tolower($0)}')"; \
ARCH="$(echo ${DOCKER_MACHINE})"; \
wget -qO - https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.${OS}.${ARCH}.tar.xz \
|tar --strip-components 1 -C /usr/local/bin -xJf - shellcheck-stable/shellcheck; } \
&& { ARCH="$(echo ${DOCKER_MACHINE} |awk '/x86_64/ {print "amd64"}; /aarch64/ {print "arm64"}')"; \
wget -qO - https://github.com/ipfs/kubo/releases/download/v${IPFS_VERSION}/kubo_v${IPFS_VERSION}_${OS}-${ARCH}.tar.gz \
|tar --strip-components 1 -C /usr/local/bin -xzf - kubo/ipfs; } \
&& mkdir -p /usr/local/lib/shellspec \
&& wget -qO - https://github.com/shellspec/shellspec/archive/refs/heads/master.tar.gz \
|tar --strip-components 1 -C /usr/local/lib/shellspec -xzf - \
&& ln -s /usr/local/lib/shellspec/shellspec /usr/local/bin/shellspec
RUN apk add --no-cache --virtual .build-deps \
build-base \
cargo \
libffi-dev \
openssl-dev \
py3-wheel \
python3-dev \
swig \
&& mkdir -p /usr/local/src/jaklis \
&& wget -qO - https://github.com/aynicos/jaklis/archive/master.tar.gz \
|tar --strip-components 1 -C /usr/local/src/jaklis -xzf - \
&& pip3 install -r /usr/local/src/jaklis/requirements.txt \
&& ln -s /usr/local/src/jaklis/jaklis.py /usr/local/bin/jaklis \
&& chmod 0755 /usr/local/bin/jaklis \
&& /usr/local/bin/jaklis --help >/dev/null \
&& mkdir -p /usr/local/src/dpgpid \
&& wget -qO - https://github.com/aynicos/dpgpid/archive/wip.tar.gz \
|tar --strip-components 1 -C /usr/local/src/dpgpid -xzf - \
&& pip3 install -r /usr/local/src/dpgpid/requirements.txt \
&& ln -s /usr/local/src/dpgpid/keygen /usr/local/bin/keygen \
&& chmod 0755 /usr/local/bin/keygen \
&& /usr/local/bin/keygen --help >/dev/null \
&& rm -rf /root/.cache \
&& apk del --no-network .build-deps \
&& find ./lib -type f -executable -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \
|tr ',' '\n' \
|sort -u \
|awk 'system("[ -e /lib/"$1" -o -e /usr/lib/"$1" -o -e ./lib/python'"${PYTHON_RELEASE}"'/site-packages/*/"$1" ]") == 0 { next } { print "so:" $1 }' \
|xargs -rt apk add --no-cache
RUN sed -i 's/^}/ location \/ { index index.php; }\n}/' /etc/nginx/http.d/default.conf
FROM dist as master
ARG DOCKER_BUILD_DIR
ARG DOCKER_GID
ARG SHELL=/bin/bash
ARG UID
ARG USER
ENV UID=${UID}
ENV GID=${UID}
ENV USER=nginx
# If we provide a numeric UID
RUN [ "$UID" -eq "$UID" ] 2>/dev/null \
# Remove user with $UID if it is not our $USER
&& if [ "$(getent passwd $UID |awk -F: '{print $1}')" != "$USER" ]; then \
sed -i '/^'$(getent passwd $UID |awk -F: '{print $1}')':x:'$UID':/d' /etc/passwd; \
sed -i '/^'$(getent group $GID |awk -F: '{print $1}')':x:'$GID':/d' /etc/group; \
fi \
# Force $UID if our $USER already exists
&& sed -i 's/^'$USER':x:[0-9]\+:[0-9]\+:/'$USER':x:'$UID':'$GID':/' /etc/passwd \
&& sed -i 's/^'$USER':x:[0-9]\+:/'$USER':x:'$GID':/' /etc/group \
# Create $USER if it does not exist
&& if [ "$(getent passwd $UID)" = "" ]; then \
echo "$USER:x:$UID:$GID::/home/$USER:$SHELL" >> /etc/passwd; \
echo "$USER:\!:$(($(date +%s) / 60 / 60 / 24)):0:99999:7:::" >> /etc/shadow; \
echo "$USER:x:$GID:" >> /etc/group; \
fi \
&& mkdir -p /home/$USER \
&& chown $UID:$GID /home/$USER \
|| true
ENV SHELL=${SHELL}
WORKDIR /var/www
RUN rm /etc/php7/conf.d/00_opcache.ini
RUN sed -i 's/^;php_flag[display_errors] = off/php_flag[display_errors] = on/' /etc/php7/php-fpm.d/www.conf

View File

@ -0,0 +1,25 @@
version: '3.6'
services:
docker:
environment:
- ENV=${ENV}
- SHELL=${DOCKER_SHELL}
labels:
- SERVICE_80_CHECK_HTTP=/
- SERVICE_80_NAME=${COMPOSE_SERVICE_NAME}-docker-80
- SERVICE_80_TAGS=${DOCKER_SERVICE_80_TAGS}
networks:
- private
- public
extra_hosts:
- astroport.localhost:${DOCKER_INTERNAL_DOCKER_HOST}
networks:
private:
external: true
name: ${DOCKER_NETWORK_PRIVATE}
public:
external: true
name: ${DOCKER_NETWORK_PUBLIC}

34
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,34 @@
version: '3.6'
services:
docker:
build:
args:
- DOCKER_BUILD_DIR=docker
- GID=${GID}
- IPFS_VERSION=${IPFS_VERSION:-0.16.0}
- UID=${UID}
- USER=${USER}
context: ../
dockerfile: docker/Dockerfile
ports:
- 80
restart: always
volumes:
- ipfs:${HOME}/.ipfs:cached,ro
- data:/var/www:delegated
working_dir: /var/www
volumes:
ipfs:
driver: local
driver_opts:
type: none
device: ${HOME}/.ipfs
o: bind
data:
driver: local
driver_opts:
type: none
device: ${APP_DIR:-.}
o: bind

30
farfetched.php Normal file
View File

@ -0,0 +1,30 @@
<?php
require_once('config.php');
require_once('lib/Gchange.class.php');
$gchange = new Gchange();
$javascripts['header'][] = 'lib/js/farfetched.js';
$bodyIds = 'farfetched';
include_once('header.php');
?>
<section id="shippable">
<h2>Envoi possible</h2>
<ul id="shippable-offers" class="offers-list">
</ul>
</section>
<section id="immaterial">
<h2>Offres immatérielles</h2>
<ul id="immaterial-offers" class="offers-list">
</ul>
</section>
<?php
include_once('footer.php');

View File

@ -5,22 +5,28 @@
</footer>
<?php
if (isset($_SESSION['gameId']) and isset($bodyIds) and strpos($bodyIds, 'startpage') === false) {
$themeJSDir = 'themes/'. $_SESSION['gameId'] . '/js';
$files = scandir($themeJSDir);
$files = array_slice($files, 2);
// if (isset($_SESSION['gameId']) and ((isset($bodyIds) and strpos($bodyIds, 'startpage') === false)) or (!isset($bodyIds))) {
foreach ($files as $f) {
// $themeJSDir = 'themes/'. $_SESSION['gameId'] . '/js';
// $files = scandir($themeJSDir);
// $files = array_slice($files, 2);
echo '
<script type="text/javascript" src="'. $themeJSDir . '/' . $f .'"></script>
';
}
// foreach ($files as $f) {
// echo '
// <script type="text/javascript" src="'. $themeJSDir . '/' . $f .'"></script>
// ';
// }
// }
foreach ($javascripts['footer'] as $js) {
echo '
<script src="'. $js . '"></script>
';
}
?>
<script type="text/javascript" src="assets/js/places.js"></script>
</body>
</html>

View File

@ -8,7 +8,7 @@ $webpageTitle = isset($webpageTitle) ? $webpageTitle : DEFAULT_WEBPAGE_TITLE;
<title><?php echo $webpageTitle; ?></title>
<?php
if (isset($_SESSION['gameId']) and isset($bodyIds) and strpos($bodyIds, 'startpage') === false) {
if (isset($_SESSION['gameId']) and ((isset($bodyIds) and strpos($bodyIds, 'startpage') === false)) or (!isset($bodyIds))) {
$themeCSSDir = 'themes/'. $_SESSION['gameId'] . '/css';
$files = scandir($themeCSSDir);
@ -26,6 +26,15 @@ $webpageTitle = isset($webpageTitle) ? $webpageTitle : DEFAULT_WEBPAGE_TITLE;
<link rel="stylesheet" type="text/css" href="themes/deco.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<?php
foreach ($javascripts['header'] as $js) {
echo '
<script src="'. $js . '"></script>
';
}
?>
</head>
<?php
$bodyIds = !isset($bodyIds) ? '' : ' id="'. $bodyIds .'"';

View File

@ -1,11 +1,50 @@
<?php
require_once('config.php');
require_once('lib/Gchange.class.php');
require_once('lib/Keygen.class.php');
require_once('lib/Fred.class.php');
$bodyIds = 'home';
include_once('header.php');
$gchange = new Gchange();
$keygen = new Keygen();
// $ipfsHost = 'http://astroport.localhost:8080';
$ipfsHost = 'http://libra.copylaradio.com:8080';
try {
$player = $gchange->getUser($_SESSION['player_pubkey']);
} catch (Exception $errMsg) {
ErrorsHandler::kaput($errMsg);
}
try {
$twLink = $ipfsHost . '/ipns/' . $keygen->getIPNSPub($_SESSION['salt'], $_SESSION['pepper']);
} catch (Exception $errMsg) {
$twLink = '#';
$fred = new Fred();
$twLink = $ipfsHost . '/ipns/' . $fred->donneMoiLaPutainDeClefIPNS($_SESSION['salt'], $_SESSION['pepper']);
}
echo '
<header>
Bienvenue '. $player->getUserName() . ' !
<!-- ('. substr($player->getUserGchangeId(), 0, 8) . ') -->
</header>
<nav>
<ul>
<li id="dashboard-map">
@ -23,6 +62,30 @@ echo '
</span>
</a>
</li>
<li id="dashboard-farfetched">
<a href="farfetched.php">
<span>
À distance ou en livraison
</span>
</a>
</li>
<li id="dashboard-minelife">
<a href="minelife/index.html">
<span>
Manuel
</span>
</a>
</li>
<li id="dashboard-tiddlywiki">
<a href="'. $twLink .'">
<span>
'. ('Mon TiddlyWiki') .'
</span>
</a>
</li>
</ul>

View File

@ -4,7 +4,7 @@ require_once('Message.class.php');
function compareMsgDate ($msg1, $msg2) {
return ($msg1->getDate() < $msg2->getDate()) ? false : true;
return ($msg1->getDate() < $msg2->getDate()) ? -1 : 1;
}
class Conversation {

View File

@ -75,14 +75,14 @@ class DAO {
'duniter' => 2,
'cesiumplus' => 5,
'gchange' => 10,
'gchange' => 1,
];
private $nodeTimeoutIncrement = [
'duniter' => 2,
'cesiumplus' => 10,
'gchange' => 10
'gchange' => 3
];
private $node = NULL;
@ -115,8 +115,8 @@ class DAO {
if (!in_array($unit, $this->units)) {
$out = [];
$out[] = _('L\'unité renseignée n\'existe pas.');
$out[] = _('Vérifiez votre synthaxe.');
$out[] = 'L\'unité renseignée n\'existe pas.';
$out[] = 'Vérifiez votre synthaxe.';
$this->decease($out);
@ -205,7 +205,7 @@ class DAO {
<html>
<head>
<meta charset="utf-8" />
<title>'. _('Erreur critique') . '</title>
<title>'. ('Erreur critique') . '</title>
<style>
@ -251,16 +251,16 @@ class DAO {
if ($this->displayType == 'img') {
return _('DUĞ1');
return ('DUĞ1');
} else {
return _('DU<sub>Ğ1</sub>');
return ('DU<sub>Ğ1</sub>');
}
} else {
return _('Ğ1');
return ('Ğ1');
}
}
@ -476,20 +476,22 @@ class DAO {
if (empty($json)) {
$out = [];
$out[] = _('Aucun noeud '. $nodeType .' n\'a été trouvé ou la requête n\'a pas abouti.');
$out[] = _('Noeud interrogés : ');
$out[] = ('Aucun noeud '. $nodeType .' n\'a été trouvé ou la requête n\'a pas abouti.');
$out[] = ('Noeud interrogés : ');
$out = array_merge($out, $nodes);
$out[] = _('URI: ' . $uri);
$out[] = ('URI: ' . $uri);
if (isset($queryParams)) {
$out[] = _('Paramètres de la requête : ');
$out[] = ('Paramètres de la requête : ');
$out[] = '<pre>'. print_r($queryParams, true) . '<pre>';
}
$this->decease($out);
// $this->decease($out);
throw new Exception(implode("\n", $out));
}
return $json;

View File

@ -0,0 +1,64 @@
<?php
class ErrorsHandler {
public function __construct () {
}
static public function kaput ($errorMsgs) {
ob_get_clean(); // to prevent error message to display inside an HTML container (case of error generated by get method calls)
if (!is_array($errorMsgs)) {
$errorMsgs = explode("\n", $errorMsgs);
}
echo '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Erreur critique</title>
<style>
div {
overflow: auto;
word-wrap: break-word;
background-color: hsl(0, 100%, 69%);
color: hsl(0, 100%, 19%);
margin: 1em;
padding: 1em;
border-radius: 1em;
position: fixed;
top: 0;
left: 0;
width: calc(100% - 4em);
max-height: calc(100vh - 4em);
}
</style>
</head>
<body>
<div>';
foreach ($errorMsgs as $msg) {
echo '<pre>' . print_r($msg, true) . '</pre>';
}
echo '
</div>
</body>
</html>';
die;
}
}

View File

@ -1,39 +1,84 @@
<?php
class Fred {
private $gatewayProtocol = 'http';
private $gatewayDomain = 'libra.copylaradio.com';
// private $gatewayDomain = 'aries.copylaradio.com';
// private $gatewayDomain = 'astroport.localhost';
private $gatewayPort = '1234';
private $gatewayDelay = 3;
private $gatewayMaxRounds = 20;
public function __construct () {
}
public function donneMoiLAdresseIPDuServeurQuiHebergeMonTiddlyWiki ($salt, $pepper) {
}
public function donneMoiLaPutainDeClefIPNS ($prenomNom, $nomDuChienSuivieDeLaDateDeNaissanceDeJohnnyHallyday) {
$salt = $prenomNom;
$pepper = $nomDuChienSuivieDeLaDateDeNaissanceDeJohnnyHallyday;
$query = 'salt='. $salt .'&pepper='. $pepper;
$query = 'salt='. urlencode($salt) .'&pepper='. urlencode($pepper) . '&getipns=on';
$page1 = file_get_contents($this->gatewayProtocol . '://'. $this->gatewayDomain .':' . $this->gatewayPort . '/?' . $query);
if (empty($page1)) {
throw new Exception("J'ai pas pû récupérer la putain de première page.");
}
$page1 = @file_get_contents($this->gatewayProtocol . '://'. $this->gatewayDomain .':' . $this->gatewayPort . '/?' . $query)
or die('<p>On a fait du sale.</p>');
preg_match("`url='([^']+)'`isU", $page1, $matches);
$url = $matches[1];
$url .= '/?'. $query;
// die($url);
$opts2 = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: fr\r\n",
'follow_location' => 0
)
);
// echo 'fred 2';
$context2 = stream_context_create($opts2);
$page2 = false;
$rounds = 0;
while (($page2 === false) and $rounds < $this->gatewayMaxRounds) {
sleep($this->gatewayDelay);
// echo "\n\n\nround n°" . $rounds . "\n";
$page2 = @file_get_contents($url, false, $context2);
// echo '<pre>'. print_r(htmlspecialchars($page2), true) . '</pre>';
$rounds++;
}
sleep($this->gatewayDelay);
if ($page2 === false) {
$page2 = @file_get_contents($url)
or die('</p>On a chié dans la colle.</p>');
throw new Exception("J'ai pas pû me connecter à ". $url ." pour récupérer la putain de deuxième page.");
} else if (empty($page2)) {
preg_match("`url='.*/ipns/([^']+)'`isU", $page2, $matches);
throw new Exception("J'ai pas pû récupérer la putain de deuxième page.");
}
preg_match("`/ipns/(.+)`is", $page2, $matches);
$ipnsKey = $matches[1];
@ -52,8 +97,12 @@ class Fred {
// echo '<pre>'; var_dump(htmlspecialchars($url)); echo '</pre>';
$page1 = file_get_contents($url)
or die('<p>On a fait du sale.</p>');
$page1 = file_get_contents($url);
if (empty($page1)) {
throw new Exception("J'ai pas pû récupérer la putain de première page.");
}
// echo '<pre>'; var_dump(htmlspecialchars($page1)); echo '</pre>';
@ -64,11 +113,19 @@ class Fred {
// echo '<pre>'; var_dump($url); echo '</pre>';
$page2 = '';
$rounds = 0;
while (empty($page2)) {
while (empty($page2) and $rounds < $this->gatewayMaxRounds) {
sleep($this->gatewayDelay);
$page2 = @file_get_contents($url);
$page2 = file_get_contents($url);
$rounds++;
}
if (empty($page2)) {
throw new Exception("J'ai pas pû récupérer la putain de deuxième page.");
}
// echo '<pre>'; var_dump(htmlspecialchars($page2)); echo '</pre>';
@ -78,41 +135,76 @@ class Fred {
return json_decode($json);
}
public function donneMoiSaPutaindeG1Pub ($prenomNom, $nomDuChienSuivieDeLaDateDeNaissanceDeJohnnyHallyday) {
$salt = $prenomNom;
$pepper = $nomDuChienSuivieDeLaDateDeNaissanceDeJohnnyHallyday;
$query = 'salt='. $salt .'&pepper='. $pepper . '&g1pub=on';
$query = 'salt='. urlencode($salt) .'&pepper='. urlencode($pepper) . '&g1pub=on';
$url = $this->gatewayProtocol . '://'. $this->gatewayDomain .':' . $this->gatewayPort . '/?' . $query;
// echo '<pre>'; var_dump(htmlspecialchars($url)); echo '</pre>';
$page1 = file_get_contents($url)
or die('<p>On a fait du sale.</p>');
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: fr\r\n",
'follow_location' => 0
)
);
$context = stream_context_create($opts);
$page1 = file_get_contents($url, false, $context);
if (empty($page1)) {
throw new Exception("J'ai pas pû récupérer la putain de première page.");
}
// die('<pre>' . htmlspecialchars($page1) . '</pre>');
preg_match("`url='([^']+)'`isU", $page1, $matches);
// die(
// '<pre>' . htmlspecialchars($page1) . '</pre>' .
// '<pre>' . print_r($matches, true) . '</pre>'
// '<pre>' . htmlspecialchars($page1) . '</pre>' .
// '<pre>' . print_r($matches, true) . '</pre>'
// );
$url = $matches[1];
// die('<pre>' . var_dump($url, true) . '</pre>');
$opts2 = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: fr\r\n",
'follow_location' => 0
)
);
$context2 = stream_context_create($opts2);
$page2 = '';
while (empty($page2)) {
$rounds = 0;
while (empty($page2) and $rounds < $this->gatewayMaxRounds) {
sleep($this->gatewayDelay);
$page2 = @file_get_contents($url);
$page2 = file_get_contents($url, false, $context2);
$rounds++;
}
if (empty($page2)) {
throw new Exception("J'ai pas pû récupérer la putain de deuxième page.");
}
preg_match("`url='.*/user/([^']+)/'`isU", $page2, $matches);
@ -120,6 +212,5 @@ class Fred {
$gchangeId = $matches[1];
return $gchangeId;
}
}

View File

@ -7,6 +7,7 @@ require_once('DAO.class.php');
require_once('GchangeUser.class.php');
require_once('GchangeRecord.class.php');
require_once('GchangeRating.class.php');
require_once('ErrorsHandler.class.php');
class Gchange {
@ -18,9 +19,13 @@ class Gchange {
private $cacheLongevity = array(
'placesNearby' => 604800, // 3 jours
'placeDetails' => 2116800, // 7 jours
'placeVisitors' => 2116800, // 7 jours
'usersNearby' => 43200, // 12 heures
'users' => 43200, // 12 heures
'records' => 900 // 15 min
'users' => 2116800, // 12 heures
'userRecords' => 900 // 15 min
);
public function __construct () {
@ -28,71 +33,21 @@ class Gchange {
$this->dao = DAO::getInstance();
}
public function getRecordsByIssuer ($issuer) {
public function fetchJson ($uri, $queryParams = null) {
try {
$recordsCacheDir = 'records/user/';
$recordsCacheFile = $issuer . '.json';
$json = $this->dao->fetchJson($uri, 'gchange', $queryParams);
$json = $this->getJsonFromCache($recordsCacheDir, $recordsCacheFile, $this->cacheLongevity['records']);
} catch (Exception $errorMsgs) {
if (empty($json)) {
$n = 20;
$queryParams = [
'size' => $n,
'query' => [
'bool' => [
'filter' => [
'term' => [
'issuer' => $issuer
]
/*
, 'range' => [
'stock' => [
'gte' => 1
]
]
*/
]
/*
, 'filter' => [
]
*/
, 'must_not' => [
[ "term" => ["stock" => 0] ]
]
]
],
'sort' => [
['time' => 'desc']
]
];
$json = $this->dao->fetchJson('/market/record/_search', 'gchange', $queryParams);
$this->cacheJson($recordsCacheDir, $recordsCacheFile, $json);
throw $errorMsgs;
}
$result = json_decode($json);
$records = [];
foreach ($result->hits->hits as $hit) {
$records[] = new GchangeRecord($hit);
}
return $records;
return $json;
}
private function getJsonFromCache ($dir, $file, $cacheLongevity) {
private function getJsonFromCache ($dir, $file, $cacheLongevity = null) {
if (!$this->isActivatedCache) {
@ -103,13 +58,30 @@ class Gchange {
$json = null;
$jsonFullPath = $this->cacheDir . $dir . $file;
if (file_exists($jsonFullPath) and ((time() - filemtime($jsonFullPath)) < $cacheLongevity)) {
$json = file_get_contents($jsonFullPath);
if (!file_exists($jsonFullPath)) {
throw new Exception("Le fichier de cache suivant n'existe pas : " . $jsonFullPath);
}
return $json;
if ($cacheLongevity !== null and (time() - filemtime($jsonFullPath) > $cacheLongevity)) {
throw new Exception("Le fichier de cache est expiré.");
}
try {
$json = file_get_contents($jsonFullPath);
} catch (Exception $errorMsgs) {
throw new Exception($jsonFullPath . " n'a pas été trouvé");
} finally {
return $json;
}
}
private function cacheJson ($dir, $file, $json) {
@ -136,13 +108,30 @@ class Gchange {
$usersCacheDir = 'users/';
$usersCacheFile = $pubkey . '.json';
$json = $this->getJsonFromCache($usersCacheDir, $usersCacheFile, $this->cacheLongevity['users']);
try {
if (empty($json)) {
$json = $this->getJsonFromCache($usersCacheDir, $usersCacheFile, $this->cacheLongevity['users']);
$json = $this->dao->fetchJson(('/user/profile/'. $pubkey), 'gchange');
$this->cacheJson($usersCacheDir, $usersCacheFile, $json);
} catch (Exception $errorMsgs) {
try {
$json = $this->fetchJson('/user/profile/'. $pubkey);
$this->cacheJson($usersCacheDir, $usersCacheFile, $json);
} catch (Exception $errorMsgs) {
try {
$json = $this->getJsonFromCache($usersCacheDir, $usersCacheFile, null);
} catch (Exception $errorMsgs) {
throw new Exception("L'utilisateur " . $pubkey . " n'a été trouvé nulle part.");
}
}
}
$result = json_decode($json);
@ -150,63 +139,50 @@ class Gchange {
return new GchangeUser($result);
}
public function getPlacesNearUser ($user, $radius) {
return $this->getNearbyPlaces($user->getLat(), $user->getLon(), $radius);
/*
$placesCacheDir = 'places-nearby/user/';
$placesCacheFile = $user->getUserGchangeId() . '.json';
$json = $this->getJsonFromCache($placesCacheDir, $placesCacheFile, $this->cacheLongevity['placesNearby']);
if (empty($json)) {
$json = $this->getNearbyPlacesJson($user->getLat(), $user->getLon(), $radius);
$this->cacheJson($placesCacheDir, $placesCacheFile, $json);
}
$result = json_decode($json);
return $result->hits->hits;
*/
}
public function getNearbyPlaces ($lat, $lon, $maxDistance, $minDistance = NULL) {
$placesCacheDir = 'places-nearby/geopoint/';
$placesCacheFile = $lat . ',' . $lon . '.json';
$json = $this->getJsonFromCache($placesCacheDir, $placesCacheFile, $this->cacheLongevity['placesNearby']);
if (empty($json)) {
$json = $this->getNearbyPlacesJson($lat, $lon, $maxDistance, $minDistance);
$this->cacheJson($placesCacheDir, $placesCacheFile, $json);
}
$result = json_decode($json);
return $result->hits->hits;
}
public function getNearbyUsers ($lat, $lon, $maxDistance, $minDistance = NULL) {
$placesCacheDir = 'users-nearby/geopoint/';
$placesCacheFile = $lat . ',' . $lon . '.json';
$nearbyUsersCacheDir = 'users-nearby/geopoint/' . $maxDistance . 'km/';
$nearbyUsersCacheFile = $lat . ',' . $lon . '.json';
$json = $this->getJsonFromCache($placesCacheDir, $placesCacheFile, $this->cacheLongevity['usersNearby']);
try {
if (empty($json)) {
$json = $this->getJsonFromCache($nearbyUsersCacheDir, $nearbyUsersCacheFile, $this->cacheLongevity['usersNearby']);
} catch (Exception $errorMsgs) {
$n = 20;
$json = $this->getNearbyPlacesJson($lat, $lon, $maxDistance, $minDistance);
$queryParams = [
'size' => $n,
'query' => [
'bool' => [
'filter' => [
[
'geo_distance' => [
"distance" => $maxDistance . 'km',
"geoPoint"=> [
"lat" => $lat,
"lon" => $lon
]
]
]
]
]
]
];
$this->cacheJson($placesCacheDir, $placesCacheFile, $json);
try {
$json = $this->fetchJson('/users/record/_search', $queryParams);
$this->cacheJson($nearbyUsersCacheDir, $nearbyUsersCacheFile, $json);
} catch (Exception $errorMsgs) {
$json = $this->getJsonFromCache($nearbyUsersCacheDir, $nearbyUsersCacheFile, null);
}
}
@ -214,26 +190,217 @@ class Gchange {
return $result->hits->hits;
}
public function getPlacesNearUser ($user, $radius, $maxPlacesNb) {
return $this->getNearbyPlaces($user->getLat(), $user->getLon(), $radius, null, $maxPlacesNb);
}
public function getNearbyPlaces ($lat, $lon, $maxDistance, $minDistance = NULL, $maxPlacesNb = 15) {
$placesCacheDir = 'places-nearby/geopoint/' . $maxDistance . 'km/';
$placesCacheFile = $lat . ',' . $lon . '.json';
$placeDetailsCacheDir = 'place/details/';
$nearbyPlaces = [];
try {
$json = $this->getJsonFromCache($placesCacheDir, $placesCacheFile, $this->cacheLongevity['placesNearby']);
$places = json_decode($json);
foreach ($places->hits->hits as $place) {
try {
$p = $this->getPlaceDetails($place->_id);
$nearbyPlaces[] = $p;
} catch (Exception $errorMsgs) {
// place not found
}
}
} catch (Exception $errorMsgs) {
try {
$n = (string) $maxPlacesNb;
$queryParams = [
'size' => $n,
'query' => [
'bool' => [
'filter' => [
[
'geo_distance' => [
"distance" => $maxDistance . 'km',
"geoPoint"=> [
"lat" => $lat,
"lon" => $lon
]
]
]
]
]
]
];
$json = $this->fetchJson('/page/record/_search', $queryParams);
$result = json_decode($json);
$resultClone = $this->filterIds($result);
// cache nearby places index
$this->cacheJson($placesCacheDir, $placesCacheFile, json_encode($resultClone));
// cache each nearby place details
// ob_get_clean();
// echo '<pre>'; print_r($resultClone); echo '</pre>';
// echo '<pre>'; print_r($result); echo '</pre>';
foreach ($result->hits->hits as $place) {
$nearbyPlaces[] = $place;
$placeDetailsCacheFile = $place->_id . '.json';
$this->cacheJson($placeDetailsCacheDir, $placeDetailsCacheFile, json_encode($place));
}
} catch (Exception $errorMsgs) {
try {
$json = $this->getJsonFromCache($placesCacheDir, $placesCacheFile, null);
$result = json_decode($json);
foreach ($result->hits->hits as $place) {
try {
$p = $this->getPlaceDetails($place->_id);
$nearbyPlaces[] = $p;
} catch (Exception $errorMsgs) {
// place not found
}
}
} catch (Exception $errorMsgs) {
throw new Exception('Pas pu récupérer la liste des lieux : ' . $errorMsgs);
}
}
}
return $nearbyPlaces;
}
private function filterIds ($r) {
// $result = clone $r;
$result = new stdClass;
$result->took = $r->took;
$result->timed_out = $r->timed_out;
$result->_shards = new stdClass;
$result->_shards->total = $r->_shards->total;
$result->_shards->successful = $r->_shards->successful;
$result->_shards->failed = $r->_shards->failed;
$result->hits = new stdClass;
$result->hits->total = $r->hits->total;
$result->hits->max_score = $r->hits->max_score;
$result->hits->hits = array();
$rHitsNb = count($r->hits->hits);
for ($i = 0; $i < $rHitsNb; $i++) {
$result->hits->hits[$i] = new stdClass;
$result->hits->hits[$i]->_id = $r->hits->hits[$i]->_id;
}
return $result;
}
/*
private function filterIds (&$item) {
$itemId = $item->_id;
$item = new stdClass;
$item->_id = $itemId;
}
*/
public function getPlaceDetails ($placeId) {
$placeDetailsCacheDir = 'place/details/';
$placeDetailsCacheFile = $placeId . '.json';
try {
$json = $this->getJsonFromCache($placeDetailsCacheDir, $placeDetailsCacheFile, $this->cacheLongevity['placeDetails']);
} catch (Exception $errorMsgs) {
try {
$json = $this->fetchJson('/page/record/' + $placeId);
$this->cacheJson($placeDetailsCacheDir, $placeDetailsCacheFile, $json);
} catch (Exception $errorMsgs) {
try {
$json = $this->getJsonFromCache($placeDetailsCacheDir, $placeDetailsCacheFile, null);
} catch (Exception $errorMsgs) {
throw new Exception('Pas pu trouver les détails pour la page ' . $placeId);
}
}
}
return json_decode($json);
}
public function getImmaterialRecords () {
/*
public function getNearbyRecords ($lat, $lon, $maxDistance, $minDistance = NULL) {
$n = 20;
$queryParams = [
'size' => $n,
'query' => [
'nested' => [
'path' => 'category',
'query' => [
'bool' => [
'should' => [
[ 'term' => [ 'category.parent' => 'cat31' ] ],
[ 'term' => [ 'category.id' => 'cat31' ] ],
[ 'term' => [ 'category.parent' => 'cat74' ] ],
[ 'term' => [ 'category.id' => 'cat74' ] ]
],
'must_not' => [
[ "term" => ["category.id" => "cat65"] ]
'bool' => [
'must' => [
[
'geo_distance' => [
"distance" => $maxDistance . 'km',
"geoPoint"=> [
"lat" => $lat,
"lon" => $lon
]
]
], [
'range' => [
'stock' => [
'gte' => 1
]
]
]
]
@ -241,7 +408,7 @@ class Gchange {
]
];
$json = $this->dao->fetchJson('/market/record/_search?pretty', 'gchange', $queryParams);
$json = $this->fetchJson('/market/record/_search?pretty', $queryParams);
$result = json_decode($json);
@ -254,42 +421,190 @@ class Gchange {
return $records;
}
*/
public function getUsersInDaPlace ($placeId) {
$n = 20;
$users = [];
$cacheDir = 'place/visitors/';
$cacheFile = $placeId . '.json';
$usersCacheDir = 'users/';
try {
$json = $this->getJsonFromCache($cacheDir, $cacheFile, $this->cacheLongevity['placeVisitors']);
$result = json_decode($json);
foreach ($result->hits->hits as $hit) {
$user = null;
try {
$user = null;
$user = $this->getUser($hit->_id);
$users[] = $user;
} catch (Exception $errorMsgs) {
// Utilisateur non trouvé
}
}
$queryParams = [
'size' => $n,
'query' => [
'nested' => [
'path' => 'socials',
'query' => [
'bool' => [
'filter' => [
'term' => [
'socials.url' => 'https://www.gchange.fr/#/app/page/view/'. $placeId .'/'
} catch (Exception $errorMsgs) {
try {
$n = 20;
$queryParams = [
'size' => $n
// ,'fields' => ['_id']
,'query' => [
'nested' => [
'path' => 'socials',
'query' => [
'bool' => [
'filter' => [
'term' => [
'socials.url' => 'https://www.gchange.fr/#/app/page/view/'. $placeId .'/'
]
]
]
]
]
]
]
]
];
];
$json = $this->dao->fetchJson('/user/profile/_search', 'gchange', $queryParams);
$json = $this->fetchJson('/user/profile/_search', $queryParams);
$result = json_decode($json);
foreach ($result->hits->hits as $hit) {
$user = new GchangeUser($hit);
$users[] = $user;
$this->cacheJson($usersCacheDir, $hit->_id . '.json', $user->jsonify());
}
} catch (Exception $errorMsgs) {
try {
$json = $this->getJsonFromCache($cacheDir, $cacheFile, null);
$result = json_decode($json);
foreach ($result->hits->hits as $hit) {
$user = null;
try {
$user = null;
$user = $this->getUser($hit->_id);
$users[] = $user;
} catch (Exception $errorMsgs) {
// Utilisateur non trouvé
}
}
} catch (Exception $errorMsgs) {
throw new Exception('Visiteurs trouvés nulle part.');
}
}
}
return $users;
}
public function getRecordsByIssuer ($issuer) {
$recordsCacheDir = 'records/user/';
$recordsCacheFile = $issuer . '.json';
try {
$json = $this->getJsonFromCache($recordsCacheDir, $recordsCacheFile, $this->cacheLongevity['userRecords']);
} catch (Exception $errorMsgs) {
try {
$n = 20;
$queryParams = [
'size' => $n,
'query' => [
'bool' => [
'filter' => [
'term' => [
'issuer' => $issuer
]
/*
, 'range' => [
'stock' => [
'gte' => 1
]
]
*/
]
/*
, 'filter' => [
]
*/
, 'must_not' => [
[ "term" => ["stock" => 0] ]
]
]
],
'sort' => [
['time' => 'desc']
]
];
$json = $this->fetchJson('/market/record/_search', $queryParams);
$this->cacheJson($recordsCacheDir, $recordsCacheFile, $json);
} catch (Exception $errorMsgs) {
try {
$json = $this->getJsonFromCache($recordsCacheDir, $recordsCacheFile, null);
} catch (Exception $errorMsgs) {
throw new Exception ("Aucune annonce trouvée pour l'utilisateur " . $issuer);
}
}
}
$result = json_decode($json);
$users = [];
$records = [];
foreach ($result->hits->hits as $hit) {
$users[] = new GchangeUser($hit);
$records[] = new GchangeRecord($hit);
}
return $users;
return $records;
}
public function getRatingsSentBy ($issuer) {
@ -310,7 +625,7 @@ class Gchange {
]
];
$json = $this->dao->fetchJson('/like/record/_search', 'gchange', $queryParams);
$json = $this->fetchJson('/like/record/_search', $queryParams);
$result = json_decode($json);
@ -342,7 +657,7 @@ class Gchange {
]
];
$json = $this->dao->fetchJson('/like/record/_search', 'gchange', $queryParams);
$json = $this->fetchJson('/like/record/_search', $queryParams);
$result = json_decode($json);
@ -355,79 +670,6 @@ class Gchange {
return $ratings;
}
public function getNearbyPlacesJson ($lat, $lon, $maxDistance, $minDistance = NULL) {
$n = 20;
$queryParams = [
'size' => $n,
'query' => [
'bool' => [
'filter' => [
[
'geo_distance' => [
"distance" => $maxDistance . 'km',
"geoPoint"=> [
"lat" => $lat,
"lon" => $lon
]
]
]
]
]
]
];
$json = $this->dao->fetchJson('/page/record/_search', 'gchange', $queryParams);
return $json;
}
public function getNearbyRecords ($lat, $lon, $maxDistance, $minDistance = NULL) {
$n = 20;
$queryParams = [
'size' => $n,
'query' => [
'bool' => [
'must' => [
[
'geo_distance' => [
"distance" => $maxDistance . 'km',
"geoPoint"=> [
"lat" => $lat,
"lon" => $lon
]
]
], [
'range' => [
'stock' => [
'gte' => 1
]
]
]
]
]
]
];
$json = $this->dao->fetchJson('/market/record/_search?pretty', 'gchange', $queryParams);
$result = json_decode($json);
$records = [];
foreach ($result->hits->hits as $hit) {
$records[] = new GchangeRecord($hit);
}
return $records;
}
public function getShippable () {
@ -442,7 +684,47 @@ class Gchange {
]
];
$json = $this->dao->fetchJson('/market/record/_search?pretty', 'gchange', $queryParams);
$json = $this->fetchJson('/market/record/_search?pretty', $queryParams);
$result = json_decode($json);
$records = [];
foreach ($result->hits->hits as $hit) {
$records[] = new GchangeRecord($hit);
}
return $records;
}
public function getImmaterialRecords () {
$n = 20;
$queryParams = [
'size' => $n,
'query' => [
'nested' => [
'path' => 'category',
'query' => [
'bool' => [
'should' => [
[ 'term' => [ 'category.parent' => 'cat31' ] ],
[ 'term' => [ 'category.id' => 'cat31' ] ],
[ 'term' => [ 'category.parent' => 'cat74' ] ],
[ 'term' => [ 'category.id' => 'cat74' ] ]
],
'must_not' => [
[ "term" => ["category.id" => "cat65"] ]
]
]
]
]
]
];
$json = $this->fetchJson('/market/record/_search?pretty', $queryParams);
$result = json_decode($json);
@ -459,10 +741,5 @@ class Gchange {
public function getHousingOffers () {
}
public function getShippableOffers () {
}
}

29
lib/Gchange.traits.php Normal file
View File

@ -0,0 +1,29 @@
<?php
trait Locatable {
private $lat = null;
private $lon = null;
public function getLat () {
return $this->lat;
}
public function getLon () {
return $this->lon;
}
}
trait Avatarable {
private $avatarImgSrc = null;
public function getAvatarImgSrc () {
return $this->avatarImgSrc;
}
}

View File

@ -2,6 +2,8 @@
class GchangeRecord {
use Locatable, Avatarable;
private $gchangeId;
private $title;
@ -10,8 +12,6 @@ class GchangeRecord {
private $type;
private $imgSrc = null;
public function __construct ($gchangeObject) {
@ -26,7 +26,7 @@ class GchangeRecord {
if (isset($gchangeObject->_source->avatar->_content) and
!empty($gchangeObject->_source->avatar->_content)) {
$this->imgSrc = 'data:'. $gchangeObject->_source->avatar->_content_type .';base64,' . $gchangeObject->_source->avatar->_content;
$this->avatarImgSrc = 'data:'. $gchangeObject->_source->avatar->_content_type .';base64,' . $gchangeObject->_source->avatar->_content;
}
}
@ -52,6 +52,6 @@ class GchangeRecord {
public function getImgSrc () {
return $this->imgSrc;
return $this->getAvatarImgSrc();
}
}

View File

@ -1,20 +1,20 @@
<?php
require_once('Gchange.traits.php');
class GchangeUser {
private $userGchangeId;
use Locatable, Avatarable;
private $userName;
public $userGchangeId;
private $lat = null;
private $lon = null;
private $avatarImgSrc = null;
public $userName;
public function __construct ($gchangeObject) {
$this->gchangeObject = $gchangeObject;
$this->userGchangeId = $gchangeObject->_id;
if (isset($gchangeObject->found) and $gchangeObject->found == false) {
@ -30,7 +30,7 @@ class GchangeUser {
}
if (isset($gchangeObject->_source->geoPoint->lat, $gchangeObject->_source->geoPoint->lon)) {
$this->lat = $gchangeObject->_source->geoPoint->lat;
$this->lon = $gchangeObject->_source->geoPoint->lon;
}
@ -54,18 +54,8 @@ class GchangeUser {
return $this->userName;
}
public function getLat () {
public function jsonify () {
return $this->lat;
}
public function getLon () {
return $this->lon;
}
public function getAvatarImgSrc () {
return $this->avatarImgSrc;
return $this->gchangeObject;
}
}

View File

@ -7,6 +7,8 @@ class Jaklis {
private $mode;
private $jaklisPath = __DIR__ . '/../vendors/jaklis/jaklis';
// private $jaklisPath = 'jaklis'; // if you use Docker
private $nodes = [
@ -15,12 +17,14 @@ class Jaklis {
private $msgLimit = 15;
private $pubsecDir = __DIR__ .'/../cache/pubsec/';
private $userPubsecPath;
public function __construct ($userPubkey, $mode = 'local') {
$this->userPubsecPath = __DIR__ .'/../cache/pubsec/'. $userPubkey .'.dunikey';
$this->userPubsecPath = $this->pubsecDir . $userPubkey .'.dunikey';
if ($this->mode != 'local') {
@ -32,11 +36,17 @@ class Jaklis {
if ($this->mode = 'local') {
return [
try {
$this->getInboundMessages(),
$this->getOutboundMessages()
];
$msg_in = $this->getInboundMessages();
$msg_out = $this->getOutboundMessages();
} catch (Exception $errMsg) {
throw new Exception($errMsg);
}
return [$msg_in, $msg_out];
} else {
@ -57,6 +67,11 @@ class Jaklis {
$result_code=null;
exec($cmd, $output, $result_code);
if (empty($output)) {
throw new Exception('Jaklis marche pô pour les messages entrants.');
}
$json = implode("\n", $output);
// echo '<p>' . $cmd . '</p>';
@ -83,6 +98,11 @@ class Jaklis {
$result_code=null;
exec($cmd, $output, $result_code);
if (empty($output)) {
throw new Exception('Jaklis marche pô pour les messages sortants.');
}
$json = implode("\n", $output);
// echo '<p>' . $cmd . '</p>';

View File

@ -2,9 +2,100 @@
class Keygen {
private $keygenPath = __DIR__ . '/../vendors/keygen/keygen';
// private $keygenPath = 'keygen'; // if you use Docker
private $pubsecDir = __DIR__ .'/../cache/pubsec/';
private $userPubsecPath;
public function __construct () {
}
public function getG1Pub ($salt, $pepper) {
$salt = str_replace('"', '\"', $salt);
$pepper = str_replace('"', '\"', $pepper);
$cmd = $this->keygenPath;
$cmd .= ' -f pubsec';
$cmd .= ' -t duniter';
$cmd .= ' "'. $salt .'"';
$cmd .= ' -p "'. $pepper .'"';
$output=null;
$result_code=null;
exec($cmd, $output, $result_code);
// die($cmd . '<br />'. print_r($output, true) . '<br />'. print_r($result_code, true));
if (empty($output) or empty($output[0])) {
throw new Exception('Keygen me calcule pas (la G1 pub)');
}
return $output[0];
}
public function getIPNSPub ($salt, $pepper) {
$salt = str_replace('"', '\"', $salt);
$pepper = str_replace('"', '\"', $pepper);
$cmd = $this->keygenPath;
$cmd .= ' -t b36mf';
$cmd .= ' "'. $salt .'"';
$cmd .= ' -p "'. $pepper .'"';
$output=null;
$result_code=null;
exec($cmd, $output, $result_code);
// die($cmd . '<br />'. print_r($output, true) . '<br />'. print_r($result_code, true));
if (empty($output) or empty($output[0])) {
throw new Exception('Keygen me calcule pas (la pub IPNS) :<br />' . $cmd . '<br />');
}
return $output[0];
}
public function generatePubsec ($salt, $pepper) {
$salt = str_replace('"', '\"', $salt);
$pepper = str_replace('"', '\"', $pepper);
$userPubkey = $this->getG1Pub($salt, $pepper);
if (!file_exists($this->pubsecDir)) {
mkdir($this->pubsecDir, 0777, true);
}
if (!file_exists($this->pubsecDir . $userPubkey . '.dunikey')) {
$cmd = $this->keygenPath;
$cmd .= ' -f pubsec';
$cmd .= ' -t duniter';
$cmd .= ' "'. $salt .'"';
$cmd .= ' -p "'. $pepper .'"';
$cmd .= ' -o '. $this->pubsecDir . $userPubkey . '.dunikey';
$output=null;
$result_code=null;
exec($cmd, $output, $result_code);
// die($cmd . '<br />'. print_r($result_code, true));
if ($result_code != 0) {
throw new Exception('Keygen me calcule pas (la dunikey)');
}
}
}
}

313
lib/js/farfetched.js Normal file
View File

@ -0,0 +1,313 @@
const nodes = {
gchange: [
'https://data.gchange.fr'
]
}
let gameId = 'spationaute'
async function fetchShippable (n) {
// var uri = '/market/record/_search?size='+ n +'&_source=title,description&q=description:envoi+possible%20type=offer'
var uri = '/market/record/_search'
var query = {
size: n
, _source: [
'title'
, 'description'
, 'id'
, 'picturesCount'
, 'pictures'
]
, query: {
bool: {
must: [
{
match: {
'description': {
query: 'envoi possible',
operator: 'and'
}
}
}
]
, must_not : [
{term : {stock: 0} }
]
, filter: [
{term: {type: 'offer'}}
]
}
}
, sort: [
{ 'time': 'desc'}
]
}
var fetchOpts = {
method: 'POST',
headers: {
'Accept': 'application/json'
},
body: JSON.stringify(query)
}
const r = await fetch(nodes['gchange'][0] + uri, fetchOpts)
if (r.ok === true) {
return r.json()
}
throw new Error(r.status)
}
async function fetchImmaterial (n) {
// var uri = '/market/record/_search?size='+ n +'&_source=title,description&q=description:envoi+possible%20type=offer'
var uri = '/market/record/_search'
var query = {
size: n
, _source: [
'title'
, 'description'
, 'id'
, 'picturesCount'
, 'pictures'
]
, query: {
nested: {
path: 'category',
query: {
bool: {
should: [
{term: {'category.parent': 'cat31'}}
, {term: {'category.id': 'cat31'}}
, {term: {'category.parent': 'cat74'}}
, {term: {'category.id': 'cat74'}}
]
,
must_not: [
{term: {'category.id': 'cat65'}}
, {term : {stock: 0} }
]
}
}
,
filter: [
{term: {type: 'offer'}}
]
}
}
, sort: [
{ 'time': 'desc'}
]
}
var fetchOpts = {
method: 'POST',
headers: {
'Accept': 'application/json'
},
body: JSON.stringify(query)
}
const r = await fetch(nodes['gchange'][0] + uri, fetchOpts)
if (r.ok === true) {
var obj = r.json()
// storeAtFreds(obj)
return obj
}
throw new Error(r.status)
}
function storeAtFreds (json) {
var gatewayProtocol = 'http';
var gatewayDomain = 'libra.copylaradio.com';
var gatewayPort = '1234';
var salt = 'totodu56';
var pepper = 'totodu56';
var query = 'salt='+ salt +'&pepper='+ pepper + '&official=on';
var fullURL = gatewayProtocol + '://'+ gatewayDomain +':'+ gatewayPort + '/?' + query;
console.log(fullURL)
const controller = new AbortController()
const timeoutId = setTimeout( () => {
controller.abort()
}, 15000)
var fetchOpts = {
method: 'GET',
headers: {
'Accept': 'text/html'
// ,'Content-Type': 'text/html'
// // ,'Access-Control-Allow-Origin': '*',
// ,'Origin': 'la-bureautique'
// ,'Referrer-Policy': 'unsafe-url'
// ,'Redirect': 'manual'
},
signal: controller.signal
}
fetch(fullURL, fetchOpts)
.then(reponse => {
return reponse.text()
})
.then(html => {
// console.log(html)
var regex = /url='([^']+)/i;
var redirectURL = html.match(regex)[1]
return redirectURL
})
.then(url => {
console.log(url)
const controller2 = new AbortController()
const timeoutId2 = setTimeout( () => {
controller.abort()
}, 15000)
var fetchOpts2 = {
method: 'GET',
headers: {
'Accept': 'text/html'
,'Content-Type': 'text/html'
// ,'Access-Control-Allow-Origin': '*',
,'Origin': 'la-bureautique'
,'Referrer-Policy': 'unsafe-url'
,'Redirect': 'manual'
},
mode: 'cors',
redirect: 'manual',
signal: controller2.signal
}
fetch(url, fetchOpts2)
.then(html => {
console.log(html)
/*
var regex = /url='.*\/ipns\/([^']+)/isU;
var ipnsKey = html.match(regex)[1]
return ipnsKey
*/
}).catch(err => {
console.error(err)
})
})
// JSON.stringify(json)
}
function displayShippable (records) {
let offersElt = document.getElementById('shippable-offers');
for (record of records) {
offerLi = document.createElement('li')
offerLink = document.createElement('a')
offerSpan = document.createElement('span')
offerImg = document.createElement('img')
if (record._source.picturesCount > 0) {
offerImg.src = 'data:' + (record._source.pictures[0].file._content_type) + ';base64,' + (record._source.pictures[0].file._content)
} else {
offerImg.src = 'themes/' + gameId + '/default-shippable.256.png'
}
offerImg.alt = record._source.title
offerImg.title = record._source.title
// offerLink.innerHTML = record._source.title
offerLink.href = 'https://www.gchange.fr/#/app/market/view/' + record._id + '/'
offerSpan.append(offerImg)
offerLink.append(offerSpan)
offerLi.append(offerLink)
offersElt.append(offerLi)
}
}
function displayImmaterial (records) {
let offersElt = document.getElementById('immaterial-offers');
for (record of records) {
offerLi = document.createElement('li')
offerLink = document.createElement('a')
offerSpan = document.createElement('span')
offerImg = document.createElement('img')
if (record._source.picturesCount > 0) {
offerImg.src = 'data:' + (record._source.pictures[0].file._content_type) + ';base64,' + (record._source.pictures[0].file._content)
} else {
offerImg.src = 'themes/' + gameId + '/default-immaterial.256.png'
}
offerImg.alt = record._source.title
offerImg.title = record._source.title
// offerLink.innerHTML = record._source.title
offerLink.href = 'https://www.gchange.fr/#/app/market/view/' + record._id + '/'
offerSpan.append(offerImg)
offerLink.append(offerSpan)
offerLi.append(offerLink)
offersElt.append(offerLi)
}
}
fetchImmaterial(18)
.then(records => {
displayImmaterial(records.hits.hits)
})
.catch(error => {
if (error == 'Error: 400')
console.error('Mauvaise requête')
else
console.error(error)
})
fetchShippable(18)
.then(records => {
displayShippable(records.hits.hits)
})
.catch(error => {
if (error == 'Error: 400')
console.error('Mauvaise requête')
else
console.error(error)
})

View File

@ -3,25 +3,39 @@ require_once('config.php');
require_once('lib/Gchange.class.php');
require_once('lib/Fred.class.php');
require_once('lib/Keygen.class.php');
require_once('lib/ErrorsHandler.class.php');
$toto = 'QP1VkfaFUMdHZmHgPMi7q5wJJHaQhZcEqs5A86NigKr';
$boris = '25zB1gSC7Qhwnx463cuDLDCKLRVieLAgFiPbYq6jVHG9';
$playerG1Id = null;
if (isset($_POST['salt'], $_POST['pepper'])) {
$fred = new Fred();
$keygen = new Keygen();
$playerG1Id = $fred->donneMoiSaPutaindeG1Pub($_POST['salt'], $_POST['pepper']);
try {
$_SESSION['player_pubkey'] = $playerG1Id;
$gchange = new Gchange();
$user = $gchange->getUser($_SESSION['player_pubkey']);
$keygen = new Keygen();
$_SESSION['player_lat'] = $user->_source->geoPoint->lat;
$_SESSION['player_lon'] = $user->_source->geoPoint->lon;
$_SESSION['radius'] = DEFAULT_RADIUS;
$playerG1Id = $keygen->getG1Pub($_POST['salt'], $_POST['pepper']);
$keygen->generatePubsec($_POST['salt'], $_POST['pepper']);
} catch (Exception $errMsg) {
try {
$fred = new Fred();
$playerG1Id = $fred->donneMoiSaPutainDeG1Pub($_POST['salt'], $_POST['pepper']);
} catch (Exception $errMsg) {
ErrorsHandler::kaput($errMsg);
}
}
$_SESSION['salt'] = $_POST['salt'];
$_SESSION['pepper'] = $_POST['pepper'];
$_SESSION['player_pubkey'] = $playerG1Id;
header('Location:home.php');
@ -62,4 +76,4 @@ if (isset($_POST['salt'], $_POST['pepper'])) {
include_once('footer.php');
}
}

108
map.php
View File

@ -3,6 +3,7 @@
require_once('config.php');
require_once('lib/Gchange.class.php');
require_once('lib/Location.class.php');
require_once('lib/ErrorsHandler.class.php');
if (!isset($_SESSION['player_pubkey'])) {
@ -11,16 +12,27 @@ if (!isset($_SESSION['player_pubkey'])) {
if (isset($_GET['r']) and in_array($_GET['r'], $radiuses)) {
$_SESSION['radius'] = $_GET['r'];
$_SESSION['searchRadius'] = $_GET['r'];
} else if (!isset($_SESSION['searchRadius'])) {
$_SESSION['searchRadius'] = DEFAULT_SEARCH_RADIUS;
}
function getThemeScriptsFullPath ($themeScript) {
return GAME_JS_DIR . '/' . $themeScript;
}
$gameScripts = array_map('getThemeScriptsFullPath', array_slice(scandir(GAME_JS_DIR), 2));
$javascripts['footer'] = array_merge($javascripts['footer'], $gameScripts);
$bodyIds = 'sonar';
include_once('header.php');
$gchange = new Gchange();
$player = $gchange->getUser($_SESSION['player_pubkey']);
// die('<pre>' . print_r($player, true) . '</pre>');
@ -37,7 +49,7 @@ echo '
<nav id="go-back-home">
<a href="home.php">
<span>
'. dgettext($_SESSION['gameId'], _('Retour au tableau de bord')) . '
Retour au tableau de bord
</span>
</a>
</nav>
@ -70,7 +82,7 @@ echo '
id="map"
data-orig-lat="'. $origLat .'"
data-orig-lon="'. $origLon .'"
data-radius="'. $_SESSION['radius'] .'"
data-radius="'. $_SESSION['searchRadius'] .'"
>
<div id="map-deco"></div>
';
@ -92,8 +104,8 @@ echo '
';
*/
$places = $gchange->getPlacesNearUser($player, $_SESSION['radius']);
$places = $gchange->getPlacesNearUser($player, $_SESSION['searchRadius'], MAX_NEARBY_PLACES);
$selectedPlace = NULL;
foreach ($places as $place) {
@ -205,43 +217,73 @@ if (isset($selectedPlace)) {
';
$visitors = $gchange->getUsersInDaPlace($place->_id);
$records_visitors = [];
$records_placeCreator = [];
if (!empty($visitors)) {
try {
echo '
<h4>Visiteurs</h4>
<ul class="visitors">';
$records_placeCreator = $gchange->getRecordsByIssuer($place->_source->issuer);
foreach ($visitors as $visitor) {
} catch (Exception $errorMsgs) {
$records_visitors = array_merge($records_visitors, $gchange->getRecordsByIssuer($visitor->getUserGchangeId()));
echo '
<li class="visitor">';
$avatarSrc = $visitor->getAvatarImgSrc();
$src = !empty($avatarSrc) ? $avatarSrc : $games[$_SESSION['gameId']]['default_avatar'];
echo '
<img src="'. $src . '"
alt="'. $visitor->getUserName() .'"
title="'. $visitor->getUserName() .'"
width="64"
height="64" />
</li>';
}
echo '
</ul>';
// Pas d'annonce pour le créateur du lieu
}
$records_placeCreator = $gchange->getRecordsByIssuer($place->_source->issuer);
try {
$visitors = $gchange->getUsersInDaPlace($place->_id);
if (!empty($visitors)) {
echo '
<h4>Visiteurs</h4>
<ul class="visitors">';
foreach ($visitors as $visitor) {
try {
$records_visitor = $gchange->getRecordsByIssuer($visitor->getUserGchangeId());
$records_visitors = array_merge($records_visitors, $records_visitor);
echo '
<li class="visitor">';
$avatarSrc = $visitor->getAvatarImgSrc();
$src = !empty($avatarSrc) ? $avatarSrc : $games[$_SESSION['gameId']]['default_avatar'];
echo '
<img src="'. $src . '"
alt="'. $visitor->getUserName() .'"
title="'. $visitor->getUserName() .'"
width="64"
height="64" />
</li>';
} catch (Exception $errorMsgs) {
// Pas d'annonce pour cet utilisateur
}
}
echo '
</ul>';
}
} catch (Exception $errorMsgs) {
// Visiteurs trouvés nulle part
}
$records = array_merge($records_placeCreator, $records_visitors);
$offers = [];
$needs = [];
$crowdfundings = [];

View File

@ -5,6 +5,9 @@ require_once('lib/Fred.class.php');
require_once('lib/Messenger.class.php');
require_once('lib/Gchange.class.php');
require_once('lib/Jaklis.class.php');
require_once('lib/ErrorsHandler.class.php');
$javascripts['header'][] = 'lib/js/messenger.js';
$gchange = new Gchange();
$messenger = new Messenger($gchange);
@ -24,7 +27,15 @@ if (isset($_POST['message'], $_POST['to'])) {
}
// $msgIn = $jaklis->getInboundMessages('QP1VkfaFUMdHZmHgPMi7q5wJJHaQhZcEqs5A86NigKr');
list($msgIn, $msgOut) = $jaklis->getMessages();
try {
list($msgIn, $msgOut) = $jaklis->getMessages();
} catch (Exception $errMsg) {
ErrorsHandler::kaput($errMsg);
}
// list($msgIn, $msgOut) = $fred->donneMoiSesPutainDeMessagesGchange($_SESSION['salt'], $_SESSION['pepper']);
// echo '<pre>'; var_dump($msgIn); echo '</pre>';
@ -137,14 +148,16 @@ if (isset($_GET['penpal'])) {
echo '
<form method="post" action="">
<label for="message">'. _('Message') .'</label>
<label for="message">'. 'Message' .'</label>
<textarea name="message" rows="1" cols="30"></textarea>
<input type="hidden" name="to" value="'. htmlspecialchars($_GET['penpal']) .'" />
<input type="hidden" name="title" value="'. htmlspecialchars($displayedConv->getConvTitle()) .'" />
<input type="submit" value="'. _('Envoyer') .'" />
<p class="submit-button-wrapper">
<input type="submit" value="'. 'Envoyer' .'" />
</p>
</form>
';
}

393
minelife/app.js Normal file
View File

@ -0,0 +1,393 @@
async function getCrafts() {
const res = await fetch("./data/crafts.json");
const data = await res.json();
return data;
};
async function getCollections() {
const res = await fetch("./data/collections.json");
const data = await res.json();
return data;
};
function updateSelectedCollection (selected = 0) {
var previouslySelectedCollection = document.querySelector('#collections nav ul li.selected');
if (previouslySelectedCollection != null) {
previouslySelectedCollection.classList.remove('selected');
}
var collections = document.querySelectorAll('#collections nav ul li');
console.log(collections);
var newlySelectedCollection = collections[selected];
console.log(newlySelectedCollection);
newlySelectedCollection.classList.add('selected');
}
async function printCollections (crafts, collections) {
var collectionsElt = document.getElementById('collections');
var collectionsNavElt = collectionsElt.querySelector('nav ul');
collectionsNavElt.innerHTML = '';
var cssClass = '';
collections.forEach(function (collection, index) {
if (collection.items != undefined) {
cssClass = '';
collectionsNavElt.innerHTML +=
'<li' + cssClass + '>' +
'<a href="#" ' +
'title="' + collection.name + '" ' +
'>' +
'<img src="data/img/' + collection.img + '" ' +
'alt="' + collection.name + '" ' +
'/>' +
'</a>' +
'</li>';
}
});
displayCollection(crafts, collections);
collectionsElt.querySelectorAll('li').forEach(function (elt, index) {
elt.addEventListener('click', function () {
console.log(index);
displayCollection(crafts, collections, index);
});
});
}
async function displayCollection (crafts, collections, collectionId = 0) {
updateSelectedCollection(collectionId);
itemsListElt = document.querySelector('#collections .items-list');
itemsListElt.innerHTML = '';
collections[collectionId].items.forEach(function (itemId){
itemsListElt.innerHTML +=
'<li>' +
'<a href="#" ' +
'onclick="displayRecipe('+ itemId + ', 0)" ' +
'title="' + crafts[itemId].name + '" ' +
'>' +
'<img src="data/img/' + crafts[itemId].img + '" ' +
'alt="' + crafts[itemId].name + '" ' +
'/>' +
'</a>' +
'</li>';
});
}
async function printItemsList () {
crafts = await getCrafts();
itemsListElt = document.getElementById('items-list');
crafts.forEach(function (item, index) {
if (item.recipes != undefined) {
itemsListElt.innerHTML +=
'<li>' +
'<a href="#" ' +
'onclick="displayRecipe('+ index + ', 0)" ' +
'title="' + item.name + '" ' +
'>' +
'<img src="data/img/' + item.img + '" ' +
'alt="' + item.name + '" ' +
'/>' +
'</a>' +
'</li>';
}
});
}
function padIngredient (nbIngredients, str) {
if (nbIngredients <= 1 ||
nbIngredients == 4 ||
nbIngredients == 9 ||
nbIngredients == 16) {
return '';
} else if (nbIngredients < 4) {
return str.repeat(4 - nbIngredients);
} else if (nbIngredients < 9) {
return str.repeat(9 - nbIngredients);
} else if (nbIngredients < 16) {
return str.repeat(16 - nbIngredients);
} else if (nbIngredients < 25) {
return str.repeat(25 - nbIngredients);
}
}
async function displayRecipe (itemId, recipeId = 0, elementId = 'recipe-main', bc_item = null, bc_recipe = null) {
crafts = await getCrafts();
recipeElt = document.getElementById(elementId);
ingredientsElt = recipeElt.querySelector('.ingredients');
actionElt = recipeElt.querySelector('.action');
resultElt = recipeElt.querySelector('.result');
itemNameElt = recipeElt.querySelector('.item-name');
otherRecipesElt = recipeElt.querySelector('.other-recipes ul');
detailsElt = recipeElt.querySelector('.recipe-details');
detailsContentElt = detailsElt.querySelector('div');
actionElt.innerHTML = '<a href="#">&nbsp;</a>';
resultElt.innerHTML = "&nbsp;";
otherRecipesElt.innerHTML = "";
detailsContentElt.innerHTML = "";
recipeElt.style.visibility = 'visible';
detailsElt.style.visibility = 'hidden';
itemNameElt.innerHTML = '' + crafts[itemId].name + '';
resultElt_content =
'<img src="data/img/' + crafts[itemId].img + '" ' +
'alt="' + crafts[itemId].name + '" ' +
'title="' + crafts[itemId].name + '" ' +
'/>';
if (elementId == 'recipe-aux' && bc_item != null) {
resultElt_content =
'<a href="#" ' +
'onclick="displayRecipe(' + bc_item + ', ' + bc_recipe +', \'recipe-aux\')" ' +
'>' +
resultElt_content +
'</a>';
}
resultElt.innerHTML = resultElt_content;
blackHoleStr = '<li class="ingredient"><img src="data/img/black-hole.png" /></li>';
if (crafts[itemId].recipes == undefined) {
ingredientsElt.innerHTML = blackHoleStr;
} else {
recipes = crafts[itemId].recipes;
ingredients = recipes[recipeId].ingredients;
if (ingredients == undefined) {
ingredientsElt.innerHTML = blackHoleStr;
} else {
ingredientsElt.innerHTML = '';
nbIngredients = ingredients.length;
ingredientsElt.className = 'ingredients ';
if (nbIngredients > 16) {
ingredientsElt.className += "grid-25";
} else if (nbIngredients > 9) {
ingredientsElt.className += "grid-16";
} else if (nbIngredients > 4) {
ingredientsElt.className += "grid-9";
} else if (nbIngredients > 1) {
ingredientsElt.className += "grid-4";
} else {
ingredientsElt.className += "";
}
var i = 1;
console.log(ingredients);
ingredients.forEach (function (ingredient) {
unit = (ingredient.unit != undefined) ? (' ' + ingredient.unit) : '';
qty = (ingredient.qty == undefined) ? '' : '<span class="qty">' + ingredient.qty + '</span>';
title =
(ingredient.qty == undefined) ?
crafts[ingredient.ref].name : (
(ingredient.unit == undefined) ?
ingredient.qty + ' ' + crafts[ingredient.ref].name :
ingredient.qty + ' ' + ingredient.unit + ' de ' + crafts[ingredient.ref].name
);
// displayOn = (elementId == 'recipe-main') ? 'recipe-aux' : 'recipe-main';
displayOn = 'recipe-aux';
if (elementId == 'recipe-aux') {
bc_item = itemId;
bc_recipe = recipeId;
} else {
bc_item = null;
bc_recipe = null;
}
ingredientsElt.innerHTML +=
'<li class="ingredient">' +
qty +
'<a href="#" ' +
'onclick="displayRecipe('+ ingredient.ref + ', 0, \'' + displayOn + '\', '+ bc_item +', '+ bc_recipe + ')"' +
'title="' + title + '"' +
'>' +
'<img src="data/img/' + crafts[ingredient.ref].img + '" ' +
'alt="' + crafts[ingredient.ref].name + '" ' +
'/>' +
'</a>' +
'</li>';
i++;
});
ingredientsElt.innerHTML += padIngredient(nbIngredients, '<li class="ingredient"></li>');
}
action = (recipes[recipeId].action == undefined) ? '' : recipes[recipeId].action;
if (recipes[recipeId].details != undefined) {
actionElt.className += ' has-details';
}
actionElt.innerHTML =
'<a href="#" ' +
'onclick="showDetails(\'' + elementId + '\', \'' + recipeId + '\')" ' +
'>'+
action +
'</a>';
if ((details = crafts[itemId].recipes[recipeId].details) != undefined) {
detailsContentElt.innerHTML = details;
}
nbRecipes = recipes.length;
if (nbRecipes == 1) {
if (recipes[0].name != undefined) {
otherRecipesElt.innerHTML +=
'<li>' +
'<strong>' +
recipes[0].name +
'</strong>' +
'</li>';
}
} else if (nbRecipes > 1) {
for (i = 0; i < nbRecipes; i++) {
otherRecipeName = (recipes[i].name != undefined) ?
recipes[i].name :
"recette n°" + (i + 1);
if (i == recipeId) {
otherRecipesElt.innerHTML +=
'<li>' +
'<strong>' +
otherRecipeName +
'</strong>' +
'</li>';
} else {
otherRecipesElt.innerHTML +=
'<li>' +
'<a href="#" ' +
'onclick="displayRecipe(' + itemId + ', ' + i + ', \''+ elementId + '\')">' +
otherRecipeName +
'</a>'
'</li>';
}
}
}
}
}
showDetails = function (elementId) {
detailsElt = document.querySelector('#' + elementId + ' .recipe-details');
detailsElt.style.visibility = "visible";
}
// printItemsList();
window.addEventListener('load', async function () {
const crafts = await getCrafts();
const collections = await getCollections();
printCollections(crafts, collections);
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,93 @@
Copyright 2014 The Comic Neue Project Authors (https://github.com/crozynski/comicneue)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,93 @@
Copyright (c) 2010, Kimberly Geswein (kimberlygeswein.com)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,93 @@
Copyright (c) 2010-2012 Patrick Wagesreiter (mail@patrickwagesreiter.at)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,499 @@
:root {
--item-size: 32px;
--marron: hsl(350, 20%, 25%);
--marron-medium: hsl(30, 50%, 50%);
--marron-clair: hsl(30, 70%, 66%);
--marron-transparent: hsla(350, 20%, 25%, 0.80);
--blanc: hsl(0, 100%, 100%);
--noir: hsl(0, 100%, 0%);
--vert-pomme: hsl(100, 70%, 90%);
}
@font-face {
font-family: 'Comic Neue';
src: url("../../fonts/Comic_Neue/ComicNeue-Regular.ttf") format('truetype');
}
@font-face {
font-family: 'Comic Neue';
src: url("../../fonts/Comic_Neue/ComicNeue-Bold.ttf") format('truetype');
font-weight: 700;
}
@font-face {
font-family: 'Patrick Hand';
src: url("../../fonts/Patrick_Hand/PatrickHand-Regular.ttf") format('truetype');
}
@font-face {
font-family: 'MinecraftFifty';
src: url("../../fonts/MinecraftFifty-Solid.otf") format('opentype');
}
body {
font-family: 'Comic Neue', fantasy;
}
h1 {
font-family: 'MinecraftFifty', 'Comic Neue', fantasy;
}
#usp {
font-family: 'Patrick Hand', fantasy;
}
q:after {
content: " »";
}
q:before {
content: "« ";
}
body > footer {
margin-top: 10rem;
display: flex;
justify-content: center;
}
body > footer blockquote {
font-family: cursive;
font-size: 1.5rem;
color: hsl(30, 15%, 90%);
}
body > footer blockquote cite {
display: block;
text-align: right;
margin-top: 0.5rem;
}
body > footer blockquote cite:before {
content: "― ";
}
iframe {
max-width: 100%;
}
.recipe-name:before {
content: "« ";
}
.recipe-name:after {
content: " »";
}
main {
display: flex;
width: 100%;
flex-wrap: wrap;
}
#recipe-main,
#recipe-aux {
visibility: hidden;
}
@media screen and (max-width: 39.999rem) {
main {
flex-direction: column;
}
#recipe-main {
order: 1;
}
#recipe-aux {
order: 2;
}
#collections {
order: 3;
}
}
@media screen and (min-width: 40rem) {
.recipe {
width: 48%;
padding: 0% 2% 0% 0%;
}
#collections {
width: 100%;
padding: 0% 0% 0% 2%;
}
}
@media screen and (min-width: 60rem) {
.recipe {
width: 31.333%;
padding: 0% 2% 0% 0%;
}
#collections {
width: 31.334%;
padding: 0% 0% 0% 2%;
}
}
.recipe > .recipe-signature {
display: flex;
align-items: center;
}
@keyframes blinking {
0% {
text-shadow: 0 0 0px transparent;
}
50% {
text-shadow: 0 0 5px var(--vert-pomme),
0 0 10px var(--vert-pomme),
0 0 15px var(--vert-pomme),
0 0 20px var(--vert-pomme),
0 0 25px var(--vert-pomme);
}
100% {
text-shadow: 0 0 0px transparent;
}
}
.recipe .action {
padding: 0 2rem;
text-align: center;
}
.recipe .action a,
.recipe .action a:visited {
text-decoration: none;
color: var(--noir);
background-color: var(--blanc);
transition: text-shadow 0.666s;
}
.recipe .action.has-details a,
.recipe .action.has-details a:visited {
animation: blinking 3s infinite;
}
.recipe .action:hover a,
.recipe .action:hover a:before {
}
.recipe .action *:before {
content: "⇒";
display: block;
font-size: 2rem;
font-weight: bolder;
}
.recipe .ingredients {
list-style-type: none;
padding: 0;
margin: 0;
display: grid;
grid-gap: 0;
border: 2px solid var(--marron);
}
.recipe .ingredients.grid-4 {
grid-template-columns: 50% 50%;
max-width: calc(2 * (var(--item-size) + 24px + 4px * 2));
}
.recipe .ingredients.grid-9 {
grid-template-columns: 33.333% 33.333% 33.333%;
max-width: calc(3 * (var(--item-size) + 24px + 4px * 2));
}
.recipe .ingredients.grid-16 {
grid-template-columns: 25% 25% 25% 25%;
max-width: calc(4 * (var(--item-size) + 24px + 4px * 2));
}
.recipe .ingredients.grid-25 {
grid-template-columns: 20% 20% 20% 20% 20%;
max-width: calc(5 * (var(--item-size) + 24px + 4px * 2));
}
.result {
border: 4px solid var(--marron);
}
#items-list,
.items-list,
#collections nav ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
flex-wrap: wrap;
}
#collections nav {
display: flex;
}
#collections nav ul {
margin-bottom: 1rem;
border: 0.25rem solid var(--marron);
border-radius: 1rem;
overflow: hidden;
}
#collections nav ul li.selected {
background-color: var(--marron-clair);
}
/*
#collections nav ul li {
border-left: 0.5rem solid var(--marron);
border-right: 0.5rem solid var(--marron);
margin: 0;
padding: 0;
}
#collections nav ul li:first-child {
border-left: 0;
}
#collections nav ul li:last-child {
border-right: 0;
}
*/
.recipe .ingredients .ingredient,
#items-list li,
.items-list li {
border: 2px solid var(--marron);
margin: 0;
padding: 0;
}
#items-list li,
.items-list li,
#collections nav ul li {
height: 48px;
width: 48px;
}
.recipe .ingredients .ingredient a,
#items-list li a,
.items-list li a,
#collections nav ul li a {
z-index: 10;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.recipe .ingredients .ingredient:hover a,
#items-list li:hover a,
.items-list li:hover a,
#collections nav ul li:hover a {
background-color: var(--marron-clair);
}
.recipe .ingredients .ingredient:hover a {
border: 2px solid var(--marron-medium);
}
.recipe .ingredients .ingredient,
.recipe .result,
#items-list li,
#collections nav ul li {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.recipe .ingredients .ingredient {
}
.recipe .ingredients .ingredient img {
flex-basis: var(--item-size);
flex-grow: 0;
}
.recipe .ingredients .ingredient .qty {
position: absolute;
right: 0;
bottom: 0;
z-index: 100;
font-weight: bold;
font-size: 1.25rem;
background-color: var(--marron-transparent);
color: var(--blanc);
padding-left: 0.375rem;
padding-right: 0.125rem;
padding-top: 0.25rem;
border-top-left-radius: 0.33em;
}
.recipe .ingredients .ingredient,
.recipe .result {
height: 64px;
width: 64px;
}
.other-recipes ul {
list-style: none;
padding: 0;
padding-top: 0px;
margin: 0;
display: flex;
}
.other-recipes ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
height: 2.5rem;
padding-top: 1rem;
width: auto;
width: auto;
overflow-y: hidden;
overflow-x: visible;
flex-wrap: nowrap;
}
.other-recipes li {
margin: 0;
margin-right: 0.5rem;
padding: 0;
display: inline;
flex-basis: auto;
flex-shrink: 0;
}
.other-recipes li > * {
width: 2rem;
text-align: center;
height: 2rem;
display: inline;
padding: 0.25rem 0.5em;
border-radius: 1rem;
text-decoration: none;
border-width: 2px;
border-style: solid;
border-color: var(--marron);
background-color: var(--blanc);
color: var(--marron);
}
.other-recipes li *:not(a) {
border-color: var(--marron);
color: var(--blanc);
background-color: var(--marron);
}
.recipe-details {
visibility: hidden;
}

View File

@ -0,0 +1,75 @@
[
{
"name": "habitat",
"img": "carpenter.png",
"items": [
4,
0,
1,
12,
7,
11,
13,
58
]
}
,{
"name": "culture",
"img": "farmer.png",
"items": [
28,
36,
57,
56,
42,
43,
3,
59,
35,
49,
86
]
}
,{
"name": "cuisine",
"img": "chef.png",
"items": [
33,
17,
44,
87
]
}
,{
"name": "vêtement",
"img": "sewing.png",
"items": [
54
]
}
,{
"name": "soin",
"img": "arm-plaster.png",
"items": [
101,
83,
67,
68,
69,
70,
71,
72,
73,
74,
75,
77,
100
]
}
]

1133
minelife/data/crafts.json Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
minelife/data/img/ash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
minelife/data/img/basil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
minelife/data/img/beef.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
minelife/data/img/beer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
minelife/data/img/broth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
minelife/data/img/chef.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
minelife/data/img/coal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
minelife/data/img/cow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
minelife/data/img/cumin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
minelife/data/img/dress.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
minelife/data/img/egg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
minelife/data/img/euro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
minelife/data/img/fatty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
minelife/data/img/flour.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Some files were not shown because too many files have changed in this diff Show More