diff --git a/www/ajax/egh_redraw.php b/www/ajax/egh_redraw.php
index b7d6597..f46193c 100644
--- a/www/ajax/egh_redraw.php
+++ b/www/ajax/egh_redraw.php
@@ -1,14 +1,11 @@
init();
-$v->updateFromCache();
-$v->generate();
+$v->update();
-file_put_contents(__DIR__ . '/../dynamic/egh.html', $v->getAll());
\ No newline at end of file
diff --git a/www/ajax/egh_refresh.php b/www/ajax/egh_refresh.php
index e67a198..78eaa61 100644
--- a/www/ajax/egh_refresh.php
+++ b/www/ajax/egh_refresh.php
@@ -1,15 +1,12 @@
init();
-$v->updateFromRemotes();
-$v->generate();
-
-file_put_contents(__DIR__ . '/../dynamic/egh.html', $v->getAll());
+$v->update();
+$v->updateCache();
diff --git a/www/ajax/egh_status.php b/www/ajax/egh_status.php
index c8cc1af..03e9fac 100644
--- a/www/ajax/egh_status.php
+++ b/www/ajax/egh_status.php
@@ -2,9 +2,17 @@
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
-if (key_exists('ajax_progress_egh_refresh', $_SESSION))
- echo $_SESSION['ajax_progress_egh_refresh'];
+global $CONFIG;
+
+if (isset($_GET['clear']))
+{
+ if (key_exists($CONFIG['extendedgitgraph']['session_var'], $_SESSION)) $_SESSION[$CONFIG['extendedgitgraph']['session_var']] = '';
+}
+
+if (key_exists($CONFIG['extendedgitgraph']['session_var'], $_SESSION))
+ echo $_SESSION[$CONFIG['extendedgitgraph']['session_var']];
else
echo '[[ NO SESSION STARTED ]]';
+
return;
\ No newline at end of file
diff --git a/www/extern/egh/ConnectionGitea.php b/www/extern/egh/ConnectionGitea.php
deleted file mode 100644
index 922c7b0..0000000
--- a/www/extern/egh/ConnectionGitea.php
+++ /dev/null
@@ -1,39 +0,0 @@
-owner = $owner;
- }
-
- public function setURL($giteaurl) {
- $this->url = $giteaurl;
- }
-
- /* @return SingleCommitInfo[] */
- public function getDataUser($cfg)
- {
- $result = []; //TODO
-
- return $result;
- }
-
- /* @return SingleCommitInfo[] */
- public function getDataRepository($cfg)
- {
- $result = []; //TODO
-
- return $result;
- }
-}
\ No newline at end of file
diff --git a/www/extern/egh/ConnectionGithub.php b/www/extern/egh/ConnectionGithub.php
deleted file mode 100644
index bd2aa3c..0000000
--- a/www/extern/egh/ConnectionGithub.php
+++ /dev/null
@@ -1,166 +0,0 @@
-owner = $owner;
- }
-
- public function setAPIToken($token) {
- $this->token = $token;
- }
-
- public function queryAPIToken($client_id, $client_secret) {
- $url = Utils::sharpFormat(self::URL_OAUTH_TOKEN, ['id'=>$client_id, 'secret'=>$client_secret, 'code'=>'egh']);
- $result = file_get_contents($url);
-
- $result = str_replace('access_token=', '', $result);
- $result = str_replace('&scope=&token_type=bearer', '', $result);
-
- $this->setAPIToken($result);
- }
-
- /**
- * @param $cfg EGHRemoteConfig
- * @return SingleCommitInfo[]
- */
- public function getDataUser($cfg)
- {
- $repos = $this->listRepositoriesByUser($cfg->Param);
-
- $result = [];
- foreach ($repos as $repo)
- {
- $commits = $this->listCommitsFromRepo($repo, $cfg->Author);
- foreach ($commits as $c) $result []= $c;
- }
-
- return $result;
- }
-
-
- /**
- * @param $cfg EGHRemoteConfig
- * @return SingleCommitInfo[]
- */
- public function getDataRepository($cfg)
- {
- return $this->listCommitsFromRepo($cfg->Param, $cfg->Author);
- }
-
- /**
- * @param $user string
- * @return string[]
- */
- private function listRepositoriesByUser($user)
- {
- $result = [];
-
- $page = 1;
- $url = Utils::sharpFormat(self::API_REPOSITORIESLIST, ['user'=>$user, 'page'=>$page, 'token'=>$this->token]);
-
- $json = $this->getJSON($url);
-
- while (! empty($json)) {
- foreach ($json as $result_repo) {
- $result []= $result_repo->{'full_name'};
- $this->owner->out("Found Repo: " . $result_repo->{'full_name'});
- }
-
- $page++;
- $url = Utils::sharpFormat(self::API_REPOSITORIESLIST, ['user'=>$user, 'page'=>$page, 'token'=>$this->token]);
- $json = $this->getJSON($url);
- }
-
- return $result;
- }
-
- /**
- * @param $repo string
- * @param $user string
- * @return SingleCommitInfo[]
- */
- private function listCommitsFromRepo($repo, $user)
- {
- $page = 1;
- $url = Utils::sharpFormat(self::API_COMMITSLIST, ['repo'=>$repo, 'author'=>$user, 'page'=>$page, 'token'=>$this->token]);
-
- $result = $this->getJSON($url);
-
- $commit_list = [];
-
- while (! empty($result)) {
- foreach ($result as $rc) $commit_list[] = new SingleCommitInfo(DateTime::createFromFormat(DateTime::ISO8601, $rc->{'commit'}->{'author'}->{'date'}), 'github', $user, $repo);
- $this->owner->out("Found " . count($result) . " Commits in " . $repo);
-
- $page++;
- $url = Utils::sharpFormat(self::API_COMMITSLIST, [ 'repo'=>$repo, 'author'=>$user, 'page'=>$page, 'token'=>$this->token ]);
- $result = $this->getJSON($url);
- }
-
- return $commit_list;
- }
- public function getJSON($url) {
- if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
- $options =
- [
- 'http' =>
- [
- 'user_agent' => $_SERVER['HTTP_USER_AGENT'],
- 'header' => 'Authorization: token ' . $this->token,
- ],
- 'https' =>
- [
- 'user_agent' => $_SERVER['HTTP_USER_AGENT'],
- 'header' => 'Authorization: token ' . $this->token,
- ],
- ];
- } else {
- $options =
- [
- 'http' =>
- [
- 'user_agent' => 'ExtendedGitGraph_for_mikescher.com',
- 'header' => 'Authorization: token ' . $this->token,
- ],
- 'https' =>
- [
- 'user_agent' => 'ExtendedGitGraph_for_mikescher.com',
- 'header' => 'Authorization: token ' . $this->token,
- ],
- ];
- }
-
- $context = stream_context_create($options);
-
- $response = @file_get_contents($url, false, $context);
-
- if ($response === false)
- {
- $this->owner->out("Error recieving json: '" . $url . "'");
- return [];
- }
-
- return json_decode($response);
- }
-
-}
\ No newline at end of file
diff --git a/www/extern/egh/EGGDatabase.php b/www/extern/egh/EGGDatabase.php
new file mode 100644
index 0000000..67ec66d
--- /dev/null
+++ b/www/extern/egh/EGGDatabase.php
@@ -0,0 +1,430 @@
+path = $path;
+ $this->logger = $log;
+ }
+
+ public function open()
+ {
+ $exists = file_exists($this->path);
+
+ if (!$exists) $this->logger->proclog("No database file found. Creating new at '" . $this->path . "'");
+
+ $dsn = "sqlite:" . $this->path;
+ $opt =
+ [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ ];
+
+ $this->pdo = new PDO($dsn, null, null, $opt);
+
+ if(!$exists) $this->init();
+ }
+
+ private function init()
+ {
+ $this->logger->proclog("Initialize new database '" . $this->path . "'");
+
+ $this->query_from_file(__DIR__."/db_init.sql");
+ }
+
+ public function close()
+ {
+ $this->pdo = null; // https://stackoverflow.com/questions/18277233
+ }
+
+ /** @param $path string */
+ public function query_from_file(string $path)
+ {
+ $sql = file_get_contents($path);
+
+ $cmds = explode("/*----SPLIT----*/", $sql);
+
+ foreach ($cmds as $cmd) $this->pdo->exec($cmd);
+ }
+
+ public function sql_query_assoc(string $query)
+ {
+ $r = $this->pdo->query($query)->fetchAll(PDO::FETCH_ASSOC);
+
+ return $r;
+ }
+
+ public function sql_query_assoc_prep(string $query, array $params)
+ {
+ $stmt = $this->pdo->prepare($query);
+
+ foreach ($params as $p)
+ {
+ if (strpos($query, $p[0]) !== FALSE) $stmt->bindValue($p[0], $p[1], $p[2]);
+ }
+
+ $stmt->execute();
+ $r = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ return $r;
+ }
+
+ public function sql_exec_prep(string $query, array $params)
+ {
+ $stmt = $this->pdo->prepare($query);
+
+ foreach ($params as $p)
+ {
+ if (strpos($query, $p[0]) !== FALSE) $stmt->bindValue($p[0], $p[1], $p[2]);
+ }
+
+ $stmt->execute();
+
+ return $stmt->rowCount();
+ }
+
+ /**
+ * @param string $url
+ * @param string $name
+ * @param string $source
+ * @return Repository
+ * @throws Exception
+ */
+ public function getOrCreateRepository(string $url, string $name, string $source)
+ {
+ $repo = $this->sql_query_assoc_prep("SELECT * FROM repositories WHERE url = :url",
+ [
+ [":url", $url, PDO::PARAM_STR],
+ ]);
+
+ if (count($repo) === 0)
+ {
+ $this->sql_exec_prep("INSERT INTO repositories (url, name, source, last_update, last_change) VALUES (:url, :nam, :src, :lu, :lc)",
+ [
+ [":url", $url, PDO::PARAM_STR],
+ [":nam", $name, PDO::PARAM_STR],
+ [":src", $source, PDO::PARAM_STR],
+ [":lu", Utils::sqlnow(), PDO::PARAM_STR],
+ [":lc", Utils::sqlnow(), PDO::PARAM_STR],
+ ]);
+
+ $repo = $this->sql_query_assoc_prep("SELECT * FROM repositories WHERE url = :url",
+ [
+ [":url", $url, PDO::PARAM_STR],
+ ]);
+
+ if (count($repo) === 0) throw new Exception("No repo after insert [" . $source . "|" . $name . "]");
+
+ $this->logger->proclog("Inserted (new) repository [" . $source . "|" . $name . "] into database");
+ }
+
+ $r = new Repository();
+ $r->ID = $repo[0]['id'];
+ $r->URL = $repo[0]['url'];
+ $r->Name = $repo[0]['name'];
+ $r->Source = $repo[0]['source'];
+ $r->LastUpdate = $repo[0]['last_update'];
+ $r->LastChange = $repo[0]['last_change'];
+ return $r;
+ }
+
+ /**
+ * @param string $source
+ * @param Repository $repo
+ * @param string $name
+ * @return Branch
+ * @throws Exception
+ */
+ public function getOrCreateBranch(string $source, Repository $repo, string $name)
+ {
+ $branch = $this->sql_query_assoc_prep("SELECT * FROM branches WHERE repo_id = :rid AND name = :nam",
+ [
+ [":rid", $repo->ID, PDO::PARAM_INT],
+ [":nam", $name, PDO::PARAM_STR],
+ ]);
+
+ if (count($branch) === 0)
+ {
+ $this->sql_exec_prep("INSERT INTO branches (repo_id, name, head, last_update, last_change) VALUES (:rid, :nam, :sha, :lu, :lc)",
+ [
+ [":rid", $repo->ID, PDO::PARAM_INT],
+ [":nam", $name, PDO::PARAM_STR],
+ [":sha", null, PDO::PARAM_STR],
+ [":lu", Utils::sqlnow(), PDO::PARAM_STR],
+ [":lc", Utils::sqlnow(), PDO::PARAM_STR],
+ ]);
+
+ $branch = $this->sql_query_assoc_prep("SELECT * FROM branches WHERE repo_id = :rid AND name = :nam",
+ [
+ [":rid", $repo->ID, PDO::PARAM_INT],
+ [":nam", $name, PDO::PARAM_STR],
+ ]);
+
+ if (count($branch) === 0) throw new Exception("No branch after insert [" . $source . "|" . $repo->Name . "|" . $name . "]");
+
+ $this->logger->proclog("Inserted (new) branch [" . $source . "|" . $repo->Name . "|" . $name . "] into database");
+ }
+
+ $r = new Branch();
+ $r->ID = $branch[0]['id'];
+ $r->Name = $branch[0]['name'];
+ $r->Repo = $repo;
+ $r->Head = $branch[0]['head'];
+ $r->LastUpdate = $branch[0]['last_update'];
+ $r->LastChange = $branch[0]['last_change'];
+ return $r;
+ }
+
+ /**
+ * @param string $source
+ * @param Repository $repo
+ * @param Branch $branch
+ * @param Commit[] $commits
+ */
+ public function insertNewCommits(string $source, Repository $repo, Branch $branch, array $commits) {
+ $this->logger->proclog("Inserted " . count($commits) . " (new) commits into [" . $source . "|" . $repo->Name . "|" . $branch->Name . "]");
+
+ foreach ($commits as $commit)
+ {
+ $strparents = implode(";", $commit->Parents);
+
+ $this->sql_exec_prep("INSERT INTO commits ([branch_id], [hash], [author_name], [author_email], [committer_name], [committer_email], [message], [date], [parent_commits]) VALUES (:brid, :sha, :an, :am, :cn, :cm, :msg, :dat, :prt)",
+ [
+ [":brid", $branch->ID, PDO::PARAM_INT],
+ [":sha", $commit->Hash, PDO::PARAM_STR],
+ [":an", $commit->AuthorName, PDO::PARAM_STR],
+ [":am", $commit->AuthorEmail, PDO::PARAM_STR],
+ [":cn", $commit->CommitterName, PDO::PARAM_STR],
+ [":cm", $commit->CommitterEmail, PDO::PARAM_STR],
+ [":msg", $commit->Message, PDO::PARAM_STR],
+ [":dat", $commit->Date, PDO::PARAM_STR],
+ [":prt", $strparents, PDO::PARAM_STR],
+ ]);
+
+ $dbid = $this->sql_query_assoc_prep("SELECT id FROM commits WHERE [branch_id] = :brid AND [Hash] = :sha",
+ [
+ [":brid", $branch->ID, PDO::PARAM_INT],
+ [":sha", $commit->Hash, PDO::PARAM_STR],
+ ]);
+
+ $commit->ID = $dbid[0]['id'];
+ }
+ }
+
+ /**
+ * @param Branch $branch
+ * @param string $head
+ */
+ public function setBranchHead(Branch $branch, string $head) {
+ $this->sql_exec_prep("UPDATE branches SET head = :head WHERE id = :id",
+ [
+ [":id", $branch->ID, PDO::PARAM_INT],
+ [":head", $head, PDO::PARAM_STR],
+ ]);
+
+ $branch->Head = $head;
+ }
+
+ public function setUpdateDateOnRepository(Repository $repo) {
+ $now = Utils::sqlnow();
+ $this->sql_exec_prep("UPDATE repositories SET last_update = :now WHERE id = :id",
+ [
+ [":id", $repo->ID, PDO::PARAM_INT],
+ [":now", $now, PDO::PARAM_STR],
+ ]);
+ $repo->LastUpdate = $now;
+ }
+
+ public function setChangeDateOnRepository(Repository $repo) {
+ $now = Utils::sqlnow();
+ $this->sql_exec_prep("UPDATE repositories SET last_change = :now WHERE id = :id",
+ [
+ [":id", $repo->ID, PDO::PARAM_INT],
+ [":now", $now, PDO::PARAM_STR],
+ ]);
+ $repo->LastChange = $now;
+ }
+
+ public function setUpdateDateOnBranch(Branch $branch) {
+ $now = Utils::sqlnow();
+ $this->sql_exec_prep("UPDATE branches SET last_update = :now WHERE id = :id",
+ [
+ [":id", $branch->ID, PDO::PARAM_INT],
+ [":now", $now, PDO::PARAM_STR],
+ ]);
+ $branch->LastUpdate = $now;
+ }
+
+ public function setChangeDateOnBranch(Branch $branch) {
+ $now = Utils::sqlnow();
+ $this->sql_exec_prep("UPDATE branches SET last_change = :now WHERE id = :id",
+ [
+ [":id", $branch->ID, PDO::PARAM_INT],
+ [":now", $now, PDO::PARAM_STR],
+ ]);
+ $branch->LastChange = $now;
+ }
+
+ /**
+ * @param string $source
+ * @param Repository $repo
+ * @param Branch[] $branches
+ */
+ public function deleteOtherBranches(string $source, Repository $repo, array $branches)
+ {
+ $db = $this->sql_query_assoc_prep("SELECT id, repo_id, name FROM branches WHERE repo_id = :rid",
+ [
+ [":rid", $repo->ID, PDO::PARAM_STR]
+ ]);
+
+ foreach ($db as $dbname)
+ {
+ $exist = false;
+ foreach ($branches as $brnch) if ($brnch->ID === $dbname['id']) $exist = true;
+
+ if (!$exist) $this->deleteBranchRecursive($source, $repo->Name, $dbname['name'], $dbname['id']);
+ }
+ }
+
+ /**
+ * @param string $source
+ * @param Repository[] $repos
+ */
+ public function deleteOtherRepositories(string $source, array $repos)
+ {
+ $db = $this->sql_query_assoc_prep("SELECT id, url, name FROM repositories WHERE source = :src",
+ [
+ [":src", $source, PDO::PARAM_STR]
+ ]);
+
+ foreach ($db as $dbname)
+ {
+ $exist = false;
+ foreach ($repos as $rep) if ($rep->ID === $dbname['id']) $exist = true;
+
+ if (!$exist) $this->deleteRepositoryRecursive($source, $dbname['name'], $dbname['id']);
+ }
+ }
+
+ /**
+ * @param string $source
+ * @param string $name
+ * @param int $id
+ */
+ private function deleteRepositoryRecursive(string $source, string $name, int $id)
+ {
+ $this->logger->proclog("Delete repository [".$source."|" . $name . "] from database (no longer exists)");
+
+ $branches = $this->sql_query_assoc_prep("SELECT id FROM branches WHERE repo_id = :rid", [ [":rid", $id, PDO::PARAM_INT] ]);
+
+ $this->sql_exec_prep("DELETE FROM repositories WHERE id = :id", [[":id", $id, PDO::PARAM_INT]]);
+ $this->sql_exec_prep("DELETE FROM branches WHERE repo_id = :rid", [[":rid", $id, PDO::PARAM_INT]]);
+ foreach ($branches as $branch) $this->sql_exec_prep("DELETE FROM commits WHERE branch_id = :bid", [[":bid", $branch["id"], PDO::PARAM_INT]]);
+ }
+
+ /**
+ * @param string $source
+ * @param string $reponame
+ * @param string $name
+ * @param int $id
+ */
+ private function deleteBranchRecursive(string $source, string $reponame, string $name, int $id)
+ {
+ $this->logger->proclog("Delete branch [" . $source . "|" . $reponame . "|" . $name . "] from database (no longer exists)");
+
+ $this->sql_exec_prep("DELETE FROM branches WHERE id = :bid", [[":bid", $id, PDO::PARAM_INT]]);
+ $this->sql_exec_prep("DELETE FROM commits WHERE branch_id = :bid", [[":bid", $id, PDO::PARAM_INT]]);
+ }
+
+ /**
+ * @param Branch $branch
+ */
+ public function deleteAllCommits(Branch $branch) {
+ $this->sql_exec_prep("DELETE FROM commits WHERE branch_id = :bid", [[":bid", $branch->ID, PDO::PARAM_INT]]);
+ }
+
+ public function deleteOldSources(array $sources)
+ {
+ $dbsources = $this->sql_query_assoc_prep("SELECT source FROM repositories GROUP BY source", []);
+
+ foreach ($dbsources as $dbsrc)
+ {
+ $exist = false;
+ foreach ($sources as $src) if ($src === $dbsrc['source']) $exist=true;
+
+ if (!$exist)
+ {
+ $this->logger->proclog("Delete source [" . $dbsrc['source'] . "] from database (no longer exists)");
+ $repos = $this->sql_query_assoc_prep("SELECT source,name,id FROM repositories WHERE source = :src", [ [":src", $dbsrc['source'], PDO::PARAM_STR] ]);
+
+ foreach ($repos as $r) $this->deleteRepositoryRecursive($r['source'], $r['name'], $r['id']);
+ }
+ }
+ }
+
+ /**
+ * @param int $year
+ * @param string[] $identities
+ * @return array
+ */
+ public function getCommitCountOfYearByDate(int $year, array $identities): array
+ {
+ $sql = file_get_contents(__DIR__ . "/db_queryyear.sql");
+
+ $cond = "(1=0)";
+ $prep =
+ [
+ [":year", "".$year, PDO::PARAM_STR]
+ ];
+ $i=0;
+ foreach ($identities as $ident)
+ {
+ $cond .= " OR (mail1 = :_".$i."_)";
+ $prep []= [":_".$i."_", $ident, PDO::PARAM_STR];
+ $i++;
+ $cond .= " OR (mail2 = :_".$i."_)";
+ $prep []= [":_".$i."_", $ident, PDO::PARAM_STR];
+ $i++;
+ }
+
+ $sql = str_replace("/*{INDETITY_COND}*/", $cond, $sql);
+
+ $rows = $this->sql_query_assoc_prep($sql, $prep);
+
+ $r = [];
+ foreach ($rows as $row) $r[$row['commitdate']] = $row['count'];
+
+ return $r;
+ }
+
+ /**
+ * @return int[]
+ */
+ public function getAllYears(): array
+ {
+ $rows = $this->sql_query_assoc("SELECT d FROM (SELECT cast(strftime('%Y', commits.date) as decimal) AS d FROM commits) GROUP BY d ORDER BY d");
+ $r = [];
+ foreach ($rows as $row) $r []= $row['d'];
+ return $r;
+ }
+}
\ No newline at end of file
diff --git a/www/extern/egh/EGHRemoteConfig.php b/www/extern/egh/EGHRemoteConfig.php
deleted file mode 100644
index 1a11566..0000000
--- a/www/extern/egh/EGHRemoteConfig.php
+++ /dev/null
@@ -1,27 +0,0 @@
-Type = $typ;
- $this->URL = $url;
- $this->Author = $usr;
- $this->Param = $param;
- }
-
-}
\ No newline at end of file
diff --git a/www/extern/egh/EGHRenderer.php b/www/extern/egh/EGHRenderer.php
deleted file mode 100644
index 6258fad..0000000
--- a/www/extern/egh/EGHRenderer.php
+++ /dev/null
@@ -1,224 +0,0 @@
- ['#F5F5F5', '#DBDEE0', '#C2C7CB', '#AAB0B7', '#9099A2', '#77828E', '#5E6B79', '#455464', '#2C3E50'],
- 'standard' => ["#ebedf0", "#c6e48b", "#7bc96f", "#239a3b", "#196127"],
- 'modern' => ["#afaca8", "#d6e685", "#8cc665", "#44a340", "#1e6823"],
- 'gray' => ["#eeeeee", "#bdbdbd", "#9e9e9e", "#616161", "#212121"],
- 'red' => ["#eeeeee", "#ff7171", "#ff0000", "#b70000", "#830000"],
- 'blue' => ["#eeeeee", "#6bcdff", "#00a1f3", "#0079b7", "#003958"],
- 'purple' => ["#eeeeee", "#d2ace6", "#aa66cc", "#660099", "#4f2266"],
- 'orange' => ["#eeeeee", "#ffcc80", "#ffa726", "#fb8c00", "#e65100"],
- 'halloween' => ["#eeeeee", "#ffee4a", "#ffc501", "#fe9600", "#03001c"],
- ];
-
- const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
- const DAYS = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
-
- /* @var ExtendedGitGraph */
- private $owner;
- /* @var SingleCommitInfo[] */
- public $data;
- /* @var string */
- public $colorScheme = 'standard';
- /* @var int[] */
- public $yearList;
- /* @var array */
- public $commitMap; // date('Y-m-d') -> count
-
- public function __construct($egh) {
- $this->owner = $egh;
- }
-
- /**
- * @param $data SingleCommitInfo[]
- */
- public function init($data)
- {
- $this->data = $data;
-
- $this->yearList = $this->getYears($data);
- $this->owner->out("Found " . count($this->yearList) . " year to generate.");
-
- $this->commitMap = $this->generateCommitMap($data, $this->yearList);
- $this->owner->out("Commitmap with ".count($this->commitMap)." entries generated.");
- }
-
- /**
- * @param $data SingleCommitInfo[]
- * @return int[]
- */
- public function getYears($data) {
- $years = array();
-
- foreach ($data as $commit) {
- if(! in_array($commit->Timestamp->format('Y'), $years))
- $years[] = intval($commit->Timestamp->format('Y'));
- }
-
- asort($years);
-
- return $years;
- }
-
- /**
- * @param $data SingleCommitInfo[]
- * @param $yearList int[]
- * @return array
- */
- private function generateCommitMap($data, $yearList)
- {
- $result = [];
-
- foreach ($yearList as $year)
- {
- $ymap = [];
-
- $date = new DateTime($year . '-01-01');
- while($date->format('Y') == $year)
- {
- $ymap[$date->format('Y-m-d')] = 0;
- $date = $date->modify("+1 day");
- }
-
- foreach ($data as $commit)
- {
- if(array_key_exists($commit->Timestamp->format('Y-m-d'), $ymap)) $ymap[$commit->Timestamp->format('Y-m-d')]++;
- }
-
- $result = array_merge($result, $ymap);
- }
-
-
- return $result;
- }
-
- /**
- * @param $year int
- * @return int
- */
- private function getMaxCommitCount($year)
- {
- $max = 0;
- foreach ($this->commitMap as $date => $count) if (Utils::startsWith($date, strval($year))) $max = max($max, $count);
- return $max;
- }
-
- /**
- * @param $year int
- * @return string
- */
- public function render($year)
- {
- $now = new DateTime();
- $date = new DateTime($year . '-01-01');
- $monthlist = array_fill(0, 12, [0, 0]);
- $colors = self::COLOR_SCHEMES[$this->colorScheme];
-
- $ymapmax = $this->getMaxCommitCount($year);
- $exponent = log(0.98/(count($colors)-1), 1/$ymapmax); // (1/max)^n = 0.98 // => 1 commit erreicht immer genau die erste stufe
-
- $html = '';
-
- $html .= '
' . "\n";
- $html .= '
' . "\n";
- $html .= '
' . "\n";
- $html .= ' ' . "\n";
- $html .= '
' . "\n";
- $html .= '' . "\n";
- $html .= '
' . "\n";
-
-
- return $html;
- }
-}
\ No newline at end of file
diff --git a/www/extern/egh/ExtendedGitGraph.php b/www/extern/egh/ExtendedGitGraph.php
deleted file mode 100644
index 4f7e5a8..0000000
--- a/www/extern/egh/ExtendedGitGraph.php
+++ /dev/null
@@ -1,189 +0,0 @@
-filenamecache = $filename_cache;
- $this->remoteconfigs = [];
- $this->ConnectionGithub = new ConnectionGithub($this);
- $this->ConnectionGitea = new ConnectionGitea($this);
- $this->outputMode = $outmode;
- $this->logFilePath = $logfile;
- }
-
- public function addRemote($type, $url, $user, $param) {
- $this->remoteconfigs []= new EGHRemoteConfig($type, $url, $user, $param);
- }
-
- public function setColorScheme($s) {
- $this->colorScheme = $s;
- }
-
- public function out($txt)
- {
- if ($txt !== '') $txt = '[' . date('H:i:s') . '] ' . $txt;
-
- if ($this->outputMode === self::OUT_SESSION)
- {
- if (session_status() !== PHP_SESSION_ACTIVE) session_start();
-
- $_SESSION[self::PROGRESS_SESSION_COOKIE] .= $txt . "\r\n";
- session_commit();
- }
-
- print $txt;
- print "\r\n";
-
- $logfile = Utils::sharpFormat($this->logFilePath, ['num'=>'']);
- file_put_contents($logfile, $txt.PHP_EOL , FILE_APPEND | LOCK_EX);
- }
-
- public function init()
- {
- if ($this->outputMode === self::OUT_SESSION)
- {
- if (session_status() !== PHP_SESSION_ACTIVE) session_start();
- $_SESSION[self::PROGRESS_SESSION_COOKIE] = '';
- session_commit();
- }
-
- $f3 = Utils::sharpFormat($this->logFilePath, ['num'=>'_3']);
- $f2 = Utils::sharpFormat($this->logFilePath, ['num'=>'_2']);
- $f1 = Utils::sharpFormat($this->logFilePath, ['num'=>'_1']);
- $f0 = Utils::sharpFormat($this->logFilePath, ['num'=>'' ]);
-
- if (file_exists($f3)) @unlink($f3);
- if (file_exists($f2)) @rename($f2, $f3);
- if (file_exists($f1)) @rename($f1, $f2);
- if (file_exists($f0)) @rename($f0, $f1);
- if (file_exists($f0)) @unlink($f0);
-
- $this->out('EXTENDED_GIT_GRAPH started');
- $this->out('');
- }
-
- public function updateFromRemotes()
- {
- $data = [];
-
- foreach ($this->remoteconfigs as $cfg)
- {
- if ($cfg->Type === 'github-user')
- $data = array_merge($data, $this->ConnectionGithub->getDataUser($cfg));
- else if ($cfg->Type === 'github-repository')
- $data = array_merge($data, $this->ConnectionGithub->getDataRepository($cfg));
- else if ($cfg->Type === 'gitea-user')
- $data = array_merge($data, $this->ConnectionGitea->getDataUser($cfg));
- else if ($cfg->Type === 'gitea-repository')
- $data = array_merge($data, $this->ConnectionGitea->getDataRepository($cfg));
- else
- $this->out("Unknown type: " . $cfg->Type);
- }
-
- $this->out("Found " . count($data) . " commits.");
-
- file_put_contents($this->filenamecache, serialize($data));
-
- $this->queriedData = $data;
-
- }
-
- public function updateFromCache()
- {
- if (file_exists($this->filenamecache))
- $this->queriedData = unserialize(file_get_contents($this->filenamecache));
- else
- $this->queriedData = [];
- }
-
- public function generate()
- {
- $renderer = new EGHRenderer($this);
- $renderer->colorScheme = $this->colorScheme;
-
- $renderer->init($this->queriedData);
-
- $this->renderedHTML = [];
- foreach ($renderer->yearList as $y) $this->renderedHTML[$y] = $renderer->render($y);
- }
-
- /**
- * @param $url string
- * @return array|mixed
- */
- public function getJSON($url) {
- if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
- $options =
- [
- 'http' => ['user_agent'=> $_SERVER['HTTP_USER_AGENT']],
- 'https' => ['user_agent'=> $_SERVER['HTTP_USER_AGENT']],
- ];
- } else {
- $options =
- [
- 'http' => ['user_agent'=> 'ExtendedGitGraph_for_mikescher.com'],
- 'https' => ['user_agent'=> 'ExtendedGitGraph_for_mikescher.com'],
- ];
- }
-
- $context = stream_context_create($options);
-
- $response = @file_get_contents($url, false, $context);
-
- if ($response === false)
- {
- $this->out("Error recieving json: '" . $url . "'");
- return [];
- }
-
- return json_decode($response);
- }
-
- public function get()
- {
- return $this->renderedHTML;
- }
-
- public function getAll()
- {
- $all = '';
- foreach ($this->get() as $year => $html) $all .= $html . "\n";
- return $all;
- }
-}
\ No newline at end of file
diff --git a/www/extern/egh/ExtendedGitGraph2.php b/www/extern/egh/ExtendedGitGraph2.php
new file mode 100644
index 0000000..355922e
--- /dev/null
+++ b/www/extern/egh/ExtendedGitGraph2.php
@@ -0,0 +1,145 @@
+logger = [];
+ if ($config['output_session']) $this->logger []= new SessionLogger($config['session_var']);
+ if ($config['output_stdout']) $this->logger []= new OutputLogger();
+ if ($config['output_logfile']) $this->logger []= new FileLogger($config['logfile'], $config['logfile_count']);
+
+ $this->sources = [];
+
+ $sourcenames = [];
+ foreach ($config['remotes'] as $rmt)
+ {
+ $newsrc = null;
+ if ($rmt['type'] === 'github')
+ $newsrc = new GithubConnection($this, $rmt['name'], $rmt['url'], $rmt['filter'], $rmt['exclusions'], $rmt['oauth_id'], $rmt['oauth_secret'], $rmt['token_cache'] );
+ else if ($rmt['type'] === 'gitea')
+ $newsrc = new GiteaConnection($this, $rmt['name'], $rmt['url'], $rmt['filter'], $rmt['exclusions'], $rmt['username'], $rmt['password'] );
+ else
+ throw new Exception("Unknown remote-type: " . $rmt['type']);
+
+ if (array_key_exists($newsrc->getName(), $sourcenames)) throw new Exception("Duplicate source name: " . $newsrc->getName());
+
+ $this->sources []= $newsrc;
+ $sourcenames []= $newsrc->getName();
+ }
+
+ $this->db = new EGGDatabase($config['data_cache_file'], $this);
+
+ $this->outputter = new FullRenderer($this, $config['identities'], $config['output_cache_files']);
+ }
+
+ public function update()
+ {
+ try
+ {
+ $this->db->open();
+
+ $this->proclog("Start incremental data update");
+ $this->proclog();
+
+ foreach ($this->sources as $src)
+ {
+ $this->proclog("======= UPDATE " . $src->getName() . " =======");
+
+ $src->update($this->db);
+
+ $this->proclog();
+ }
+
+ $this->db->deleteOldSources(array_map(function (IRemoteSource $v){ return $v->getName(); }, $this->sources));
+
+ $this->proclog("Update finished.");
+
+ $this->db->close();
+ }
+ catch (Exception $exception)
+ {
+ $this->proclog("(!) FATAL ERROR -- UNCAUGHT EXCEPTION THROWN");
+ $this->proclog();
+ $this->proclog($exception->getMessage());
+ $this->proclog();
+ $this->proclog($exception->getTraceAsString());
+ }
+ }
+
+ public function updateCache(): string
+ {
+ try
+ {
+ $this->db->open();
+
+ $this->proclog("Start update cache");
+ $this->proclog();
+
+ $data = $this->outputter->updateCache($this->db);
+
+ $this->db->close();
+ $this->proclog("UpdateCache finished.");
+
+ return $data;
+ }
+ catch (Exception $exception)
+ {
+ $this->proclog("(!) FATAL ERROR -- UNCAUGHT EXCEPTION THROWN");
+ $this->proclog();
+ $this->proclog($exception->getMessage());
+ $this->proclog();
+ $this->proclog($exception->getTraceAsString());
+ }
+ }
+
+ /**
+ * @return string|null
+ */
+ public function loadFromCache()
+ {
+ try
+ {
+ $this->db->open();
+
+ $data = $this->outputter->loadFromCache();
+
+ return $data;
+ }
+ catch (Exception $exception)
+ {
+ $this->proclog("(!) FATAL ERROR -- UNCAUGHT EXCEPTION THROWN");
+ $this->proclog();
+ $this->proclog($exception->getMessage());
+ $this->proclog();
+ $this->proclog($exception->getTraceAsString());
+ }
+ }
+
+ public function proclog($text = '')
+ {
+ foreach($this->logger as $lgr) $lgr->proclog($text);
+ }
+}
\ No newline at end of file
diff --git a/www/extern/egh/Logger.php b/www/extern/egh/Logger.php
new file mode 100644
index 0000000..727b011
--- /dev/null
+++ b/www/extern/egh/Logger.php
@@ -0,0 +1,82 @@
+0;$i--)
+ {
+ $f2 = Utils::sharpFormat($filename, [ 'num' => '_'.( $i ) ]);
+ $f1 = Utils::sharpFormat($filename, [ 'num' => '_'.( $i-1 ) ]);
+ if ($i-1 === 0) $f1 = Utils::sharpFormat($filename, [ 'num' => '' ]);
+
+ if (file_exists($f2)) @unlink($f2);
+ if (file_exists($f1)) @rename($f1, $f2);
+ }
+
+
+ $f0 = Utils::sharpFormat($filename, ['num'=>'' ]);
+ if (file_exists($f0)) @unlink($f0);
+ $this->path = $f0;
+ }
+
+ public function proclog($text)
+ {
+ if ($text !== '') $text = '[' . date('H:i:s') . '] ' . $text;
+
+ file_put_contents($this->path, $text . PHP_EOL , FILE_APPEND | LOCK_EX);
+ }
+}
+
+class SessionLogger implements ILogger
+{
+ /** @var string $sessionvar */
+ private $sessionvar;
+
+ /** @var string $sessionvar */
+ public function __construct($sessionvar)
+ {
+ $this->sessionvar = $sessionvar;
+
+ if (session_status() !== PHP_SESSION_DISABLED)
+ {
+ if (session_status() !== PHP_SESSION_ACTIVE) session_start();
+ $_SESSION[$sessionvar] = '';
+ session_commit();
+ }
+ }
+
+ public function proclog($text)
+ {
+ if (session_status() === PHP_SESSION_DISABLED) return;
+
+ $_SESSION[$this->sessionvar] .= $text . "\r\n";
+ session_commit();
+ }
+}
+
+class OutputLogger implements ILogger
+{
+ public function proclog($text)
+ {
+ if ($text !== '') $text = '[' . date('H:i:s') . '] ' . $text;
+
+ print $text;
+ print "\r\n";
+ }
+
+}
\ No newline at end of file
diff --git a/www/extern/egh/Models.php b/www/extern/egh/Models.php
new file mode 100644
index 0000000..c626633
--- /dev/null
+++ b/www/extern/egh/Models.php
@@ -0,0 +1,83 @@
+logger = $logger;
+ $this->identities = $identities;
+ $this->cache_files_path = $cfpath;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function updateCache(EGGDatabase $db)
+ {
+ $years = $db->getAllYears();
+ $dyears = [];
+
+ $result = "";
+ foreach ($years as $year)
+ {
+ $gen = new SingleYearRenderer($this->logger, $year, $this->identities, $this->cache_files_path);
+ $cc = $gen->updateCache($db);
+ if ($cc === null) continue;
+
+ $result .= $cc;
+ $result .= "\n\n\n";
+
+ $dyears []= $year;
+ }
+
+ $data = json_encode($dyears);
+
+ $path = Utils::sharpFormat($this->cache_files_path, ['ident' => 'fullrenderer']);
+
+ file_put_contents($path, $data);
+ $this->logger->proclog("Updated cache file for full renderer");
+
+ return $result;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function loadFromCache()
+ {
+ $path = Utils::sharpFormat($this->cache_files_path, ['ident' => 'fullrenderer']);
+ if (!file_exists($path))
+ {
+ $this->logger->proclog("No cache found for [fullrenderer]");
+ return null;
+ }
+
+ $years = json_decode(file_get_contents($path));
+
+ $result = "";
+
+ foreach ($years as $year)
+ {
+ $gen = new SingleYearRenderer($this->logger, $year, $this->identities, $this->cache_files_path);
+ $cc = $gen->loadFromCache();
+ if ($cc === null) return null;
+ $result .= $cc;
+ $result .= "\n\n\n";
+ }
+ return $result;
+ }
+}
+
+class SingleYearRenderer implements IOutputGenerator
+{
+ const DIST_X = 13;
+ const DIST_Y = 13;
+ const DAY_WIDTH = 11;
+ const DAY_HEIGHT = 11;
+
+ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+ const DAYS = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
+
+ /** @var ILogger $logger */
+ private $logger;
+
+ /** @var int $year */
+ private $year;
+
+ /** @var string[] $identities */
+ private $identities;
+
+ /** @var string $cache_files_path */
+ private $cache_files_path;
+
+ /**
+ * @param ILogger $logger
+ * @param int $year
+ * @param string[] $identities
+ * @param string $cfpath
+ */
+ public function __construct(ILogger $logger, int $year, array $identities, string $cfpath)
+ {
+ $this->logger = $logger;
+ $this->year = $year;
+ $this->identities = $identities;
+ $this->cache_files_path = $cfpath;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function loadFromCache()
+ {
+ $path = Utils::sharpFormat($this->cache_files_path, ['ident' => 'singleyear_'.$this->year]);
+ if (!file_exists($path))
+ {
+ $this->logger->proclog("No cache found for [".('singleyear_'.$this->year)."]");
+ return null;
+ }
+ return file_get_contents($path);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function updateCache(EGGDatabase $db)
+ {
+ $this->logger->proclog("Generate cache file for year ".$this->year);
+ $data = $this->generate($db);
+
+ if ($data === null) {
+ $this->logger->proclog("No data for year ".$this->year);
+ $this->logger->proclog("");
+ return null;
+ }
+
+ $path = Utils::sharpFormat($this->cache_files_path, ['ident' => 'singleyear_'.$this->year]);
+
+ file_put_contents($path, $data);
+ $this->logger->proclog("Updated cache file for year ".$this->year);
+ $this->logger->proclog("");
+
+ return $data;
+ }
+
+ /**
+ * @param EGGDatabase $db
+ * @return string|null
+ * @throws Exception
+ */
+ private function generate(EGGDatabase $db)
+ {
+ $dbdata = $db->getCommitCountOfYearByDate($this->year, $this->identities);
+
+ if (Utils::array_value_max(0, $dbdata) === 0) return null;
+
+ $now = new DateTime();
+ $date = new DateTime($this->year . '-01-01');
+ $ymapmax = Utils::array_value_max(1, $dbdata);
+
+ $monthlist = array_fill(0, 12, [0, 0]);
+
+ $exponent9 = log(0.98/((9)-1), 1/$ymapmax);
+ $exponent5 = log(0.98/((5)-1), 1/$ymapmax);
+
+
+ $html = '';
+
+ $html .= '' . "\n";
+ $html .= '
' . "\n";
+ $html .= '
' . "\n";
+ $html .= ' ' . "\n";
+ $html .= '
' . "\n";
+ $html .= '' . "\n";
+ $html .= '
' . "\n";
+
+
+ return $html;
+ }
+}
\ No newline at end of file
diff --git a/www/extern/egh/RemoteSource.php b/www/extern/egh/RemoteSource.php
new file mode 100644
index 0000000..558320d
--- /dev/null
+++ b/www/extern/egh/RemoteSource.php
@@ -0,0 +1,575 @@
+logger = $logger;
+ $this->name = $name;
+ $this->filter = $filter;
+ $this->exclusions = $exclusions;
+ }
+
+ /** @inheritDoc
+ * @throws Exception
+ */
+ public function update(EGGDatabase $db)
+ {
+ $this->preUpdate();
+
+ $repos = $this->listAndUpdateRepositories($db);
+
+ foreach ($repos as $repo)
+ {
+ $branches = $this->listAndUpdateBranches($db, $repo);
+ $db->setUpdateDateOnRepository($repo);
+
+ $repo_changed = false;
+ foreach ($branches as $branch)
+ {
+ if ($branch->HeadFromAPI === $branch->Head)
+ {
+ $db->setUpdateDateOnBranch($branch);
+ $this->logger->proclog("Branch: [" . $this->name . "|" . $repo->Name . "|" . $branch->Name . "] is up to date");
+ continue;
+ }
+
+ $commits = $this->listAndUpdateCommits($db, $repo, $branch);
+ $db->setUpdateDateOnBranch($branch);
+ if (count($commits) === 0)
+ {
+ $this->logger->proclog("Branch: [" . $this->name . "|" . $repo->Name . "|" . $branch->Name . "] has no new commits");
+ continue;
+ }
+
+ $this->logger->proclog("Found " . count($commits) . " new commits in Branch: [" . $this->name . "|" . $repo->Name . "|" . $branch->Name . "]");
+
+ $repo_changed = true;
+ $db->setChangeDateOnBranch($branch);
+ }
+
+ if ($repo_changed) $db->setChangeDateOnRepository($repo);
+ }
+
+ $this->postUpdate();
+ }
+
+ /**
+ * @throws Exception
+ */
+ protected abstract function preUpdate();
+
+ /**
+ * @throws Exception
+ */
+ protected abstract function postUpdate();
+
+ /**
+ * @param string $user
+ * @param int $page
+ * @return array
+ */
+ protected abstract function queryRepositories($user, $page);
+
+ /**
+ * @param string $reponame
+ * @return array
+ */
+ protected abstract function queryBranches($reponame);
+
+ /**
+ * @param string $reponame
+ * @param string $branchname
+ * @param string $startsha
+ * @return array
+ */
+ protected abstract function queryCommits($reponame, $branchname, $startsha);
+
+ /**
+ * @param mixed $data
+ * @return array
+ * @throws Exception
+ */
+ protected abstract function readRepository($data);
+
+ /**
+ * @param mixed $data
+ * @return array
+ * @throws Exception
+ */
+ protected abstract function readBranch($data);
+
+ /**
+ * @param mixed $data
+ * @return array
+ * @throws Exception
+ */
+ protected abstract function readCommit($data);
+
+ /**
+ * @param EGGDatabase $db
+ * @return Repository[]
+ * @throws Exception
+ */
+ private function listAndUpdateRepositories(EGGDatabase $db) {
+ $f = explode('/', $this->filter);
+
+ $result = [];
+
+ $page = 1;
+ $json = $this->queryRepositories($f[0], $page);
+
+ while (! empty($json))
+ {
+ foreach ($json as $result_repo)
+ {
+ $jdata = $this->readRepository($result_repo);
+
+ if (!Utils::isRepoFilterMatch($this->filter, $this->exclusions, $jdata['full_name']))
+ {
+ $this->logger->proclog("Skip Repo: " . $jdata['full_name']);
+ continue;
+ }
+
+ $this->logger->proclog("Found Repo in Remote: " . $jdata['full_name']);
+
+ $result []= $db->getOrCreateRepository($jdata['html_url'], $jdata['full_name'], $this->name);
+ }
+
+ $page++;
+ $json = $this->queryRepositories($f[0], $page);
+ }
+
+ $db->deleteOtherRepositories($this->name, $result);
+
+ return $result;
+ }
+
+ /**
+ * @param EGGDatabase $db
+ * @param Repository $repo
+ * @return Branch[]
+ * @throws Exception
+ */
+ private function listAndUpdateBranches(EGGDatabase $db, Repository $repo) {
+
+ $result = [];
+
+ $json = $this->queryBranches($repo->Name);
+
+ foreach ($json as $result_branch) {
+ $jdata = $this->readBranch($result_branch);
+
+ if ($jdata === null) continue;
+
+ $bname = $jdata['name'];
+ $bhead = $jdata['sha'];
+
+ $this->logger->proclog("Found Branch in Remote: [" . $repo->Name . "] " . $bname);
+
+ $b = $db->getOrCreateBranch($this->name, $repo, $bname);
+ $b->HeadFromAPI = $bhead;
+ $result []= $b;
+ }
+
+ $db->deleteOtherBranches($this->name, $repo, $result);
+
+ return $result;
+ }
+
+ /**
+ * @param EGGDatabase $db
+ * @param Repository $repo
+ * @param Branch $branch
+ * @return Commit[]
+ * @throws Exception
+ */
+ private function listAndUpdateCommits(EGGDatabase $db, Repository $repo, Branch $branch) {
+
+ $newcommits = [];
+
+ if ($branch->HeadFromAPI === null) return [];
+
+ $target = $branch->Head;
+
+ $next_sha = [ $branch->HeadFromAPI ];
+ $visited = [ $branch->HeadFromAPI ];
+
+ $json = $this->queryCommits($repo->Name, $branch->Name, $next_sha[0]);
+
+ for (;;)
+ {
+ foreach ($json as $result_commit)
+ {
+ $jdata = $this->readCommit($result_commit);
+
+ $sha = $jdata['sha'];
+ $author_name = $jdata['author_name'];
+ $author_email = $jdata['author_email'];
+ $committer_name = $jdata['committer_name'];
+ $committer_email = $jdata['committer_email'];
+ $message = $jdata['message'];
+ $date = $jdata['date'];
+
+ $parents = $jdata['parents'];
+
+ if (($rmshakey = array_search($sha, $next_sha)) !== false) unset($next_sha[$rmshakey]);
+
+ if (in_array($sha, $visited)) continue;
+ $visited []= $sha;
+
+ if ($sha === $target && count($next_sha) === 0)
+ {
+ if (count($newcommits) === 0)
+ {
+ $this->logger->proclog("Found no new commits for: [" . $this->name . "|" . $repo->Name . "|" . $branch->Name . "] (HEAD at {" . substr($branch->HeadFromAPI, 0, 8) . "})");
+ return [];
+ }
+
+ $db->insertNewCommits($this->name, $repo, $branch, $newcommits);
+ $db->setBranchHead($branch, $branch->HeadFromAPI);
+
+ return $newcommits;
+ }
+
+ $commit = new Commit();
+ $commit->Branch = $branch;
+ $commit->Hash = $sha;
+ $commit->AuthorName = $author_name;
+ $commit->AuthorEmail = $author_email;
+ $commit->CommitterName = $committer_name;
+ $commit->CommitterEmail = $committer_email;
+ $commit->Message = $message;
+ $commit->Date = $date;
+ $commit->Parents = $parents;
+
+ $newcommits []= $commit;
+
+ foreach ($parents as $p)
+ {
+ $next_sha []= $p;
+ }
+ }
+
+ $next_sha = array_values($next_sha); // fix numeric keys
+ if (count($next_sha) === 0) break;
+
+ $json = $this->queryCommits($repo->Name, $branch->Name, $next_sha[0]);
+ }
+
+ $this->logger->proclog("HEAD pointer in Branch: [" . $this->name . "|" . $repo->Name . "|" . $branch->Name . "] no longer matches. Re-query all " . count($newcommits) . " commits (old HEAD := {".substr($branch->Head, 0, 8)."})");
+
+ $db->deleteAllCommits($branch);
+
+ if (count($newcommits) === 0) return [];
+
+ $db->insertNewCommits($this->name, $repo, $branch, $newcommits);
+ $db->setBranchHead($branch, $branch->HeadFromAPI);
+
+ return $newcommits;
+ }
+
+ /** @inheritDoc */
+ public function getName() { return $this->name; }
+
+ /** @inheritDoc */
+ public abstract function toString();
+}
+
+class GithubConnection extends StandardGitConnection
+{
+ const API_OAUTH_AUTH = 'https://github.com/login/oauth/authorize?client_id=%s';
+ const URL_OAUTH_TOKEN = 'https://github.com/login/oauth/access_token?client_id={id}&client_secret={secret}&code={code}';
+
+ const API_RATELIMIT = 'https://api.github.com/rate_limit';
+ const API_REPOSITORIESLIST = 'https://api.github.com/users/{user}/repos?page={page}&per_page=100';
+ const API_COMMITSLIST = 'https://api.github.com/repos/{repo}/commits?per_page=100&sha={sha}';
+ const API_BRANCHLIST = 'https://api.github.com/repos/{repo}/branches';
+
+ /** @var string $url */
+ private $url;
+
+ /** @var string $oauth_id */
+ private $oauth_id;
+
+ /** @var string $oauth_secret */
+ private $oauth_secret;
+
+ /** @var string $apitokenpath */
+ private $apitokenpath;
+
+ /** @var string $apitoken */
+ private $apitoken;
+
+ /**
+ * @param ILogger $logger
+ * @param string $name
+ * @param string $url
+ * @param string $filter
+ * @param string[] exclusions
+ * @param string $oauth_id
+ * @param string $oauth_secret
+ * @param string $apitokenpath
+ */
+ public function __construct(ILogger $logger, string $name, string $url, string $filter, array $exclusions, string $oauth_id, string $oauth_secret, string $apitokenpath)
+ {
+ parent::__construct($logger, $name, $filter, $exclusions);
+
+ $this->url = $url;
+ $this->oauth_id = $oauth_id;
+ $this->oauth_secret = $oauth_secret;
+ $this->apitokenpath = $apitokenpath;
+
+ if ($this->apitokenpath !== null && file_exists($this->apitokenpath))
+ $this->apitoken = file_get_contents($this->apitokenpath);
+ else
+ $this->apitoken = null;
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function queryAPIToken() {
+ $url = Utils::sharpFormat(self::URL_OAUTH_TOKEN, ['id'=>$this->oauth_id, 'secret'=>$this->oauth_secret, 'code'=>'egg']);
+ $fullresult = $result = file_get_contents($url);
+
+ $result = str_replace('access_token=', '', $result);
+ $result = str_replace('&scope=&token_type=bearer', '', $result);
+
+ $this->logger->proclog("Updated Github API token");
+
+ if (Utils::startsWith($result, "error=")) throw new Exception($fullresult);
+
+ if ($result!=='' && $result !== null && $this->apitokenpath !== null)
+ file_put_contents($this->apitokenpath, $result);
+
+ $this->apitoken = $result;
+ }
+
+ /** @inheritDoc */
+ protected function preUpdate()
+ {
+ if ($this->apitoken === null) $this->queryAPIToken();
+ }
+
+ /** @inheritDoc */
+ protected function postUpdate()
+ {
+ //
+ }
+
+ /** @inheritDoc */
+ protected function queryRepositories($user, $page)
+ {
+ $url = Utils::sharpFormat(self::API_REPOSITORIESLIST, ['user'=>$user, 'page'=>$page]);
+ return Utils::getJSONWithTokenAuth($this->logger, $url, $this->apitoken);
+ }
+
+ /** @inheritDoc */
+ protected function queryBranches($reponame)
+ {
+ $url = Utils::sharpFormat(self::API_BRANCHLIST, ['repo'=>$reponame]);
+ return Utils::getJSONWithTokenAuth($this->logger, $url, $this->apitoken);
+ }
+
+ /** @inheritDoc */
+ protected function queryCommits($reponame, $branchname, $startsha)
+ {
+ $url = Utils::sharpFormat(self::API_COMMITSLIST, [ 'repo'=>$reponame, 'sha'=>$startsha ]);
+ $this->logger->proclog("Query commits from: [" . $this->name . "|" . $reponame . "|" . $branchname . "] continuing at {" . substr($startsha, 0, 8) . "}");
+ return Utils::getJSONWithTokenAuth($this->logger, $url, $this->apitoken);
+ }
+
+ /** @inheritDoc */
+ protected function readRepository($data)
+ {
+ return
+ [
+ 'full_name' => $data->{'full_name'},
+ 'html_url' => $data->{'html_url'},
+ ];
+ }
+
+ /** @inheritDoc */
+ protected function readBranch($data)
+ {
+ if (isset($data->{'block'})) return null;
+
+ return
+ [
+ 'name' => $data->{'name'},
+ 'sha' => $data->{'commit'}->{'sha'},
+ ];
+ }
+
+ /** @inheritDoc */
+ protected function readCommit($data)
+ {
+ return
+ [
+ 'sha' => $data->{'sha'},
+
+ 'author_name' => $data->{'commit'}->{'author'}->{'name'},
+ 'author_email' => $data->{'commit'}->{'author'}->{'email'},
+
+ 'committer_name' => $data->{'commit'}->{'committer'}->{'name'},
+ 'committer_email' => $data->{'commit'}->{'committer'}->{'email'},
+
+ 'message' => $data->{'commit'}->{'message'},
+ 'date' => (new DateTime($data->{'commit'}->{'author'}->{'date'}))->format("Y-m-d H:i:s"),
+
+ 'parents' => array_map(function ($v){ return $v->{'sha'}; }, $data->{'parents'}),
+ ];
+ }
+
+ /** @inheritDoc */
+ public function toString() { return "[Github|".$this->filter."]"; }
+}
+
+class GiteaConnection extends StandardGitConnection
+{
+ const API_BASE_URL = '/api/v1';
+
+ const API_USER_REPO_LIST = '/users/{user}/repos';
+ const API_BRANCH_LIST = '/repos/{repo}/branches';
+ const API_COMMIT_LIST = '/repos/{repo}/commits?sha={sha}';
+
+ /** @var string $url */
+ private $url;
+
+ /** @var string $username */
+ private $username;
+
+ /** @var string $password */
+ private $password;
+
+ /**
+ * @param ILogger $logger
+ * @param string $name
+ * @param string $url
+ * @param string $filter
+ * @param string[] $exclusions
+ * @param string $username
+ * @param string $password
+ */
+ public function __construct(ILogger $logger, string $name, string $url, string $filter, array $exclusions, string $username, string $password)
+ {
+ parent::__construct($logger, $name, $filter, $exclusions);
+
+ $this->url = $url;
+ $this->username = $username;
+ $this->password = $password;
+ }
+
+ /** @inheritDoc */
+ protected function preUpdate()
+ {
+ //
+ }
+
+ /** @inheritDoc */
+ protected function postUpdate()
+ {
+ //
+ }
+
+ /** @inheritDoc */
+ protected function queryRepositories($user, $page)
+ {
+ if ($page > 1) return [];
+ $url = Utils::sharpFormat(Utils::urlCombine($this->url, self::API_BASE_URL, self::API_USER_REPO_LIST), ['user'=>$user ]);
+ return Utils::getJSONWithTokenBasicAuth($this->logger, $url, $this->username, $this->password);
+ }
+
+ /** @inheritDoc */
+ protected function queryBranches($reponame)
+ {
+ $url = Utils::sharpFormat(Utils::urlCombine($this->url, self::API_BASE_URL, self::API_BRANCH_LIST), ['repo'=>$reponame]);
+ return Utils::getJSONWithTokenBasicAuth($this->logger, $url, $this->username, $this->password);
+ }
+
+ /** @inheritDoc */
+ protected function queryCommits($reponame, $branchname, $startsha)
+ {
+ $url = Utils::sharpFormat(Utils::urlCombine($this->url, self::API_BASE_URL, self::API_COMMIT_LIST), [ 'repo'=>$reponame, 'sha'=>$startsha ]);
+ $this->logger->proclog("Query commits from: [" . $this->name . "|" . $reponame . "|" . $branchname . "] continuing at {" . substr($startsha, 0, 8) . "}");
+ return Utils::getJSONWithTokenBasicAuth($this->logger, $url, $this->username, $this->password);
+ }
+
+ /** @inheritDoc */
+ protected function readRepository($data)
+ {
+ return
+ [
+ 'full_name' => $data->{'full_name'},
+ 'html_url' => $data->{'html_url'},
+ ];
+ }
+
+ /** @inheritDoc */
+ protected function readBranch($data)
+ {
+ return
+ [
+ 'name' => $data->{'name'},
+ 'sha' => $data->{'commit'}->{'id'},
+ ];
+ }
+
+ /** @inheritDoc */
+ protected function readCommit($data)
+ {
+ return
+ [
+ 'sha' => $data->{'commit'}->{'tree'}->{'sha'},
+
+ 'author_name' => $data->{'commit'}->{'author'}->{'name'},
+ 'author_email' => $data->{'commit'}->{'author'}->{'email'},
+
+ 'committer_name' => $data->{'commit'}->{'committer'}->{'name'},
+ 'committer_email' => $data->{'commit'}->{'committer'}->{'email'},
+
+ 'message' => $data->{'commit'}->{'message'},
+ 'date' => (new DateTime($data->{'commit'}->{'author'}->{'date'}))->format("Y-m-d H:i:s"),
+
+ 'parents' => array_map(function ($v){ return $v->{'sha'}; }, $data->{'parents'}),
+ ];
+ }
+
+ /** @inheritDoc */
+ public function toString() { return "[Gitea|".$this->url."|".$this->filter."]"; }
+}
\ No newline at end of file
diff --git a/www/extern/egh/SingleCommitInfo.php b/www/extern/egh/SingleCommitInfo.php
deleted file mode 100644
index 8da17bb..0000000
--- a/www/extern/egh/SingleCommitInfo.php
+++ /dev/null
@@ -1,31 +0,0 @@
-Timestamp = $ts;
- $this->SourcePlatform = $src;
- $this->SourceUser = $usr;
- $this->SourceRepository = $repo;
- }
-
-
-}
\ No newline at end of file
diff --git a/www/extern/egh/Utils.php b/www/extern/egh/Utils.php
index 99ddaea..ae8002f 100644
--- a/www/extern/egh/Utils.php
+++ b/www/extern/egh/Utils.php
@@ -2,7 +2,12 @@
class Utils
{
- public static function sharpFormat($str, $args)
+ /**
+ * @param string $str
+ * @param string[] $args
+ * @return string
+ */
+ public static function sharpFormat(string $str, array $args)
{
foreach ($args as $key => $val)
{
@@ -11,9 +16,164 @@ class Utils
return $str;
}
- public static function startsWith($haystack, $needle)
+ /**
+ * @param string $haystack
+ * @param string $needle
+ * @return bool
+ */
+ public static function startsWith(string $haystack, string $needle)
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
-}
\ No newline at end of file
+
+ /**
+ * @param string $haystack
+ * @param string $needle
+ * @return bool
+ */
+ public static function endsWith(string $haystack, string $needle)
+ {
+ $length = strlen($needle);
+ return ($length === 0) || (substr($haystack, -$length) === $needle);
+ }
+
+ /**
+ * @param string $filter
+ * @param string[] $exclusions
+ * @param string $name
+ * @return bool
+ */
+ public static function isRepoFilterMatch(string $filter, array $exclusions, string $name)
+ {
+ foreach ($exclusions as $ex)
+ {
+ if (strtolower($ex) === strtolower($name)) return false;
+ }
+
+ $f0 = explode('/', $filter);
+ $f1 = explode('/', $name);
+
+ if (count($f0) !== 2) return false;
+ if (count($f1) !== 2) return false;
+
+ if ($f0[0] !== $f1[0] && $f0[0] !== '*') return false;
+ if ($f0[1] !== $f1[1] && $f0[1] !== '*') return false;
+
+ return true;
+ }
+
+ /**
+ * @param ILogger $logger
+ * @param string $url
+ * @param string $authtoken
+ * @return array|mixed
+ */
+ public static function getJSONWithTokenAuth($logger, $url, $authtoken)
+ {
+ return Utils::getJSON($logger, $url, 'Authorization: token ' . $authtoken);
+ }
+
+ /**
+ * @param ILogger $logger
+ * @param string $url
+ * @param string $usr
+ * @param string $pass
+ * @return array|mixed
+ */
+ public static function getJSONWithTokenBasicAuth($logger, $url, $usr, $pass)
+ {
+ return Utils::getJSON($logger, $url, 'Authorization: Basic ' . base64_encode($usr.':'.$pass));
+ }
+
+ /**
+ * @param ILogger $logger
+ * @param string $url
+ * @param string $header
+ * @return array|mixed
+ */
+ private static function getJSON($logger, $url, $header)
+ {
+ //$logger->proclog("[@] " . $url);
+
+ if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
+ $options =
+ [
+ 'http' =>
+ [
+ 'user_agent' => $_SERVER['HTTP_USER_AGENT'],
+ 'header' => $header,
+ ],
+ 'https' =>
+ [
+ 'user_agent' => $_SERVER['HTTP_USER_AGENT'],
+ 'header' => $header,
+ ],
+ ];
+ } else {
+ $options =
+ [
+ 'http' =>
+ [
+ 'user_agent' => 'ExtendedGitGraph_for_mikescher.com',
+ 'header' => $header,
+ 'ignore_errors' => true,
+ ],
+ 'https' =>
+ [
+ 'user_agent' => 'ExtendedGitGraph_for_mikescher.com',
+ 'header' => $header,
+ 'ignore_errors' => true,
+ ],
+ ];
+ }
+
+ $context = stream_context_create($options);
+
+ $response = @file_get_contents($url, false, $context);
+
+ if ($response === false)
+ {
+ $logger->proclog("Error recieving json: '" . $url . "'");
+ $logger->proclog(print_r(error_get_last(), true));
+ return [];
+ }
+
+ return json_decode($response);
+ }
+
+ /**
+ * @return string
+ */
+ public static function sqlnow()
+ {
+ return gmdate("Y-m-d H:i:s");
+ }
+
+ /**
+ * @param int $n0
+ * @param array $dbdata
+ * @return int
+ */
+ public static function array_value_max(int $n0, array $dbdata): int
+ {
+ foreach ($dbdata as $_ => $val) $n0 = max($n0, $val);
+ return $n0;
+ }
+
+ public static function urlCombine(string... $elements)
+ {
+ $r = $elements[0];
+ $skip = true;
+ foreach ($elements as $e)
+ {
+ if ($skip) { $skip=false; continue; }
+
+ if (Utils::endsWith($r, '/')) $r = substr($r, 0, strlen($r)-1);
+ if (Utils::startsWith($e, '/')) $e = substr($e, 1);
+
+ $r = $r . '/' . $e;
+ }
+ return $r;
+ }
+}
diff --git a/www/extern/egh/db_init.sql b/www/extern/egh/db_init.sql
new file mode 100644
index 0000000..839755b
--- /dev/null
+++ b/www/extern/egh/db_init.sql
@@ -0,0 +1,37 @@
+CREATE TABLE "repositories"
+(
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
+ "source" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "url" TEXT NOT NULL UNIQUE,
+ "last_update" TEXT NOT NULL,
+ "last_change" TEXT NOT NULL
+);
+
+/*----SPLIT----*/
+
+CREATE TABLE "branches"
+(
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
+ "repo_id" INTEGER NOT NULL,
+ "name" TEXT NOT NULL,
+ "head" TEXT,
+ "last_update" TEXT NOT NULL,
+ "last_change" TEXT NOT NULL
+);
+
+/*----SPLIT----*/
+
+CREATE TABLE "commits"
+(
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
+ "branch_id" INTEGER NOT NULL,
+ "hash" TEXT NOT NULL,
+ "author_name" TEXT NOT NULL,
+ "author_email" TEXT NOT NULL,
+ "committer_name" TEXT NOT NULL,
+ "committer_email" TEXT NOT NULL,
+ "message" TEXT NOT NULL,
+ "date" TEXT NOT NULL,
+ "parent_commits" TEXT NOT NULL
+);
diff --git a/www/extern/egh/db_queryyear.sql b/www/extern/egh/db_queryyear.sql
new file mode 100644
index 0000000..5d1c04c
--- /dev/null
+++ b/www/extern/egh/db_queryyear.sql
@@ -0,0 +1,10 @@
+SELECT commitdate AS commitdate, COUNT(*) as count FROM
+ (
+ SELECT
+ [hash] AS hash, min([author_email]) AS mail1, min([committer_email]) AS mail2, date(min([date])) AS commitdate
+ FROM COMMITS
+ GROUP BY hash
+ HAVING (strftime('%Y', commitdate) = :year AND (/*{INDETITY_COND}*/))
+ )
+GROUP BY commitdate
+ORDER BY commitdate
\ No newline at end of file
diff --git a/www/internals/mikeschergitgraph.php b/www/internals/mikeschergitgraph.php
index 070f85b..0b4d02d 100644
--- a/www/internals/mikeschergitgraph.php
+++ b/www/internals/mikeschergitgraph.php
@@ -1,41 +1,35 @@
addRemote('github-user', null, 'Mikescher', 'Mikescher');
- //$v->addRemote('github-user', null, 'Mikescher', 'Sam-Development');
- //$v->addRemote('github-repository', null, 'Mikescher', 'Anastron/ColorRunner');
- $v->addRemote('gitea-repository', null, 'Mikescher', 'Mikescher/server-scripts');
- $v->addRemote('gitea-repository', null, 'Mikescher', 'Mikescher/apache-sites');
- $v->addRemote('gitea-repository', null, 'Mikescher', 'Mikescher/MVU_API');
-
- $v->setColorScheme($CONFIG['egh_theme']);
-
- $v->ConnectionGithub->setAPIToken($CONFIG['egh_token']);
-
- $v->ConnectionGitea->setURL('https://gogs.mikescher.com');
-
- return $v;
+ return new ExtendedGitGraph2($CONFIG['extendedgitgraph']);
}
public static function getPathRenderedData()
{
- return __DIR__ . '/../dynamic/egh.html';
+ return __DIR__ . '/../dynamic/egg/cache_fullrenderer.html';
}
- public static function includeRender()
+ /**
+ * @return string|null
+ * @throws Exception
+ */
+ public static function get()
{
- if (file_exists(__DIR__ . '/../dynamic/egh.html'))
- include __DIR__ . '/../dynamic/egh.html';
+ $d = self::create()->loadFromCache();
+ if ($d === null) return "";
+ return $d;
}
public static function checkConsistency()
diff --git a/www/pages/about.php b/www/pages/about.php
index a092057..706750b 100644
--- a/www/pages/about.php
+++ b/www/pages/about.php
@@ -47,7 +47,7 @@ global $OPTIONS;
-
+