Added all temporary working files

templating-wip
Eirik Th S 2022-08-05 17:46:24 +02:00
parent 8dc5d08b0c
commit e4f224cde7
36 changed files with 1224 additions and 58 deletions

View File

@ -2,7 +2,8 @@
class Auth {
private static $instance = null;
private $isLoggedIn = false;
private $isLoggedIn = false;
private $userid = false;
private function __construct(){
session_start();
@ -102,10 +103,10 @@ class Auth {
($row['ctime']+($row['expire']*24*60*60) > time())
){
session_regenerate_id();
$_SESSION['user_id'] = $row['user_id'];
$_SESSION['user_name'] = $row['full_name'];
$_SESSION['user_id'] = $row['user_id'];
$_SESSION['user_name'] = $row['full_name'];
$_SESSION['user_agent'] = $md5agent;
$_SESSION['user_key'] = sha1($row['ckey']);
$_SESSION['user_key'] = sha1($row['ckey']);
return true;
}
@ -224,6 +225,10 @@ class Auth {
}
return false;
}
public static function currentUserId(){
return $_SESSION['user_id'];
}
}
function PwdGen($pass, $returnHashed = false): string {

Binary file not shown.

View File

@ -0,0 +1,32 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"X-Domain: domain1\n"
msgid "comment"
msgstr "kommentar"
msgid "Login"
msgstr "Logg inn"
msgid "Log out"
msgstr "Logg ut"
msgid "Plan shopping"
msgstr "Planlegg handelen"
msgid "Review"
msgstr "Kontroller"
msgid "Recent changes"
msgstr "Nylige endringer"
msgctxt "verb"
msgid "Plan"
msgstr "Planlegg"
msgid "Coming soon"
msgstr "Kommer snart"
msgid "Home"
msgstr "Hjem"

15
models/Nutrient.php Normal file
View File

@ -0,0 +1,15 @@
<?php
//namespace models;
class Nutrient extends Model {
// protected static $database = "i18n";
protected static $table = "nutrient";
protected static $fields = [
"nutrient_id" => "INT",
"name" => "VARCHAR",
"parent" => "TEXT"
];
}

20
models/PlanSpace.php Normal file
View File

@ -0,0 +1,20 @@
<?php
//namespace models;
class PlanSpace extends Model {
protected static $table = "plan_space";
protected static $fields = [
"space_id" => "INT",
"space_name" => "VARCHAR",
"owner_id" => "VARCHAR",
"space_type" => [ 'STORE', 'CHECK', 'CALORIES' ]
];
public static function getUserSpaces(){
return array_merge(
static::get([ 'owner_id' => Auth::currentUserId() ]),
PlanSpaceMember::get([ 'member_id' => Auth::currentUserId() ])
);
}
}

View File

@ -0,0 +1,13 @@
<?php
//namespace models;
class PlanSpaceMember extends PlanSpace {
protected static $table = "plan_space_member";
protected static $fields = [
"space_id" => "INT",
"member_id" => "VARCHAR",
"timestamp" => "VARCHAR"
];
}

15
models/Product.php Normal file
View File

@ -0,0 +1,15 @@
<?php
namespace models;
class Product extends Model {
// protected static $database = "i18n";
protected static $table = "product";
protected static $fields = [
"product_id" => "INT",
"product_group_id" => "INT",
"name" => "VARCHAR"
];
}

View File

@ -0,0 +1,16 @@
{% extends "assets/base.html.twig" %}
{% block content %}
<button class="btn btn-secondary" id="lookForTranslations">Look for translations</button>
<div id="output"></div>
{% endblock %}
{% block endContent %}
<script>
$("document").ready(()=>{
$("#lookForTranslations").on('click', ev => {
$("#output").load('?translations');
});
});
</script>
{% endblock %}

View File

@ -77,7 +77,7 @@
<footer>
{{ 'Any prices you find on this website is added by its users and are only a guideline.'|t }}<br>
<strong>PaperBag</strong> is a <a href="//svagard.no/projects/paperbag" target="_blank" rel="nofollow">Svagård</a> project<br>
{% if debug %}<strong>DEBUG</strong><br>{% endif %}
{#
<!--<a href="https://useg.it/eirik/GroceryAssist" target="_blank" rel="nofollow">View source</a> |
<a href="/cookies" rel="nofollow">Cookies</a> |

View File

@ -14,7 +14,9 @@
<div class="input-group" style="width: 200px;">
<label class="input-group-text" for="spaceSelect">Space:</label>
<select class="form-select" id='spaceSelect' aria-label="Select space">
<option>...</option>
{% for space in spaces %}
<option>...</option>
{% endfor %}
</select>
</div>
</div>

View File

@ -3,13 +3,18 @@ ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
CONST PATH_RECIPE = "/recipe";
$docs['general'][] = array("method"=>'GET', "href"=>"/", "name"=>"Nothing", "body"=>"{}");
$docs['recipe'][] = array("method"=>'GET', "href"=>"/recipe", "name"=>"Get all available recipes", "body"=>"{}");
$docs['recipe'][] = array("method"=>'POST', "href"=>"/recipe", "name"=>"Create recipe", "body"=>"{\n \"name\": \"string\",\n \"portions\": \"integer\",\n \"public\": \"boolean\" \n}");
$docs['recipe'][] = array("method"=>'POST', "href"=>"/recipe", "name"=>"Add item to recipe", "body"=>"{\n \"recipe_id\": \"integer\",\n \"name\": \"string\",\n \"price\": \"number\",\n \"amount\": \"integer\" \n}");
$docs['recipe'][] = array("method"=>'POST', "href"=>"/recipe", "name"=>"Edit recipe item", "body"=>"{\n \"recipe_id\": \"integer\",\n \"recipe_item_id\": \"integer\",\n \"newName\": \"string\",\n \"newPrice\": \"number\",\n \"newAmount\": \"integer\",\n \"newItem_id\": \"integer\" \n}");
$docs['recipe'][] = array("method"=>'POST', "href"=>"/recipe", "name"=>"Delete recipe item", "body"=>"{\n \"recipe_id\": \"integer\",\n \"del_item_id\": \"integer\",\n \"delName\": \"string\" \n}");
$docs['product'][] = array("method"=>'GET', "href"=>"/product/{search}", "name"=>"Search in the products", "body"=>"Ost");
$docs['product'][] = array("method"=>'POST', "href"=>"/product", "name"=>"Add a product", "body"=>"{\n \"name\": \"string\",\n \"groupId\": \"integer\"\n}", "payload"=>[ "name"=>"text", "groupId"=>"number" ]);
$docs['recipe'][] = array("method"=>'GET', "href"=>PATH_RECIPE, "name"=>"Get all available recipes", "body"=>"{}");
$docs['recipe'][] = array("method"=>'POST', "href"=>PATH_RECIPE, "name"=>"Create recipe", "body"=>"{\n \"name\": \"string\",\n \"portions\": \"integer\",\n \"public\": \"boolean\" \n}");
$docs['recipe'][] = array("method"=>'POST', "href"=>PATH_RECIPE, "name"=>"Add item to recipe", "body"=>"{\n \"recipe_id\": \"integer\",\n \"name\": \"string\",\n \"price\": \"number\",\n \"amount\": \"integer\" \n}");
$docs['recipe'][] = array("method"=>'POST', "href"=>PATH_RECIPE, "name"=>"Edit recipe item", "body"=>"{\n \"recipe_id\": \"integer\",\n \"recipe_item_id\": \"integer\",\n \"newName\": \"string\",\n \"newPrice\": \"number\",\n \"newAmount\": \"integer\",\n \"newItem_id\": \"integer\" \n}");
$docs['recipe'][] = array("method"=>'POST', "href"=>PATH_RECIPE, "name"=>"Delete recipe item", "body"=>"{\n \"recipe_id\": \"integer\",\n \"del_item_id\": \"integer\",\n \"delName\": \"string\" \n}");
@ -76,9 +81,19 @@ function capitalizeFirst($input){
<form class="apiExec">
<div class="row row-cols-2">
<div>
<input class="form-control" type="hidden" id="url" name="url" value="<?=$row['href'];?>">
<textarea class="form-control" class="apiBody" name="apiBody"><?=$row['body'];?></textarea>
<input type="submit">
<input type="hidden" id="methodType" name="_methodType" value="<?=$row['method'];?>">
<input type="hidden" id="url" name="_url" value="<?=$row['href'];?>">
<?php
if(isset($row['payload'])){
foreach ($row['payload'] as $key => $type){
echo "<input type='$type' name='$key' placeholder='$key' class='form-control mb-2 payloadInput'>";
}
}
else {
echo "<textarea class='form-control' class='apiBody mb-2' name='_apiBody'>".$row['body']."</textarea>";
}
?>
<button type="submit" class="btn btn-primary mb-2">Submit</button>
</div>
<div class="apiExecResponse"></div>
</div>
@ -98,17 +113,36 @@ function capitalizeFirst($input){
let formElem = $(ev.currentTarget);
let params = formElem.serializeArray();
formElem.find(".apiExecResponse").html("...")
formElem.find(".apiExecResponse").html("...");
let url = "."+params[0].value;
let body = JSON.parse( params[1].value );
let methodType = params[0].value;
let url = "."+params[1].value;
let body = params[2].value;
if(url.length > 0 && typeof body === "object"){
let newParams = params.filter(o=>o.name.substr(0,1) !== "_");
$.post(url, body, resp => {
console.log(resp);
let response = JSON.stringify(resp, null, 4);
formElem.find(".apiExecResponse").html("<pre><code>"+response+"</code></pre>");
console.log(params[1].value);
if(url.length > 0){
if(methodType === "GET" && url.indexOf('{') >= 0){
url = url.split('{')[0];
url += body;
}
else {
// body = JSON.parse( params[2].value );
body = newParams;
}
$.ajax({
type: methodType,
url: url,
data: body,
success: resp => {
console.log(resp);
let response = JSON.stringify(resp, null, 4);
formElem.find(".apiExecResponse").html("<pre><code>" + response + "</code></pre>");
}
});
}
});
@ -117,11 +151,18 @@ function capitalizeFirst($input){
let currElem = $(ev.currentTarget);
let textarea = currElem.find('textarea');
textarea.val( currElem.find('.apiBodyHolder').html() );
currElem.find('.apiExecResponse').html('');
setTimeout(()=> {
textarea.height( (textarea[0].scrollHeight)-12 );
}, 10);
if(textarea.length > 0){
textarea.val( currElem.find('.apiBodyHolder').html() );
setTimeout(()=> {
textarea.height( (textarea[0].scrollHeight)-12 );
}, 10);
}
else {
currElem.find('.payloadInput').val('');
}
});
</script>
</body>

View File

@ -16,6 +16,13 @@ elseif(isset($data['recipes'])){
switch ($group){
case "product":
include 'products.php';
if(!empty($_POST)){
returns($data);
}
if( $data['sc'] == "groups" ){
returns( ProductGroup::getGroups() );
}
if(isset($data['sc'])){ $data['q'] = $data['sc']; }
if(isset($data['q'])){
if(strlen($data['q']) > 2){

View File

@ -149,6 +149,41 @@ class Product {
}
class ProductGroup {
private $name; // string
public $id; // integer
public $name; // string
public $parent; // integer
public static function getGroups(): array {
global $db;
$db = $db ?? database();
$query = $db->query("SELECT * FROM product_group");
$result = [];
while($row = $query->fetch_assoc()){
if($row['parent'] != null){
$result[ $row['parent'] ]['children'][] = $row;
}
else {
$result[ $row['group_id'] ] = $row;
}
}
return $result;
}
public function __construct(int $id, string $name, int $parent = null){
$this->id = $id;
$this->name = $name;
$this->parent = $parent;
}
public function toString(){
return [
"id"=>$this->id,
"name"=>$this->name,
"parent"=>$this->parent
];
}
}

22
www/api/v2/.htaccess Normal file
View File

@ -0,0 +1,22 @@
#RewriteBase /
RewriteEngine On
#RewriteRule ^/v1/?([*a-zA-Z0-9_-]+)$ /api/index.php?slug1=$1 [L]
#RewriteRule ^([*a-zA-Z0-9_-]+)(?:/|)$ index.php?fi=$1 [L]
RewriteRule ^([*a-zA-Z0-9_-]+)$ index.php?fi=$1 [L]
RewriteRule ^([*a-zA-Z0-9_-]+)/([*a-zA-Z0-9_%-]+)(?:/|)$ index.php?fi=$1&sc=$2 [L]
RewriteRule ^([*a-zA-Z0-9_-]+)/([*a-zA-Z0-9_%-]+)/([*a-zA-Z0-9_\W-]+)(?:/|)$ index.php?fi=$1&sc=$2&th=$3 [L]
RewriteRule ^([*a-zA-Z0-9_-]+)/([*a-zA-Z0-9_%-]+)/([*a-zA-Z0-9_\W-]+)/([*a-zA-Z0-9_%-]+)(?:/|)$ index.php?fi=$1&sc=$2&th=$3&fo=$4 [L]
#RewriteRule ^([a-zA-Z0-9_-]+)/$ index.php?slug=$1 [L]
#RewriteRule ^project/$ projects/index.php [L]
#RewriteRule ^projects/$ projects/index.php [L]
#RewriteRule ^v1/?([a-zA-Z0-9_-]+)$ index.php?project=$1 [L]
#RewriteRule ^project/?([a-zA-Z0-9_-]+)/$ projects/index.php?project=$1 [L]
#RewriteRule ^project/?([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ projects/index.php?project=$1&item=$2 [L]
#RewriteRule ^project/?([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ projects/index.php?project=$1&item=$2 [L]
#RewriteRule ^project/?([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ projects/index.php?project=$1&item=$2&ID=$3 [L]
#RewriteRule ^project/?([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ projects/index.php?project=$1&item=$2&ID=$3 [L]

110
www/api/v2/api.php Normal file
View File

@ -0,0 +1,110 @@
<?php
const VERIFY_STRING = "string";
const VERIFY_INT = "integer";
const VERIFY_NUMBER = "number";
const DEBUG = true;
abstract class Api {
protected $success = true; // bool
protected $message; // string
protected $result; // any
protected $methods; // array
protected $data; // array
function __construct(){
header("Content-Type: application/json");
try {
$this->parseRequest();
} catch (ApiException $e) {
$this->success = false;
$this->message = $e;
}
if(empty($this->data)){
$this->success = false;
$this->message = "No data"; // Todo: Specify missing fields in output
}
if($this->success){
$this->execute();
}
$this->printResult();
}
private function printResult(){
$returns['status'] = $this->success;
$returns['message'] = $this->message;
$returns['data'] = $this->result ?? $this->message;
if(DEBUG){
$returns['debug'][] = debug_backtrace();
$returns['debug']["methods"] = $this->methods;
}
echo json_encode( $returns );
}
protected function execute(){ }
/**
* @throws ApiException
*/
private function parseRequest(){
foreach ($this->methods as $method => $args){
if($method === "POST"){
$requestData = $_POST;
}
elseif($method === "GET"){
$requestData = $_GET;
}
elseif($method === "URI"){
$requestData = [];
foreach ($args as $k => $arg) {
$requestData[$arg['keyword']] = $_GET['args'][$k];
}
}
else {
throw new ApiException("Unrecognized method in function.");
}
foreach ($args as $arg){
if(isset($requestData[ $arg['keyword'] ])){
$value = $requestData[ $arg['keyword'] ];
switch ($arg['type']){
case VERIFY_STRING:
$value = (string) $value;
break;
case VERIFY_INT:
$value = (int) $value;
break;
case VERIFY_NUMBER:
$value = (double) $value;
break;
default:
throw new ApiException("Unknown argument type");
}
$this->data[$arg['keyword']] = $value;
}
}
}
return true;
}
}
class ApiException extends Exception {
function __construct($message = "", $code = 0, Throwable $previous = null){
parent::__construct($message, $code, $previous);
}
}

30
www/api/v2/index.php Normal file
View File

@ -0,0 +1,30 @@
<?php
$dirs = [];
$file = "";
$args = [];
foreach($_GET as $arg){
if(
(empty($dirs) && is_dir($arg)) ||
is_dir( implode('/', $dirs)."/".$arg )
){
$dirs[] = $arg;
}
elseif(file_exists( $filePathTemp = implode('/', $dirs)."/".$arg.".php" )){
$file = $arg.".php";
$filePath = $filePathTemp;
}
else {
$args[] = $arg;
}
}
if(!empty($filePath)){
$_GET['args'] = $args;
echo $filePath;
// include $filePath;
}

View File

@ -0,0 +1,28 @@
<?php
require_once '../api.php';
class AddProductAPI extends Api {
protected $methods = [
"GET" => [
[
"name"=>"search",
"keyword"=>"q",
"type"=>VERIFY_STRING
]
],
"URI" => [
[
"name"=>"search",
"keyword"=>"q",
"type"=>VERIFY_STRING,
]
]
];
function execute(){
}
}
new AddProductAPI();

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1,49 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once $_SERVER['DOCUMENT_ROOT'].'/api/v2/api.php';
require_once $_SERVER['DOCUMENT_ROOT'].'/models/products.php';
class ProductGetApi extends Api {
protected $methods = [
"GET" => [
[
"name"=>"search",
"keyword"=>"q",
"type"=>VERIFY_STRING
]
],
"URI" => [
[
"name"=>"search",
"keyword"=>"q",
"type"=>VERIFY_STRING,
]
]
];
function execute(){
parent::execute();
if($this->data){
$search = Product::search( $this->data['q'] );
if(!empty($search)){
$this->success = true;
$this->message = "OK";
$this->result = $search;
}
}
}
}
$request = new ProductGetApi();

View File

@ -0,0 +1 @@
<?php

35
www/api/v2/recipe/get.php Normal file
View File

@ -0,0 +1,35 @@
<?php
class RecipesGetApi extends Api {
protected $methods = [
"GET" => [
[
"name" => "search",
"keyword" => "q",
"type" => VERIFY_STRING
]
],
"URI" => [
[
"name" => "search",
"keyword" => "q",
"type" => VERIFY_STRING,
]
]
];
function execute(){
// parent::execute();
if($this->data){
$this->result = Recipes::getAll();
$this->success = true;
$this->message = "OK";
}
}
}
$request = new RecipesGetApi();

24
www/api/v2/space/get.php Normal file
View File

@ -0,0 +1,24 @@
<?php
require_once '../api.php';
class GetSpacesApi extends Api {
protected $methods = [
"GET" => [
[
"name" => "search",
"keyword" => "q",
"type" => VERIFY_STRING
]
]
];
function execute(){
$this->result = PlanSpace::get();
$this->success = true;
$this->message = "OK";
}
}
$request = new GetSpacesApi();

89
www/dinner/index.php Normal file
View File

@ -0,0 +1,89 @@
<?php $rPath = "../"; require $rPath.'webdata/init.php'; //requireLogin(); ?><!DOCTYPE html>
<html lang="en">
<head>
<?=getHtmlHeaders($rPath);?>
<title>Dinner Planner - PaperBag - Plan Your Shopping</title>
<!-- jsDelivr :: Sortable :: Latest (https://www.jsdelivr.com/package/npm/sortablejs) -->
<script src="/js/sortable/Sortable.min.js"></script> <!-- CDN: https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js -->
<script src="/js/sortable/jquery-sortable.js"></script> <!-- CDN: https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js -->
</head>
<body id='dinnerplan'>
<div id="page-container">
<div id="page-wrapper">
<?php include $rPath.'webdata/navbar.php'; ?>
<h1 class="headline text-center">Dinner Planner - PaperBag</h1>
<div class="container" style="padding-top: 5px; padding-bottom: 15px; text-align: center;">
<div class="accordion" id="accordionExample" style="max-width: 720px; margin: auto;">
<?php
// Start of the planner-list
$listDay = new DateTime();
// List days
$weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
// Loop for 7 days ahead
for($i = 0; $i < 7; $i++){
// HTML ids
$accordionParent = "accordionExample";
$headingid = "heading".$i;
$contentid = "collapse".$i;
// Day properties
$weekday = $weekdays[ $listDay->format('N') - 1 ];
$isToday = $listDay->format('Y-m-d') == date('Y-m-d');
$isSunday = $listDay->format('N') == 7;
// Set next day
$listDay->add( DateInterval::createFromDateString('1 day') );
?>
<div class="accordion-item">
<h2 class="accordion-header" id="<?=$headingid;?>">
<button class="accordion-button <?=($isToday?"":"collapsed");?> <?=$isSunday?"text-danger":"";?>" type="button" data-bs-toggle="collapse" data-bs-target="#<?=$contentid;?>" aria-expanded="<?=$isToday?"true":"false";?>" aria-controls="<?=$contentid;?>">
<div style="width: 100px; max-width: 15%; margin-right: 10px; overflow: hidden;">
<?=$weekday;?>
</div>
<div style="">
Taco
</div>
</button>
</h2>
<div id="<?=$contentid;?>" class="accordion-collapse collapse <?=$isToday?"show":"";?>" aria-labelledby="<?=$headingid;?>" data-bs-parent-stay-open="#<?=$accordionParent;?>">
<div class="accordion-body">
<ul class="list-group">
<li>Kjøttdeig</li>
<li>Mais</li>
<li>Revet ost</li>
<li>...</li>
</ul>
Estimated price: <span class="priceWrapper price">245.00</span>
</div>
</div>
</div>
<?php
if($isSunday){
echo "<hr>";
}
}
?>
</div>
</div>
</div>
<?php include $rPath.'webdata/footer.html'; ?>
</div>
</body>
</html>

19
www/domain1.po Normal file
View File

@ -0,0 +1,19 @@
msgid ""
msgstr ""
"X-Domain: domain1\n"
#: index.php:9
msgid "PaperBag - Plan & Execute Your Shopping"
msgstr ""
#: test.php:36
msgid "Guten morgen"
msgstr ""
#: test.php:38
msgid "Ich bin 13 Jahre alt."
msgstr ""
#: test.php:41
msgid "Login"
msgstr ""

3
www/domain2.po Normal file
View File

@ -0,0 +1,3 @@
msgid ""
msgstr ""
"X-Domain: domain2\n"

3
www/domain3.po Normal file
View File

@ -0,0 +1,3 @@
msgid ""
msgstr ""
"X-Domain: domain3\n"

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filter-square" viewBox="0 0 16 16">
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
<path d="M6 11.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 499 B

View File

@ -6,6 +6,7 @@ class HomePage extends WebPage {
public $title = "PaperBag - Plan & Execute Your Shopping";
function load(){
$this->title = _("PaperBag - Plan & Execute Your Shopping");
if (file_exists('changes.txt')) {
$this->changes = file_get_contents('changes.txt');

478
www/paperbag_project.svg Normal file
View File

@ -0,0 +1,478 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Vectornator for iOS (http://vectornator.io/) -->
<svg
height="100%"
style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;"
version="1.1"
viewBox="0 0 1024 1024"
width="100%"
xml:space="preserve"
id="svg130"
sodipodi:docname="paperbag_project.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:vectornator="http://vectornator.io"><sodipodi:namedview
id="namedview132"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.57866746"
inkscape:cx="554.72274"
inkscape:cy="563.36328"
inkscape:window-width="1920"
inkscape:window-height="1007"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="false"
inkscape:lockguides="true" />
<metadata
id="metadata2">
<vectornator:setting
key="IsTimeLapseWatermarkDisabled"
value="false" />
<vectornator:setting
key="UndoHistoryDisabled"
value="true" />
<vectornator:setting
key="VNDimensionsVisible"
value="true" />
<vectornator:setting
key="VNSnapToGuides"
value="true" />
<vectornator:setting
key="WDCMYKEnabledKey"
value="false" />
<vectornator:setting
key="WDDisplayWhiteBackground"
value="false" />
<vectornator:setting
key="WDDynamicGuides"
value="false" />
<vectornator:setting
key="WDGuidesVisible"
value="true" />
<vectornator:setting
key="WDIsolateActiveLayer"
value="false" />
<vectornator:setting
key="WDOutlineMode"
value="false" />
<vectornator:setting
key="WDRulersVisible"
value="true" />
<vectornator:setting
key="WDSnapToEdges"
value="false" />
<vectornator:setting
key="WDSnapToGrid"
value="false" />
<vectornator:setting
key="WDSnapToPoints"
value="false" />
<vectornator:setting
key="WDUnits"
value="Pixels" />
</metadata>
<defs
id="defs4"><linearGradient
inkscape:collect="always"
id="linearGradient32642"><stop
style="stop-color:#6d3b1d;stop-opacity:1"
offset="0"
id="stop32638" /><stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop32640" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient32526"><stop
style="stop-color:#6d3b1d;stop-opacity:1"
offset="0"
id="stop32522" /><stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop32524" /></linearGradient><linearGradient
id="linearGradient26849"
inkscape:swatch="solid"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop26847" /></linearGradient><inkscape:path-effect
effect="bspline"
id="path-effect50075"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" /><inkscape:path-effect
effect="bspline"
id="path-effect49836"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" /><inkscape:path-effect
effect="bspline"
id="path-effect36428"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" /><linearGradient
inkscape:collect="always"
id="linearGradient23721"><stop
style="stop-color:#502d16;stop-opacity:1"
offset="0"
id="stop23717" /><stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop23719" /></linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient32526"
id="linearGradient23723"
x1="97.338539"
y1="631.9906"
x2="288.20328"
y2="653.50916"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient32642"
id="linearGradient35087"
gradientUnits="userSpaceOnUse"
x1="214.13283"
y1="641.42719"
x2="421.24988"
y2="668.87" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient32526"
id="linearGradient7587"
gradientUnits="userSpaceOnUse"
x1="266.94949"
y1="645.74878"
x2="60.621822"
y2="589.76025"
gradientTransform="translate(-54.3374,425.54222)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient23721"
id="linearGradient7589"
gradientUnits="userSpaceOnUse"
x1="147.94511"
y1="643.8504"
x2="288.20328"
y2="653.50916" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient32642"
id="linearGradient27631"
x1="657.17761"
y1="199.90794"
x2="865.33978"
y2="992.72205"
gradientUnits="userSpaceOnUse" /></defs>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 1"
style="display:inline;opacity:1"><path
d="m 479.70824,311.74676 c 46.5789,-2.97382 93.27876,-3.97282 139.67399,-9.38094"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="7.28096"
id="path14"
style="clip-rule:evenodd;fill:#502d16;fill-rule:nonzero;stroke-width:7.181;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:export-filename="/home/eirik/Documents/usegit/paperbag/www/favicon.png"
inkscape:export-xdpi="53.330002"
inkscape:export-ydpi="53.330002" /><path
d="m 619.38223,302.36582 c 12.16315,-1.63349 24.29094,-3.70458 36.37131,-6.39003 3.027,-0.67242 6.45575,-1.58131 10.18797,-2.66525 -0.55679,-6.33662 -1.54031,-23.8095 -1.95079,-31.10819 -0.92256,-16.40478 0.8555,-32.41462 0.85539,-48.79509 C 662.711,140.232 683.238,48.2508 553.672,65.9527 413.583,85.0921 427.74236,196.52332 427.74236,313.88232 c 0,2.739 0.19464,-0.52232 26.15105,-1.39874 C 480.13,311.325 480.13,311.325 480.13,311.325 c -0.56992,-12.01966 3.39412,-51.09304 6.45968,-66.87096 9.39138,-48.3269 21.18308,-125.92605 79.42972,-129.48309 75.84596,-4.63326 43.61348,128.9705 53.36283,187.39487"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="5.11077"
id="path18"
style="clip-rule:evenodd;fill:url(#linearGradient27631);fill-opacity:1;fill-rule:nonzero;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:nodetypes="cccsccsccccc"
inkscape:export-filename="/home/eirik/Documents/usegit/paperbag/www/favicon.png"
inkscape:export-xdpi="53.330002"
inkscape:export-ydpi="53.330002" /><g
id="g14741"
style="clip-rule:evenodd;opacity:1;mix-blend-mode:normal;fill:url(#linearGradient23723);fill-opacity:1;fill-rule:nonzero;stroke-linecap:round;stroke-linejoin:round"
inkscape:export-filename="/home/eirik/Documents/usegit/paperbag/www/favicon.png"
inkscape:export-xdpi="53.330002"
inkscape:export-ydpi="53.330002"><path
d="M 231.48132,898.16 C 205.69804,811.44406 254.845,621.00683 263.98411,526.96742 267.86027,487.09371 274.34028,446.66065 276.73464,406.70863 277.61421,392.03731 284.46467,358.4497 285.224,345.669 228.37842,415.89352 105.05332,960.163 110.18806,961.34872 141.52111,931.55334 201.63949,907.63262 231.431,898.16"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="5.95864"
id="path30"
style="fill:url(#linearGradient35087);fill-opacity:1.0"
sodipodi:nodetypes="cccccc" /></g><path
d="m 665.81943,293.4334 c -0.59036,-0.39378 63.7089,-10.10395 71.62021,-11.80117 32.56835,-6.98531 65.13571,-10.09704 101.79831,-10.12994 30.03242,14.91978 33.2655,46.9455 43.91305,75.27071 10.777,31.845 18.5025,137.27629 21.17748,165.45697 C 907.51556,551.14293 908.973,692.818 918,787.475 c -16.87849,4.28903 -35.57655,10.96608 -37.31583,11.54479 -47.83179,15.9059 -96.83322,26.5717 -145.05887,40.88668 C 632.3243,870.56629 414.96113,906.33189 354.324,954.518 316.65406,882.82433 321.85927,672.6089 317.82559,638.12497 306.76272,543.56554 310.89847,439.3031 285.224,345.669 c 25.795,-8.5305 81.769,-19.23 106.344,-23.996 29.357,-5.693 58.925,-8.511 88.562,-10.348"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="5.75513"
id="path46"
style="clip-rule:evenodd;fill:#6d3b1d;fill-opacity:1;fill-rule:nonzero;stroke-linecap:round;stroke-linejoin:round"
sodipodi:nodetypes="ccccccccccccc"
inkscape:export-filename="/home/eirik/Documents/usegit/paperbag/www/favicon.png"
inkscape:export-xdpi="53.330002"
inkscape:export-ydpi="53.330002" /></g><g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="bag-side"
style="display:inline"><g
id="g14741-7"
style="clip-rule:evenodd;display:inline;mix-blend-mode:normal;fill:url(#linearGradient7589);fill-opacity:1;fill-rule:nonzero;stroke-linecap:round;stroke-linejoin:round"
transform="translate(54.38772,-425.5422)"><path
d="m 177.14392,1323.7022 c -25.78328,-86.7159 23.36368,-277.1532 32.50279,-371.19256 3.87616,-39.87371 10.35617,-80.30677 12.75053,-120.25879 0.87957,-14.67132 7.73003,-48.25893 8.48936,-61.03963 m -53.793,552.49098 c 14.99425,7.0555 104.36899,47.5738 121.51666,55.586 -36.2936,-70.9217 -31.08839,-281.1371 -35.12207,-315.621 -11.06287,-94.5595 -6.63706,-199.0342 -32.60159,-292.456"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="5.95864"
id="path30-5"
style="fill:url(#linearGradient7587);fill-opacity:1"
sodipodi:nodetypes="cccccccc" /></g></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="name"
style="display:none;opacity:1"><g
id="g824"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 477.212,574.771 c -0.271,-3.597 -0.553,-7.194 -0.956,-10.775 -1.103,-9.787 -2.845,-19.482 -3.706,-29.301 -1.163,-13.267 -2.02,-26.582 -2.364,-39.907 -0.867,-33.578 5.849,-67.303 22.336,-95.298 4.441,-7.541 12.63,-15.637 20.289,-18.636 6.621,-2.592 8.308,2.594 9.46,9.33 1.099,6.432 1.179,13.044 1.467,19.587 0.622,14.088 0.681,28.173 0.4975,45.777 -0.4605,37.548 -1.4515,71.575 -0.1395,105.628"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path58"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccccsccccc" /><path
d="m 470.846,486.212 c 2.722,-2.324 6.309,-4.166 8.03,-5.31 13.641,-9.07 31.982,-13.753 45.184,-24.632"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path62"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccc" /></g><g
id="g846"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 561.529,481.529 c 0.32,-0.007 0.641,-0.031 0.961,-0.072 5.744,-0.744 11.466,-3.634 15.939,-7.87 6.816,-6.454 11.871,-15.132 17.065,-23.381 9.69,-15.392 17.695,-31.668 23.141,-49.69 3.984,-13.18 9.933,-41.691 0.027,-54.08 -15.87,-19.848 -47.682,7.357 -62.321,22.9405 -1.349,17.1875 0.537,34.3605 1.438,49.5285 1.813,30.529 4.023,61.068 4.374,91.668 0.129,11.311 -0.462,22.615 -0.414,33.927 0.011,2.737 -0.197,5.835 -0.06,8.757"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path66"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="cccccsccccc" /></g><g
id="g869"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 648.235,442.488 c 25.72,-13.625 55.835,-12.43 75.544,-37.725"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path70"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
d="m 720.595,332.592 c -17.555,6.472 -34.703,14.242 -52.879,18.261 -4.144,0.916 -8.402,0.952 -12.607,1.372 -1.206,0.12 -7.825,0.375 -7.823,0.416 0.023,0.52 3.76,1.596 4.305,1.878 l -1.203,0.064 c 4.883,-6.267 6.378,8.935 6.481,10.19 0.577,6.99 0.805,14.06 0.326,21.053 -2.367,34.571 -13.577,67.057 -16.641,101.316 -0.437,4.886 -1.179,10.646 1.251,14.682 1.975,3.281 6.382,3.685 9.814,3.693 5.284,0.012 10.503,-1.747 15.523,-3.634 15.996,-6.013 29.552,-17.021 46.861,-17.941"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path78"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccccccccsccsc" /></g><g
id="g916"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 465.746,737.237 c 14.139,-7.465 26.518,-20.232 38.255,-31.08 15.696,-14.504 36.072,-31.655 42.194,-53.864 8.413,-30.522 -32.76,-49.092 -55.768,-43.178 -13.27,3.411 -18.748,8.794 -28.793,17.998 -3.673,3.366 -7.757,6.363 -11.5975,10.4195 0.0715,2.7755 0.6285,5.1275 1.1145,7.4975 1.661,8.096 3.564,16.133 4.946,24.297 6.697,39.575 11.489,80.31 12.451,120.647 0.342,14.351 -0.345,24.898 -0.075,37.6175 -4.583,3.3595 9.31,-3.7285 12.209,-4.3195 12.016,-2.452 23.846,-6.388 35.516,-10.082 30.205,-9.561 73.793,-21.079 85.595,-55.358 2.229,-6.475 -2,-14.607 -3.689,-20.683 -7.824,-28.149 -54.721,-42.382 -81.079,-34.764 -5.136,1.484 -12.864,3.11 -16.66,7.271"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path82"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="cccscccccccscccc" /></g><g
id="g940"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 623.885,794.918 c -0.27,-6.282 0.329,-13.895 0.193,-16.502 -1.101,-21.194 -2.93,-42.311 -4.032,-63.508 -0.175,-3.369 -0.29,-6.726 -0.3835,-11.9465 -0.1695,-18.0795 0.7475,-34.0915 1.1785,-50.4595 0.541,-20.519 2.029,-38.709 8.878,-58.012 4.363,-12.295 13.614,-31.443 28.525,-32.139 14.374,-0.67 20.939,32.138 23.135,43.124 8.809,44.07 10.189,89.563 17.864,133.785 1.434,8.267 9.716,25.206 7.562,33.134"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path86"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccsccccscc" /><path
d="m 619.677,704.202 c -10e-4,-0.044 -10e-4,-0.088 -0.002,-0.131"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path88"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path
d="m 619.925,699.64 c -0.005,-0.074 -0.006,-0.145 -10e-4,-0.214 0.344,-4.813 6.338,-8.001 9.335,-9.812 16.152,-9.759 30.246,-10.632 47.735,-14.951 5.578,-1.377 9.695,-7.149 15.353,-7.413"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path92"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g><g
id="g961"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 824.703,576.019 c 11.782,-20.168 -22.898,-32.351 -37.464,-28.391 -14.657,3.985 -29.238,23.583 -37.543,35.238 -21.84,30.655 -55.467,82.924 -40.366,122.437 4.989,13.053 15.445,20.948 26.938,27.542 29.679,17.03 65.674,-3.455 85.765,-27.441 14.461,-17.264 -9.905,-23.36 -23.569,-25.69 -3.439,-0.587 -8.946,-1.439 -13.853,-1.409"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path94"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g><g
id="g801"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
d="m 352.496,526.385 c -0.497,-16.614 -1.029,-33.227 -1.945,-49.822 -0.734,-13.313 -1.312,-26.669 -2.965,-39.875 -0.437,-3.488 -1.251,-5.581 -2.432,-7.074 -0.994,-1.815 -2.276,-4.181 -2.0135,-6.4435 3.3605,-4.4815 7.8155,-8.0065 9.4735,-9.2245 5.498,-4.037 15.355,-8.4 21.201,-11.661 15.434,-8.61 30.356,-18.178 48.064,-18.004 6.312,0.061 13.008,0.999 16.65,8.245 5.897,11.732 -1.458,34.176 -5.273,44.527 -11.273,30.58 -33.198,57.228 -57.658,73.998 -7.177,4.921 -17.173,6.619 -23.511,14.3825 0.849,26.2915 1.238,50.9515 2.058,75.5785 0.203,6.096 0.182,12.295 0.866,18.248"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path56"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccccccccccccsc" /></g><g
id="g893"
style="clip-rule:evenodd;display:inline;opacity:1;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="translate(-8.0160215,10.973181)"><path
d="m 777.854,480.454 c -0.158,-0.658 -0.267,-1.222 -0.3,-1.576 -0.65,-7.085 -0.634,-14.267 -0.747,-21.41 -0.564,-35.865 1.823,-71.501 0.573,-107.341 -0.016,-0.45 -0.036,-0.994 0.029,-1.0495 -0.545,0.1395 -0.275,-7.5045 -0.435,-8.8045 -0.716,-5.808 -0.581,-11.75 -0.802,-17.596 -0.133,-3.505 -0.919,-6.674 -0.253,-9.7845 14.15,-9.4145 34.364,-11.3295 48.999,-8.3165 17.881,3.681 16.95,19.813 10.604,35.255 -2.664,6.483 -6.244,12.448 -10.059,18.092 -3.767,5.573 -8.12,10.601 -12.59,15.418 -5.144,5.544 -10.356,11.102 -16.201,15.61 -7.448,5.744 -16.039,9.799 -24.811,11.853"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path72"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccccccccccsccc" /><path
d="m 804.464,387.834 c 13.389,15.324 20.481,34.191 30.063,52.482 6.409,12.234 13.842,23.871 20.295,36.145"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path76"
style="fill:none;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g></g><g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="letter"
style="display:inline"><g
id="g801-6"
style="clip-rule:evenodd;display:inline;fill:none;fill-rule:nonzero;stroke:#ffffff;stroke-width:11.7272;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(3.4275386,0,0,2.2262343,-727.53708,-512.27998)"><path
d="m 352.496,526.385 c -0.497,-16.614 -1.029,-33.227 -1.945,-49.822 -0.734,-13.313 -1.312,-26.669 -2.965,-39.875 -0.437,-3.488 -1.251,-5.581 -2.432,-7.074 -0.994,-1.815 -2.276,-4.181 -2.0135,-6.4435 3.3605,-4.4815 7.8155,-8.0065 9.4735,-9.2245 5.498,-4.037 15.355,-8.4 21.201,-11.661 15.434,-8.61 30.356,-18.178 48.064,-18.004 6.312,0.061 13.008,0.999 16.65,8.245 5.897,11.732 -1.458,34.176 -5.273,44.527 -11.273,30.58 -33.198,57.228 -57.658,73.998 -7.177,4.921 -17.173,6.619 -23.511,14.3825 0.849,26.2915 1.238,50.9515 2.058,75.5785 0.203,6.096 0.182,12.295 0.866,18.248"
fill="none"
opacity="1"
stroke="#000000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="6"
id="path56-2"
style="fill:none;stroke:#ffffff;stroke-width:11.7272;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccccccccccccsc" /></g></g></svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,17 +1,16 @@
<?php
require '../webdata/init.php';
require_once '../../Router.php';
$db = database();
//$db = database();
header("Content-Type: application/json");
if(!checkLogin()){
//if(!checkLogin()){
if(Auth::checkLogin()){
returns("Not logged in",2);
}
$user_id = $_SESSION['user_id'];
//$user_id = $_SESSION['user_id'];
$data = [];
$returns = [];
@ -281,13 +280,13 @@ function checkArgs($args){
}
function verifySpaceID($input = null): int{
global $db, $user_id;
global $user_id;
if($input != null){
$countOwnerSQL = "SELECT count(0) FROM plan_space WHERE owner_id = $user_id AND space_id = $input";
$countMemberSQL = "SELECT count(0) FROM plan_space_member WHERE member_id = $user_id AND space_id = $input";
$verifyAccessSQL = "SELECT ($countOwnerSQL)+($countMemberSQL) verified;";
if(($verifyRes = $db->query($verifyAccessSQL)) && $verifyRes->fetch_row()[0] >= 1){
if(($verifyRes = DB::query($verifyAccessSQL)) && count($verifyRes ) >= 1){
return $input;
}
return 0;
@ -295,12 +294,12 @@ function verifySpaceID($input = null): int{
// CHECK FOR users own space
$selectSpaceQuery = $db->query("SELECT space_id FROM plan_space WHERE `owner_id` = '$user_id';");
$selectSpaceQuery = DB::query("SELECT space_id FROM plan_space WHERE `owner_id` = '$user_id';");
// IF none, create a space
if($selectSpaceQuery->num_rows == 0){
$db->query("INSERT INTO plan_space SET owner_id = $user_id;");
return $db->insert_id;
if(count($selectSpaceQuery) == 0){
DB::query("INSERT INTO plan_space SET owner_id = $user_id;");
return DB::insert_id();
}
return $selectSpaceQuery->fetch_row()[0];

View File

@ -3,9 +3,13 @@ require_once '../../Router.php';
class PlanPage extends WebPage implements RequireAuth {
public $pagekey = "plan";
public $title = "Plan - PaperBag - Plan & Execute Your Shopping";
public $title = "Plan Your Shopping - PaperBag";
function load(){
$this->spaces = PlanSpace::getUserSpaces();
// Utils::debug( $this->spaces );
// exit;
}
}

View File

@ -220,7 +220,7 @@ class Store {
if(state === "closed"){
this.state = "closed";
this.selector.find('.archiveSection').slideDown(animTime);
}
if(state !== "closed"){
this.selector.find('.archiveSection').slideUp(animTime);
@ -315,7 +315,7 @@ class Store {
" <div style='display: flex;'>" + // draggable='true'
//" <span style='float: left; margin-right: 0px; width: 32px; margin-left: -22px;'><svg xmlns='http://www.w3.org/2000/svg' fill='gray' class='bi bi-grip-vertical' viewBox='0 0 16 16' height='32' width='32'> <path d='M7 2a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM7 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM7 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0z'></path></svg></span>" +
" <div style='flex: 10px; display: none; margin-right: 5px;' class='checkItems'><input type='checkbox'"+(checked?" checked":"")+"></div>" +
" <div style='flex: 55%;'>"+text+"</div>" +
" <div style='flex: 55%;'>"+(debug?itemID+" ":"")+text+"</div>" +
" <div style='flex: 35%;' class='priceWrapper'>" +
" <span class='price'>"+(price*amount).toFixed(2)+"</span>" +
@ -333,6 +333,9 @@ class Store {
" <button class='btn btn-outline-success itemAmountBtn' type='button'>+</button>" +
" </div>" +
// " <button class='btn btn-secondary' style='float: right;'>Save</button>" +
" <button class='btn btn-outline-secondary ariaButton editItemBtn' type='button' style='float: right; margin: 5px 10px;'><img src='../icon/pencil-square.svg' alt='Edit item'></button>" +
// " <select class='form-control form-select-sm'><option>Choose product group</option><option>Dairy</option><option>Cheese</option></select>" +
" </div>" +

61
www/test.php Normal file
View File

@ -0,0 +1,61 @@
<?php
//use models\PlanSpace;
require '../Router.php';
//echo implode(", ", array_fill(0, 4, "?"));
//echo "<br>\n";
//model("PlanSpace");
try {
// $test = Nutrient::get();
foreach(TranslationString::get(['language' => 'nb']) as $n){
// foreach(Translation::get() as $n){
Utils::debug($n);
}
} catch (DatabaseException $e) {
}
die();
//require '../application/Translator.php';
//$trans = Translator::getInstance();
//use Gettext\Generator\PoGenerator;
//use Gettext\Translation;
//use Gettext\Translations;
/*$translations = Translations::create('my-domain');
//You can add new translations:
$translation = Translation::create('comments', 'One comment', '%s comments');
$translations->add($translation);
//Find a specific translation
$translation = $translations->find('comments', 'One comment');
//Edit headers, domain, etc
$translations->getHeaders()->set('Last-Translator', 'Oscar Otero');
$translations->setDomain('my-blog');
$gen = new PoGenerator();
//$gen->generateFile($translations, 'testLocale.po');
*/
//$a = new TranslatorScanning();
//$b = TranslatorScanning::loader();
//TranslatorScanning::compiler();
$c = new Translator();
echo _("Guten morgen");
echo "<br>";
echo _("Ich bin 13 Jahre alt.");
echo "<br>";
echo _("Login");

View File

@ -122,16 +122,16 @@ CREATE OR REPLACE TABLE `product_price` (
CONSTRAINT `product_price_FK` FOREIGN KEY (`product_id`) REFERENCES `product` (`product_id`)
);
CREATE OR REPLACE TABLE `nutrition` (
`nutrition_id` mediumint(9) NOT NULL AUTO_INCREMENT,
`nutrition` varchar(100) NOT NULL,
`parent` mediumint(9) DEFAULT NULL,
PRIMARY KEY (`nutrition_id`),
KEY `nutrition_FK` (`parent`),
CONSTRAINT `nutrition_FK` FOREIGN KEY (`parent`) REFERENCES `nutrition` (`nutrition_id`)
CREATE OR REPLACE TABLE `nutrient` (
`nutrient_id` mediumint(9) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`parent` mediumint(9) DEFAULT NULL,
PRIMARY KEY (`nutrient_id`),
KEY `nutrient_FK` (`parent`),
CONSTRAINT `nutrient_FK` FOREIGN KEY (`parent`) REFERENCES `nutrient` (`nutrient_id`)
);
INSERT INTO nutrition (nutrition_id, nutrition, parent) VALUES
INSERT INTO nutrient (nutrient_id, name, parent) VALUES
(1, 'Energy', NULL),
(2, 'Fat', NULL), (3, 'of which saturates', 2),
(4, 'Carbohydrate', NULL), (5, 'of which sugars', 4), (6, 'of which starch', 4),
@ -139,14 +139,13 @@ INSERT INTO nutrition (nutrition_id, nutrition, parent) VALUES
(8, 'Salt', NULL);
CREATE OR REPLACE TABLE `product_nutrition` (
`product_id` bigint(20) NOT NULL,
`variant_id` int(11) NOT NULL,
`nutrition_id` mediumint(9) NOT NULL,
`value` smallint(6) NOT NULL,
`units` enum('g','ml','cups','%') NOT NULL DEFAULT 'g',
PRIMARY KEY (`product_id`,`variant_id`,`nutrition_id`),
KEY `product_nutrition_FK` (`nutrition_id`),
CONSTRAINT `product_nutrition_FK` FOREIGN KEY (`nutrition_id`) REFERENCES `nutrition` (`nutrition_id`)
`product_id` bigint(20) NOT NULL,
`variant_id` int(11) NULL,
`nutrient_id` mediumint(9) NOT NULL,
`value` smallint(6) NOT NULL,
`units` enum('g','ml','cups','%') NOT NULL DEFAULT 'g',
PRIMARY KEY (`product_id`,`variant_id`,`nutrient_id`),
CONSTRAINT `product_nutrition_FK` FOREIGN KEY (`nutrient_id`) REFERENCES `nutrient` (`nutrient_id`)
);
";