From 28ae8beb2de097f93132dc62432734f22f4d061a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Sun, 21 Jan 2018 15:29:01 +0100 Subject: [PATCH] /about/ + extendedGitGraph --- www/data/css/styles.css | 122 +++++++- www/data/css/styles.min.css | 24 +- www/data/css/styles.scss | 1 + www/data/css/styles_about.scss | 104 +++++++ www/data/css/styles_blogview.scss | 20 +- www/data/css/styles_global.scss | 18 ++ www/data/javascript/egh.js | 57 ++++ www/dynamic/.gitignore | 3 + www/dynamic/.gitkeep | 0 www/extern/egh/ConnectionGitea.php | 39 +++ www/extern/egh/ConnectionGithub.php | 166 +++++++++++ www/extern/egh/EGHRemoteConfig.php | 27 ++ www/extern/egh/EGHRenderer.php | 224 ++++++++++++++ www/extern/egh/ExtendedGitGraph.php | 184 ++++++++++++ www/extern/egh/SingleCommitInfo.php | 31 ++ www/extern/egh/Utils.php | 19 ++ www/fragments/befunge93_runner.php | 2 +- www/fragments/bfjoust_runner.php | 2 +- www/fragments/blogview_euler_list.php | 2 +- www/fragments/blogview_euler_single.php | 2 +- www/fragments/blogview_markdown.php | 2 +- www/fragments/blogview_plain.php | 2 +- www/index.php | 4 +- www/internals/base.php | 6 +- www/pages/about.php | 54 ++++ .../programs/ExtendedGitGraph_description.md | 279 +++++++++++++++++- 26 files changed, 1346 insertions(+), 48 deletions(-) create mode 100644 www/data/css/styles_about.scss create mode 100644 www/data/javascript/egh.js create mode 100644 www/dynamic/.gitignore create mode 100644 www/dynamic/.gitkeep create mode 100644 www/extern/egh/ConnectionGitea.php create mode 100644 www/extern/egh/ConnectionGithub.php create mode 100644 www/extern/egh/EGHRemoteConfig.php create mode 100644 www/extern/egh/EGHRenderer.php create mode 100644 www/extern/egh/ExtendedGitGraph.php create mode 100644 www/extern/egh/SingleCommitInfo.php create mode 100644 www/extern/egh/Utils.php create mode 100644 www/pages/about.php diff --git a/www/data/css/styles.css b/www/data/css/styles.css index 9b1a79d..1e1868d 100644 --- a/www/data/css/styles.css +++ b/www/data/css/styles.css @@ -89,6 +89,19 @@ body { width: 0 !important; height: 0 !important; } +.boxedcontent { + color: #333; + border: 1px solid black; + background-color: #E0E0E0; + width: 100%; + margin-left: auto; + margin-right: auto; } + .boxedcontent .bc_header { + background-color: #BBB; + padding: 0 4px; } + .boxedcontent .bc_data { + padding: 8px; } + /* 400px */ #headerdiv { z-index: 999; @@ -265,21 +278,6 @@ html, body { .ble_title { font-size: 1.25em; } } /* 400px */ -.blogcontent { - color: #333; - border: 1px solid black; - background-color: #E0E0E0; - width: 90%; - margin-left: auto; - margin-right: auto; } - -.bc_header { - background-color: #BBB; - padding: 0 4px; } - -.bc_data { - padding: 8px; } - .base_markdown code { font-family: Consolas, Monaco, "Courier New", Menlo, monospace; direction: ltr; @@ -348,6 +346,9 @@ html, body { border: none !important; background: transparent !important; } +.blogcontent_euler, .blogcontent_markdown, .blogcontent_plain { + width: 90%; } + /* 400px */ #PEB_tableProblems .PEB_tablerowProblems:hover { background-color: #888; } @@ -796,6 +797,97 @@ html, body { #prgv_header h1 { font-size: 28pt; } } /* 400px */ +.aboutcontent { + display: block; + width: 100%; } + +.aboutcontent .boxedcontent { + margin-bottom: 20px; } + +.about_egh_container { + display: flex; + flex-direction: column; + align-items: center; } + +.git_list { + display: inline-block; + width: 715px; + height: 115px; + overflow: visible; } + +@media (max-width: 991px) { + .git_list { + width: 100%; + height: auto; } + + .extGitGraphContainer { + width: 95%; + width: calc(100% - 16px); } } +.git_list text.caption { + font-size: 10px; + fill: #666; } + +.git_list text.caption_month { + font-size: 8px; + fill: #BBB; } + +.git_list text.caption_day { + font-size: 8px; + fill: #BBB; } + +.svg-tip:after { + box-sizing: border-box; + position: absolute; + left: 50%; + height: 5px; + width: 5px; + bottom: -10px; + margin: 0 0 0px -5px; + content: " "; + border: 5px solid transparent; + border-top-color: rgba(0, 0, 0, 0.8); + -moz-border-top-colors: none; + -moz-border-right-colors: none; + -moz-border-bottom-colors: none; + -moz-border-left-colors: none; + border-image: none; } + +.svg-tip { + padding: 5px; + background: none repeat scroll 0 0 rgba(0, 0, 0, 0.8); + color: #BBB; + font-size: 12px; + position: absolute; + z-index: 99999; + text-align: center; + border-radius: 3px; + box-sizing: border-box; + opacity: 0; } + +.extGitGraphContainer { + background-color: #FCFCFC; + margin: 10px; + display: inline-block; + border: 1px solid #222; + border-radius: 0; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.25) inset; } + +.egg_footer { + margin-top: 5px; + text-align: right; + margin-right: 5px; + margin-bottom: 5px; + color: #888; } + +.egg_footer > a { + text-decoration: none; + color: inherit; } + +.egg_footer > a:hover { + text-decoration: none; + color: #22F; } + +/* 400px */ .euler_pnl_base { display: inline-flex; flex-direction: column; diff --git a/www/data/css/styles.min.css b/www/data/css/styles.min.css index 8a931c2..2922168 100644 --- a/www/data/css/styles.min.css +++ b/www/data/css/styles.min.css @@ -18,6 +18,9 @@ body{background-color:#EEE;color:#CCC;font-family:serif} .blockcontent{display:block;width:100%} .generic_hidden{visibility:hidden !important} .generic_collapsed{visibility:collapse !important;display:none !important;width:0 !important;height:0 !important} +.boxedcontent{color:#333;border:1px solid black;background-color:#e0e0e0;width:100%;margin-left:auto;margin-right:auto} +.boxedcontent .bc_header{background-color:#BBB;padding:0 4px} +.boxedcontent .bc_data{padding:8px} #headerdiv{z-index:999;background-color:#333;display:flex;border-bottom:1px solid #111;box-shadow:0 0 8px #000;position:fixed;width:100%} #headerdiv .logowrapper{flex:initial;margin:0;padding:0;height:42px} #headerdiv .logowrapper .logo{height:30px;margin:4px 0 8px 6px;flex:initial} @@ -58,9 +61,6 @@ html,body{margin:0;padding:0;height:100%} .ble_date{background-color:#AAA;padding:2px;font-size:.8em;font-style:italic} .ble_title{font-weight:bold;font-size:1.2em;text-align:left;margin:2px 0 2px 10px} @media(max-width:767px){.ble_title{font-size:1.25em}} -.blogcontent{color:#333;border:1px solid black;background-color:#e0e0e0;width:90%;margin-left:auto;margin-right:auto} -.bc_header{background-color:#BBB;padding:0 4px} -.bc_data{padding:8px} .base_markdown code{font-family:Consolas,Monaco,"Courier New",Menlo,monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none} .base_markdown pre{font-size:14px;display:block;padding:9.5px;margin:0 0 10px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre-wrap;background-color:#f8f8f8;color:black;border:1px solid rgba(0,0,0,0.15);border-radius:2px} .base_markdown blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #CCC} @@ -77,6 +77,7 @@ html,body{margin:0;padding:0;height:100%} .mdtable_container{overflow-x:auto} .notable{width:unset !important;border:none !important} .notable th,.notable td,.notable tr{border:none !important;background:transparent !important} +.blogcontent_euler,.blogcontent_markdown,.blogcontent_plain{width:90%} #PEB_tableProblems .PEB_tablerowProblems:hover{background-color:#888} #PEB_tableProblems .PEB_TC_Value{font-family:Consolas,Monaco,"Courier New",Menlo,monospace} #PEB_tableProblems .PEB_TC_Value:not(:hover){color:transparent;-o-transition:.5s;-ms-transition:.5s;-moz-transition:.5s;-webkit-transition:.5s;transition:.5s} @@ -203,6 +204,23 @@ html,body{margin:0;padding:0;height:100%} .prgv_right_lang{justify-content:space-evenly} #prgv_header h1{font-size:28pt} } +.aboutcontent{display:block;width:100%} +.aboutcontent .boxedcontent{margin-bottom:20px} +.about_egh_container{display:flex;flex-direction:column;align-items:center} +.git_list{display:inline-block;width:715px;height:115px;overflow:visible} +@media(max-width:991px){ + .git_list{width:100%;height:auto} + .extGitGraphContainer{width:95%;width:calc(100% - 16px)} +} +.git_list text.caption{font-size:10px;fill:#666} +.git_list text.caption_month{font-size:8px;fill:#BBB} +.git_list text.caption_day{font-size:8px;fill:#BBB} +.svg-tip:after{box-sizing:border-box;position:absolute;left:50%;height:5px;width:5px;bottom:-10px;margin:0 0 0 -5px;content:" ";border:5px solid transparent;border-top-color:rgba(0,0,0,0.8);-moz-border-top-colors:none;-moz-border-right-colors:none;-moz-border-bottom-colors:none;-moz-border-left-colors:none;border-image:none} +.svg-tip{padding:5px;background:none repeat scroll 0 0 rgba(0,0,0,0.8);color:#BBB;font-size:12px;position:absolute;z-index:99999;text-align:center;border-radius:3px;box-sizing:border-box;opacity:0} +.extGitGraphContainer{background-color:#fcfcfc;margin:10px;display:inline-block;border:1px solid #222;border-radius:0;box-shadow:0 0 1px rgba(0,0,0,0.25) inset} +.egg_footer{margin-top:5px;text-align:right;margin-right:5px;margin-bottom:5px;color:#888} +.egg_footer>a{text-decoration:none;color:inherit} +.egg_footer>a:hover{text-decoration:none;color:#22F} .euler_pnl_base{display:inline-flex;flex-direction:column;border:1px solid #AAA;border-radius:5px 5px 0 0;margin:15px} .euler_pnl_header{display:flex;align-items:center;justify-content:center;padding:4px;background:#AAA} .euler_pnl_header a{color:#222;text-decoration:none;font-family:Lato,"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:22px;font-weight:900} diff --git a/www/data/css/styles.scss b/www/data/css/styles.scss index 8d127d8..9e062be 100644 --- a/www/data/css/styles.scss +++ b/www/data/css/styles.scss @@ -9,6 +9,7 @@ @import 'styles_errorview'; @import 'styles_programslist'; @import 'styles_programsview'; +@import 'styles_about'; @import 'styles_eulerpanel'; @import 'styles_programspanel'; diff --git a/www/data/css/styles_about.scss b/www/data/css/styles_about.scss new file mode 100644 index 0000000..467880c --- /dev/null +++ b/www/data/css/styles_about.scss @@ -0,0 +1,104 @@ +@import 'styles_config'; + +.aboutcontent { + display: block; + width: 100%; +} + +.aboutcontent .boxedcontent{ + margin-bottom: 20px; +} + +.about_egh_container { + display: flex; + flex-direction: column; + align-items: center; +} + +.git_list { + display: inline-block; + width: 715px; + height: 115px; + overflow: visible; +} + +@include rdmedia_range(0,2) { + .git_list { width: 100%; height: auto; } + .extGitGraphContainer { width: 95%; width: calc(100% - 16px); } +} + +.git_list text.caption { + font-size: 10px; + fill: #666; +} + +.git_list text.caption_month { + font-size: 8px; + fill: #BBB; +} + +.git_list text.caption_day { + font-size: 8px; + fill: #BBB; +} + +.svg-tip:after { + box-sizing: border-box; + position: absolute; + left: 50%; + height: 5px; + width: 5px; + bottom: -10px; + margin: 0 0 0px -5px; + content: " "; + border: 5px solid transparent; + border-top-color: rgba(0, 0, 0, 0.8); + -moz-border-top-colors: none; + -moz-border-right-colors: none; + -moz-border-bottom-colors: none; + -moz-border-left-colors: none; + border-image: none; +} + +.svg-tip { + padding: 5px; + background: none repeat scroll 0 0 rgba(0, 0, 0, 0.8); + color: #BBB; + font-size: 12px; + position: absolute; + z-index: 99999; + text-align: center; + border-radius: 3px; + box-sizing: border-box; + opacity: 0; +} + +.extGitGraphContainer { + background-color: #FCFCFC; + margin: 10px; + display: inline-block; + + border: 1px solid #222; + border-radius: 0; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.25) inset; +} + +.egg_footer { + margin-top: 5px; + + text-align: right; + margin-right: 5px; + margin-bottom: 5px; + + color: #888; +} + +.egg_footer > a { + text-decoration: none; + color: inherit; +} + +.egg_footer > a:hover { + text-decoration: none; + color: #22F; +} \ No newline at end of file diff --git a/www/data/css/styles_blogview.scss b/www/data/css/styles_blogview.scss index 5cb3108..822c0b3 100644 --- a/www/data/css/styles_blogview.scss +++ b/www/data/css/styles_blogview.scss @@ -1,23 +1,5 @@ @import 'styles_config'; -.blogcontent { - color: $COL_TEXT_DARK; - border: 1px solid black; - background-color: $COL_BACKGROUND_2; - width: 90%; - margin-left: auto; - margin-right: auto; -} - -.bc_header { - background-color: #BBB; - padding: 0 4px; -} - -.bc_data { - padding: 8px; -} - .base_markdown { code { @@ -115,3 +97,5 @@ background: transparent !important; } } + +.blogcontent_euler, .blogcontent_markdown, .blogcontent_plain { width: 90%;} diff --git a/www/data/css/styles_global.scss b/www/data/css/styles_global.scss index 020e6cd..fef109e 100644 --- a/www/data/css/styles_global.scss +++ b/www/data/css/styles_global.scss @@ -86,3 +86,21 @@ body { width:0 !important; height:0 !important; } + +.boxedcontent { + color: $COL_TEXT_DARK; + border: 1px solid black; + background-color: $COL_BACKGROUND_2; + width: 100%; + margin-left: auto; + margin-right: auto; + + .bc_header { + background-color: #BBB; + padding: 0 4px; + } + + .bc_data { + padding: 8px; + } +} \ No newline at end of file diff --git a/www/data/javascript/egh.js b/www/data/javascript/egh.js new file mode 100644 index 0000000..b1469e2 --- /dev/null +++ b/www/data/javascript/egh.js @@ -0,0 +1,57 @@ +function formatDate(date) { + const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; + const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + + let wday = days[date.getDay()]; + let day = date.getDate(); + let monthIndex = date.getMonth(); + let year = date.getFullYear(); + + let suffix = 'th'; + if (day === 1) suffix = 'st'; + if (day === 2) suffix = 'nd'; + if (day === 3) suffix = 'rd'; + + return wday + ' ' + day + suffix + ' ' + monthNames[monthIndex] + ', ' + year; +} + +window.onload = function () +{ + let svgtips = document.getElementsByClassName("svg-tip"); + let rects = document.getElementsByClassName("egg_rect"); + + let masterTip = null; + + for (let tip of svgtips) + { + tip.style.opacity = '1'; + tip.style.display = 'none'; + + masterTip = tip; + } + + let masterTipHeader = masterTip.getElementsByTagName('strong')[0]; + let masterTipContent = masterTip.getElementsByTagName('span')[0]; + + for (let rect of rects) + { + rect.addEventListener("mouseenter", function(event) + { + let datesplit = event.target.getAttribute('data-date').split('-'); + let count = event.target.getAttribute('data-count'); + let date = new Date(Number(datesplit[0]), Number(datesplit[1])-1, Number(datesplit[2])); + + masterTip.style.display = 'block'; + + masterTipHeader.innerHTML = count + ' commits'; + masterTipContent.innerHTML = ' on ' + formatDate(date); + + masterTip.style.left = (window.pageXOffset + event.target.getBoundingClientRect().left - masterTip.getBoundingClientRect().width /2 - 3.5 + 9) + 'px'; + masterTip.style.top = (window.pageYOffset + event.target.getBoundingClientRect().top - masterTip.getBoundingClientRect().height -10) + 'px'; + }); + rect.addEventListener("mouseleave", function(event) + { + masterTip.style.display = 'none'; + }); + } +}; \ No newline at end of file diff --git a/www/dynamic/.gitignore b/www/dynamic/.gitignore new file mode 100644 index 0000000..0318876 --- /dev/null +++ b/www/dynamic/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!.gitkeep \ No newline at end of file diff --git a/www/dynamic/.gitkeep b/www/dynamic/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/www/extern/egh/ConnectionGitea.php b/www/extern/egh/ConnectionGitea.php new file mode 100644 index 0000000..922c7b0 --- /dev/null +++ b/www/extern/egh/ConnectionGitea.php @@ -0,0 +1,39 @@ +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 new file mode 100644 index 0000000..bd2aa3c --- /dev/null +++ b/www/extern/egh/ConnectionGithub.php @@ -0,0 +1,166 @@ +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/EGHRemoteConfig.php b/www/extern/egh/EGHRemoteConfig.php new file mode 100644 index 0000000..1a11566 --- /dev/null +++ b/www/extern/egh/EGHRemoteConfig.php @@ -0,0 +1,27 @@ +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 new file mode 100644 index 0000000..6258fad --- /dev/null +++ b/www/extern/egh/EGHRenderer.php @@ -0,0 +1,224 @@ + ['#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"; + + $week = 0; + $wday = 0; + while($date->format('Y') == $year) + { + if ($date > $now) // THE FUTURE, SPONGEBOB + { + while ($date->format('d') != $date->format('t')) + { + if ($date->format('N') == 1 && $date->format('z') > 0) $week++; + $date = $date->modify("+1 day"); + } + $monthlist[$date->format('m') - 1][1] = $week + ($wday / 7); + + $date = $date->modify("+1 year"); // Kill + continue; + } + + $c_count = $this->commitMap[$date->format('Y-m-d')]; + $color_idx = min((count($colors)-1), ceil(pow($c_count/$ymapmax, $exponent) * (count($colors)-1))); + $color = $colors[$color_idx]; + + $wday = ($date->format('N') - 1); + + if ($date->format('N') == 1 && $date->format('z') > 0) + { + $html .= '' . "\n"; + $week++; + $html .= '' . "\n"; + } + + if ($date->format('d') == 1) + { + $monthlist[$date->format('m') - 1][0] = $week + ($wday / 7); + } + else if ($date->format('d') == $date->format('t')) + { + $monthlist[$date->format('m') - 1][1] = $week + ($wday / 7); + } + + $html .= '' . "\n"; + + $date = $date->modify("+1 day"); + } + + $html .= '' . "\n"; + + for($i = 0; $i < 12; $i++) + { + if ($monthlist[$i][1]-$monthlist[$i][0] > 0) + { + $posx = (($monthlist[$i][0]+$monthlist[$i][1])/2) * self::DIST_X; + $html .= '' . self::MONTHS[$i] . '' . "\n"; + } + } + + for($i = 0; $i < 7; $i++) { + $html .= '' . self::DAYS[$i] . '' . "\n"; + } + + $html .= '' . $year . '' . "\n"; + + $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 new file mode 100644 index 0000000..9e16726 --- /dev/null +++ b/www/extern/egh/ExtendedGitGraph.php @@ -0,0 +1,184 @@ +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(); + } + else if ($this->outputMode === self::OUT_STDOUT) + { + 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; + } +} \ No newline at end of file diff --git a/www/extern/egh/SingleCommitInfo.php b/www/extern/egh/SingleCommitInfo.php new file mode 100644 index 0000000..8da17bb --- /dev/null +++ b/www/extern/egh/SingleCommitInfo.php @@ -0,0 +1,31 @@ +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 new file mode 100644 index 0000000..99ddaea --- /dev/null +++ b/www/extern/egh/Utils.php @@ -0,0 +1,19 @@ + $val) + { + $str = str_replace('{'.$key.'}', $val, $str); + } + return $str; + } + + public static function startsWith($haystack, $needle) + { + $length = strlen($needle); + return (substr($haystack, 0, $length) === $needle); + } +} \ No newline at end of file diff --git a/www/fragments/befunge93_runner.php b/www/fragments/befunge93_runner.php index 914bdc2..f7fe09d 100644 --- a/www/fragments/befunge93_runner.php +++ b/www/fragments/befunge93_runner.php @@ -58,7 +58,7 @@ if ($interactive) { $result .= ' ' . "\n"; $result .= '' . "\n"; - $result .= includeScriptOnce("/data/javascript/blogpost_bef93runner.js", false) . "\n"; + $result .= includeScriptOnce("/data/javascript/blogpost_bef93runner.js", false, '') . "\n"; } else { diff --git a/www/fragments/bfjoust_runner.php b/www/fragments/bfjoust_runner.php index d7d5a23..c643738 100644 --- a/www/fragments/bfjoust_runner.php +++ b/www/fragments/bfjoust_runner.php @@ -35,6 +35,6 @@ $result .= '' . "\n"; $result .= '' . "\n"; -$result .= includeScriptOnce("/data/javascript/blogpost_BFJoustBot_script.js", false) . "\n"; +$result .= includeScriptOnce("/data/javascript/blogpost_BFJoustBot_script.js", false, '') . "\n"; return $result; \ No newline at end of file diff --git a/www/fragments/blogview_euler_list.php b/www/fragments/blogview_euler_list.php index ad465f6..04a5345 100644 --- a/www/fragments/blogview_euler_list.php +++ b/www/fragments/blogview_euler_list.php @@ -8,7 +8,7 @@ $problems = Euler::listAll(); ?> -
+
diff --git a/www/fragments/blogview_euler_single.php b/www/fragments/blogview_euler_single.php index ead1eb0..3b154ad 100644 --- a/www/fragments/blogview_euler_single.php +++ b/www/fragments/blogview_euler_single.php @@ -25,7 +25,7 @@ $max = ceil($max / 20) * 20; ?> -
+
diff --git a/www/fragments/blogview_markdown.php b/www/fragments/blogview_markdown.php index 14a5fa4..75520a2 100644 --- a/www/fragments/blogview_markdown.php +++ b/www/fragments/blogview_markdown.php @@ -4,7 +4,7 @@ require_once (__DIR__ . '/../internals/blog.php'); require_once (__DIR__ . '/../internals/ParsedownCustom.php'); ?> -
+
diff --git a/www/fragments/blogview_plain.php b/www/fragments/blogview_plain.php index e2b8a3c..45ad008 100644 --- a/www/fragments/blogview_plain.php +++ b/www/fragments/blogview_plain.php @@ -3,7 +3,7 @@ require_once (__DIR__ . '/../internals/base.php'); require_once (__DIR__ . '/../internals/blog.php'); ?> -
+
diff --git a/www/index.php b/www/index.php index dc2529a..aa7e224 100644 --- a/www/index.php +++ b/www/index.php @@ -173,4 +173,6 @@ try { //TODO euler best of on top of list (?) //TODO optimize image sizes for display/download (? - auto?) //TODO send cache header (?) -//TODO programs add [license] \ No newline at end of file +//TODO programs add [license] +//TODO admin +//TODO last 3 blog entries on /index/ (?) \ No newline at end of file diff --git a/www/internals/base.php b/www/internals/base.php index 4c8510f..eab9f1d 100644 --- a/www/internals/base.php +++ b/www/internals/base.php @@ -88,7 +88,7 @@ function formatMilliseconds($millis) } } -function includeScriptOnce($script, $echo = true) +function includeScriptOnce($script, $echo = true, $attr=false) { global $REGISTERED_SCRIPTS; @@ -96,14 +96,14 @@ function includeScriptOnce($script, $echo = true) { if (in_array($script, $REGISTERED_SCRIPTS)) return false; $REGISTERED_SCRIPTS []= $script; - echo ""; + echo ""; return true; } else { if (in_array($script, $REGISTERED_SCRIPTS)) return ''; $REGISTERED_SCRIPTS []= $script; - return ""; + return ""; } } diff --git a/www/pages/about.php b/www/pages/about.php new file mode 100644 index 0000000..0eb4a4a --- /dev/null +++ b/www/pages/about.php @@ -0,0 +1,54 @@ + + + + + Mikescher.com - About + + + + + + +
+ + + +
+ +
+ +

About mikescher.com


+ +
+
About me
+ +
+ +

Welcome to my private homepage.

+

My name is Mike, and this is my homepage. I use it to upload programs I have written and sometimes for a little bit of blogging.

+

There are also sections about Project Euler, self-printed books and more

+ +
+ +
+ +
+
My git timeline
+ +
+ + + +
+ +
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/www/statics/programs/ExtendedGitGraph_description.md b/www/statics/programs/ExtendedGitGraph_description.md index 46faf57..efa6960 100644 --- a/www/statics/programs/ExtendedGitGraph_description.md +++ b/www/statics/programs/ExtendedGitGraph_description.md @@ -1,3 +1,278 @@ -A little PHP library to display and update an commit Overview from [Github](https://github.com/). +extendedGitGraph +================ -Design-wise its similiar to the official Commit Graph from github - but you can see your commits over a bigger timeframe. \ No newline at end of file +Displays a Commit Table for every of your github-years. +This is practically a copy of githubs Commit-Graph functionality. +But with the extra feature of showing commits older than a year, from private repositories abd from other git remotes. + +*See it live in action [here](http://www.mikescher.de/about)* + +### How to use: + +Create a new ExtendedGitGraph object + +The constructor parameters are: + + * the path to the cache file + * The output mode + - STDOUT: Log to `print` and logfile + - SESSION: Log session-variable and logfile (used for ajax calls) + - LOGFILE: Only log to logfile + * The logfile path. The 4 latest logs are kept and the placeholder {num} is used for different filenames + +~~~php +include 'src/ExtendedGitGraph.php'; + +$v = new ExtendedGitGraph(__DIR__ . '/egh_cache.bin', ExtendedGitGraph::OUT_STDOUT, __DIR__ . '/../temp/egh_log{num}.log'); +~~~ + +Next you need to add sources for us to search, currently supported are: + + * Github User accounts + * Github Repositories + * Gitea User accounts *(WIP)* + * Gitea Repositories *(WIP)* + +~~~php +$v->addRemote('github-user', null, 'Mikescher', 'Mikescher'); +$v->addRemote('github-user', null, 'Mikescher', 'Blackforestbytes'); +$v->addRemote('github-repository', null, 'Mikescher', 'Anastron/ColorRunner'); +$v->addRemote('gitea-user', null, 'Mikescher', 'Mikescher'); +$v->addRemote('gitea-repository', null, 'Mikescher', 'Benzin/MVU_API'); +~~~ + +If you use github you need to specify an API token to get more than 60 API calls: +(get one from [Github -> Settings -> Developer Settings -> Personal access tokens](https://github.com/settings/tokens)) + +~~~php +$v->ConnectionGithub->setAPIToken('1234567890ABCDEF'); +~~~ + +If you use gitea you need to specify the server url + +~~~php +$v->ConnectionGitea->setURL('https://my-git-server.tld'); +~~~ + +Now we generate the graphs, first call `init()` +~~~php +$v->init(); +~~~ + +Then either query the data from our specified sources with `updateFromRemotes()` or load teh values from the last query from our specified cache file with `updateFromCache()` +~~~php +$v->updateFromRemotes(); +//$v->updateFromCache(); +~~~ + +Next call `generate()` to create HTML and get the snippets by calling `get()`. + +~~~php +$v->setColorScheme('blue'); +$v->generate(); + +foreach ($v->get() as $year => $html) +{ + file_put_contents(__DIR__ . '/../output/out_'.$year.'.html', $html); +} +~~~ + +You can set the used color scheme with `setColorScheme`, supported are: + - custom + - standard + - modern + - gray + - red + - blue + - purple + - orange + - halloween + +![](https://raw.githubusercontent.com/Mikescher/extendedGitGraph/master/README-DATA/preview_orange.png) +![](https://raw.githubusercontent.com/Mikescher/extendedGitGraph/master/README-DATA/preview_purple.png) +![](https://raw.githubusercontent.com/Mikescher/extendedGitGraph/master/README-DATA/preview_green.png) +![](https://raw.githubusercontent.com/Mikescher/extendedGitGraph/master/README-DATA/preview_red.png) + +### Reload with Ajax: + +The reloading can take a **long** time if you have a lot of commits and repositories. +Because of that you can also refresh via Ajax: + + - Call the file `ajax/ajaxReload.php?scheme=x` to start the reloading + - Call the file `ajax/ajaxStatus.php` to get the current status (for displaying purposes) + - Call the file `ajax/ajaxRedraw.php?scheme=x` to only redraw from cache + +> **Attention:** +> You need to create a file `ajaxSecret.php` that returns an ExtendedGitGraph object with your settings (remotes, repositories, tokens, etc). +> Don't forget to set output mode to `ExtendedGitGraph::OUT_SESSION` + +Below a crappy example implementation with jQuerys Ajax calls: + +~~~html + + + + + + + + + + + + + + + + +
+      +
[REDRAW] +      + [REGENERATE] +      + + +
+
+
+
+ +
+ \n\n\n"; + } + ?> +
+ + + + +~~~