From f52cb919cf1bc0b1c3202eacc57a28e1fb1a3f2b Mon Sep 17 00:00:00 2001 From: Daniel Brendel Date: Sun, 25 Sep 2022 12:35:24 +0200 Subject: [PATCH] New widget: Steam Workshop --- README.md | 8 +- app/config/routes.php | 2 +- app/controller/api.php | 22 + app/controller/index.php | 12 - app/models/HitsModel.php | 3 +- app/modules/SteamApp.php | 2 +- app/modules/SteamServer.php | 2 +- app/modules/SteamUser.php | 2 +- app/modules/SteamWorkshop.php | 53 ++ app/resources/css/steam_workshop.dev.css | 133 ++++ app/resources/js/steam_workshop.dev.js | 285 ++++++++ app/views/generator.php | 677 ------------------ app/views/index.php | 142 +++- app/views/layout.php | 1 + app/views/navbar.php | 4 + build_ver.bat | 9 + public/css/steamwidgets/v1/steam_workshop.css | 133 ++++ public/js/steamwidgets/v1/steam_workshop.js | 285 ++++++++ 18 files changed, 1075 insertions(+), 700 deletions(-) create mode 100644 app/modules/SteamWorkshop.php create mode 100644 app/resources/css/steam_workshop.dev.css create mode 100644 app/resources/js/steam_workshop.dev.js delete mode 100644 app/views/generator.php create mode 100644 public/css/steamwidgets/v1/steam_workshop.css create mode 100644 public/js/steamwidgets/v1/steam_workshop.js diff --git a/README.md b/README.md index e49a070..ba08927 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ SteamWidgets offers the possibility to comfortably render Steam related widgets document with as less effort as possible. ## Current featured widgets -- Steam App Widget: Renders a widget of a Steam app/game -- Steam Server Widget: Renders a widget of a Steam game server -- Steam User Widget: Renders a widget of a Steam user - +- Steam App Widget: Renders widgets of a Steam app/game +- Steam Server Widget: Renders widgets of a Steam game server +- Steam User Widget: Renders widgets of a Steam user +- Steam Workshop Widget: Renders widgets of a Steam workshop item diff --git a/app/config/routes.php b/app/config/routes.php index f4a80be..cc5bc98 100644 --- a/app/config/routes.php +++ b/app/config/routes.php @@ -17,10 +17,10 @@ return [ array('/', 'GET', 'index@index'), - array('/generator', 'GET', 'index@generator'), array('/api/query/app', 'GET', 'api@queryAppInfo'), array('/api/query/server', 'GET', 'api@queryServerInfo'), array('/api/query/user', 'GET', 'api@queryUserInfo'), + array('/api/query/workshop', 'GET', 'api@queryWorkshopInfo'), array('/api/resource/query', 'GET', 'api@queryResource'), array('/stats/{pw}', 'GET', 'stats@index'), array('/stats/query/{pw}', 'ANY', 'stats@query'), diff --git a/app/controller/api.php b/app/controller/api.php index c7f3767..872c476 100644 --- a/app/controller/api.php +++ b/app/controller/api.php @@ -81,6 +81,28 @@ class ApiController extends BaseController { } } + /** + * Query Steam workshop data + * + * @param Asatru\Controller\ControllerArg $request + * @return Asatru\View\JsonHandler + */ + public function queryWorkshopInfo($request) + { + try { + $itemid = $request->params()->query('itemid', null); + + $data = SteamWorkshop::querySteamData($itemid); + + //Save hit + HitsModel::addHit(HitsModel::HITTYPE_MODULE_WORKSHOP); + + return json(array('code' => 200, 'itemid' => $itemid, 'data' => $data)); + } catch (\Exception $e) { + return json(array('code' => 500, 'msg' => $e->getMessage())); + } + } + /** * Query JavaScript or CSS resource for component * diff --git a/app/controller/index.php b/app/controller/index.php index e258326..833d357 100644 --- a/app/controller/index.php +++ b/app/controller/index.php @@ -27,16 +27,4 @@ class IndexController extends BaseController { //Generate and return a view by using the helper return parent::view(['content', 'index']); } - - /** - * Handles URL: /generator - * - * @param Asatru\Controller\ControllerArg $request - * @return Asatru\View\ViewHandler - */ - public function generator($request) - { - //Generate and return a view by using the helper - return view('generator', []); - } } diff --git a/app/models/HitsModel.php b/app/models/HitsModel.php index d09064b..ed981d1 100644 --- a/app/models/HitsModel.php +++ b/app/models/HitsModel.php @@ -10,6 +10,7 @@ class HitsModel extends \Asatru\Database\Model const HITTYPE_MODULE_APP = 'mod_app'; const HITTYPE_MODULE_SERVER = 'mod_server'; const HITTYPE_MODULE_USER = 'mod_user'; + const HITTYPE_MODULE_WORKSHOP = 'mod_workshop'; /** * Validate hit type @@ -21,7 +22,7 @@ class HitsModel extends \Asatru\Database\Model public static function validateHitType($type) { try { - $types = [self::HITTYPE_MODULE_APP, self::HITTYPE_MODULE_SERVER, self::HITTYPE_MODULE_USER]; + $types = [self::HITTYPE_MODULE_APP, self::HITTYPE_MODULE_SERVER, self::HITTYPE_MODULE_USER, self::HITTYPE_MODULE_WORKSHOP]; if (!in_array($type, $types)) { throw new Exception('Invalid hit type: ' . $type); diff --git a/app/modules/SteamApp.php b/app/modules/SteamApp.php index a20c5ed..9b215c0 100644 --- a/app/modules/SteamApp.php +++ b/app/modules/SteamApp.php @@ -1,7 +1,7 @@ response->result)) && ($obj->response->result) && (isset($obj->response->resultcount)) && ($obj->response->resultcount == 1)) { + $obj->response->publishedfiledetails[0]->creator_data = null; + + try { + $obj->response->publishedfiledetails[0]->creator_data = SteamUser::querySteamData(env('STEAM_API_KEY'), $obj->response->publishedfiledetails[0]->creator); + } catch (\Exception $e) { + } + + return $obj->response->publishedfiledetails[0]; + } + + throw new \Exception('Invalid data response'); + } +} diff --git a/app/resources/css/steam_workshop.dev.css b/app/resources/css/steam_workshop.dev.css new file mode 100644 index 0000000..d61bc30 --- /dev/null +++ b/app/resources/css/steam_workshop.dev.css @@ -0,0 +1,133 @@ +.steam-workshop { + position: relative; + min-width: 360px; + max-width: 555px; + height: 205px; + background-color: rgb(22, 32, 45); + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + border-radius: 25px; + margin-top: 20px; + margin-left: 10px; + margin-right: 10px; +} + +.steam-workshop-preview { + position: relative; + display: inline-block; + min-width: 205px; + height: 205px; + background-repeat: no-repeat; + background-size: 100% 100%; + border-top-left-radius: 25px; + border-bottom-left-radius: 25px; +} + +.steam-workshop-info { + position: relative; + display: inline-block; + width: 55%; + margin-left: 20px; + top: -20px; +} + +.steam-workshop-info-title { + margin-bottom: 10px; + color: rgb(220, 220, 220); + font-size: 1.1em; + font-family: Verdana, Arial, sans-serif; +} + +.steam-workshop-info-description { + margin-bottom: 10px; + color: rgb(150, 150, 150); + font-size: 0.8em; + font-family: Verdana, Arial, sans-serif; +} + +.steam-workshop-info-stats { + margin-bottom: 12px; +} + +.steam-workshop-info-stats-item { +} + +.steam-workshop-info-stats-item-count { + display: inline-block; + min-width: 50px; + color: rgb(59, 135, 185); +} + +.steam-workshop-info-stats-item-label { + display: inline-block; + text-transform: uppercase; + color: rgb(100, 100, 100); +} + +.steam-workshop-info-footer { +} + +.steam-workshop-info-footer-author { + display: inline-block; + width: 69%; +} + +.steam-workshop-info-footer-author a { + color: rgb(50, 115, 220); +} + +.steam-workshop-info-footer-author a:hover { + color: rgb(50, 115, 220); + text-decoration: underline; +} + +.steam-workshop-info-footer-action { + display: inline-block; +} + +.steam-workshop-info-footer-action a { + color: #D2E885; + background: linear-gradient(to bottom, #a4d007 5%, #536904 95%); + border-radius: 5px; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; + text-decoration: none; + font-size: 0.9em; + font-family: 'Motiva Sans', sans-serif; +} + +.steam-workshop-info-footer-action a:hover { + color: rgb(250, 250, 250); +} + +@media screen and (max-width: 465px) { + .steam-workshop { + min-width: unset; + width: 350px; + min-height: 205px; + height: unset; + } + + .steam-workshop-preview { + min-width: unset; + width: 350px; + height: 350px; + border-top-right-radius: 25px; + border-bottom-left-radius: unset; + } + + .steam-workshop-info { + width: 100%; + height: 195px; + top: unset; + } + + .steam-workshop-info-title { + margin-top: 5px; + } + + .steam-workshop-info-footer-author { + width: 64%; + } +} \ No newline at end of file diff --git a/app/resources/js/steam_workshop.dev.js b/app/resources/js/steam_workshop.dev.js new file mode 100644 index 0000000..4e671eb --- /dev/null +++ b/app/resources/js/steam_workshop.dev.js @@ -0,0 +1,285 @@ +/** + * SteamWidgets - Steam Widgets for your website + * + * Module: Steam Workshop Widget + * + * Visit: https://github.com/danielbrendel + */ + + const STEAMWIDGETS_WORKSHOP_ENDPOINT = 'http://localhost:8000'; + const STEAMWIDGETS_WORKSHOP_VERSION = 'v1'; + + /** + * Class SteamWorkshopElem + * + * Handle custom HTML element to render Steam workshop widgets + */ + class SteamWorkshopElem extends HTMLElement + { + DESCRIPTION_MAX_LEN = 40; + + storedData = {}; + oldHeader = ''; + + connectedCallback() + { + var itemid = (typeof this.attributes.itemid !== 'undefined') ? this.attributes.itemid.value : null; + var views = (typeof this.attributes.views !== 'undefined') ? this.attributes.views.value : 'Views'; + var subscriptions = (typeof this.attributes.subscriptions !== 'undefined') ? this.attributes.subscriptions.value : 'Subscriptions'; + var favorites = (typeof this.attributes.favorites !== 'undefined') ? this.attributes.favorites.value : 'Favorites'; + var author = (typeof this.attributes.author !== 'undefined') ? this.attributes.author.value : 'By :creator'; + var viewtext = (typeof this.attributes.viewtext !== 'undefined') ? this.attributes.viewtext.value : 'View item'; + var showImage = (typeof this.attributes['show-image'] !== 'undefined') ? parseInt(this.attributes['show-image'].value) : 1; + var styleBorder = (typeof this.attributes['style-border'] !== 'undefined') ? this.attributes['style-border'].value : null; + var styleShadow = (typeof this.attributes['style-shadow'] !== 'undefined') ? parseInt(this.attributes['style-shadow'].value) : 1; + + if (itemid !== null) { + this.storedData = { + itemid: itemid, + views: views, + subscriptions: subscriptions, + favorites: favorites, + author: author, + viewtext: viewtext, + showImage: showImage, + styleBorder: styleBorder, + styleShadow: styleShadow + }; + + this.setupWidget( + itemid, + views, + subscriptions, + favorites, + author, + viewtext, + showImage, + styleBorder, + styleShadow + ); + } + } + + setupWidget(itemid, views, subscriptions, favorites, author, viewtext, showImage, styleBorder, styleShadow) + { + var req = new XMLHttpRequest(); + var self = this; + + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + let json = JSON.parse(req.responseText); + + if (!document.getElementById('steamwidgets-workshop-styles')) { + let link = document.createElement('link'); + link.id = 'steamwidgets-workshop-styles'; + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = STEAMWIDGETS_WORKSHOP_ENDPOINT + '/api/resource/query?type=css&module=workshop&version=' + STEAMWIDGETS_WORKSHOP_VERSION; + document.getElementsByTagName('head')[0].appendChild(link); + } + + let description = json.data.description; + if (description.length >= self.DESCRIPTION_MAX_LEN) { + description = description.substr(0, self.DESCRIPTION_MAX_LEN - 3) + '...'; + } + + author = author.replace(':creator', json.data.creator_data.personaname); + + let html = ` +
+
+ +
+
` + json.data.title + `
+ +
` + description + `
+ +
+
+
` + self.readableCount(json.data.views) + `
+
` + views + `
+
+ +
+
` + self.readableCount(json.data.subscriptions) + `
+
`+ subscriptions + `
+
+ +
+
` + self.readableCount(json.data.favorited) + `
+
`+ favorites + `
+
+
+ + +
+
+ `; + + self.innerHTML = html; + } + }; + req.open('GET', STEAMWIDGETS_WORKSHOP_ENDPOINT + '/api/query/workshop?itemid=' + itemid, true); + req.send(); + } + + updateWidget() + { + this.setupWidget( + this.storedData.itemid, + this.storedData.views, + this.storedData.subscriptions, + this.storedData.favorites, + this.storedData.author, + this.storedData.viewtext, + this.storedData.showImage, + this.storedData.styleBorder, + this.storedData.styleShadow + ); + } + + changeLang(views, subscriptions, favorites, author, viewtext) + { + this.storedData.views = views; + this.storedData.subscriptions = subscriptions; + this.storedData.favorites = favorites; + this.storedData.author = author; + this.storedData.viewtext = viewtext; + + this.setupWidget( + this.storedData.itemid, + this.storedData.views, + this.storedData.subscriptions, + this.storedData.favorites, + this.storedData.author, + this.storedData.viewtext, + this.storedData.showImage, + this.storedData.styleBorder, + this.storedData.styleShadow + ); + } + + setImageVisibility(visibility) + { + this.storedData.showImage = visibility; + + this.setupWidget( + this.storedData.itemid, + this.storedData.views, + this.storedData.subscriptions, + this.storedData.favorites, + this.storedData.author, + this.storedData.viewtext, + this.storedData.showImage, + this.storedData.styleBorder, + this.storedData.styleShadow + ); + } + + readableCount(count) + { + const COUNT_MILLION = 1000000; + const COUNT_HUNDREDTHOUSAND = 100000; + const COUNT_TENTHOUSAND = 10000; + const COUNT_THOUSAND = 1000; + + if (count >= COUNT_MILLION) { + return (Math.round(count / COUNT_MILLION, 1)).toString() + 'M'; + } else if ((count < COUNT_MILLION) && (count >= COUNT_HUNDREDTHOUSAND)) { + return (Math.round(count / COUNT_THOUSAND, 1)).toString() + 'K'; + } else if ((count < COUNT_HUNDREDTHOUSAND) && (count >= COUNT_TENTHOUSAND)) { + return (Math.round(count / COUNT_THOUSAND, 1)).toString() + 'K'; + } else if ((count < COUNT_TENTHOUSAND) && (count >= COUNT_THOUSAND)) { + return (Math.round(count / COUNT_THOUSAND, 1)).toString() + 'K'; + } else { + return count.toString(); + } + } + } + + window.customElements.define('steam-workshop', SteamWorkshopElem); + + /** + * Class SteamWorkshop + * + * Dynamically create a Steam workshop widgets via JavaScript + */ + class SteamWorkshop + { + elem = null; + selident = null; + cfg = {}; + + constructor(selector, config = {}) + { + this.selident = selector; + this.cfg = config; + + var itemid = (typeof config.itemid !== 'undefined') ? config.itemid : null; + var views = (typeof config.views !== 'undefined') ? config.views : 'Views'; + var subscriptions = (typeof config.subscriptions !== 'undefined') ? config.subscriptions : 'Subscriptions'; + var favorites = (typeof config.favorites !== 'undefined') ? config.favorites : 'Favorites'; + var author = (typeof config.author !== 'undefined') ? config.author : 'By :creator'; + var viewtext = (typeof config.viewtext !== 'undefined') ? config.viewtext : 'View item'; + var showImage = (typeof config.showImage !== 'undefined') ? config.showImage : null; + + if (typeof showImage === 'boolean') { + showImage = (showImage) ? 1 : 0; + } + + var styleBorder = null; + var styleShadow = 1; + + if (typeof config.style !== 'undefined') { + styleBorder = (typeof config.style.border !== 'undefined') ? config.style.border : null; + styleShadow = (typeof config.style.shadow !== 'undefined') ? config.style.shadow : 1; + } + + if (typeof styleShadow === 'boolean') { + styleShadow = (styleShadow) ? 1 : 0; + } + + this.elem = document.createElement('steam-workshop'); + this.elem.setAttribute('itemid', itemid); + this.elem.setAttribute('views', views); + this.elem.setAttribute('subscriptions', subscriptions); + this.elem.setAttribute('favorites', favorites); + this.elem.setAttribute('author', author); + this.elem.setAttribute('viewtext', viewtext); + this.elem.setAttribute('show-image', showImage); + this.elem.setAttribute('style-border', styleBorder); + this.elem.setAttribute('style-shadow', styleShadow); + + let sel = document.querySelector(selector); + if (sel) { + sel.appendChild(this.elem); + } + } + + updateWidget() + { + this.elem.updateWidget(); + } + + changeLang(views, subscriptions, favorites, author, viewtext) + { + this.elem.changeLang(views, subscriptions, favorites, author, viewtext); + } + + setImageVisibility(visibility) + { + this.elem.setImageVisibility(visibility); + } + + remove() + { + this.elem.remove(); + } + } + \ No newline at end of file diff --git a/app/views/generator.php b/app/views/generator.php deleted file mode 100644 index 30591ce..0000000 --- a/app/views/generator.php +++ /dev/null @@ -1,677 +0,0 @@ - - - - - - - - - - - - - - {{ env('APP_TITLE') }} - - - - @if (env('APP_DEBUG')) - - @else - - @endif - - - - - - - -
-
-
-

{{ env('APP_NAME') }} Widget Generator

-
- -
-

Please select a type of widget you want to create.

- -

For more information about {{ env('APP_NAME') }} please visit {{ url('/') }}

-
- -
- - -
- Go Back

- -

Create Steam App Widget

- -
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
- Generate -
-
-
- -
- -
-

-
-


-
-
- -
- Go Back

- -

Create Steam Server Widget

- -
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
- Generate -
-
-
- -
- -
-

-
-


-
-
- -
- Go Back

- -

Create Steam User Widget

- -
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
- Generate -
-
-
- -
- -
-

-
-


-
-
-
-
- - - - -
- - - - - \ No newline at end of file diff --git a/app/views/index.php b/app/views/index.php index e30b851..75226e9 100644 --- a/app/views/index.php +++ b/app/views/index.php @@ -26,6 +26,7 @@
  • Steam App Widget
  • Steam Server Widget
  • Steam User Widget
  • +
  • Steam Workshop Widget
  • @@ -40,8 +41,8 @@
    - STEAM_WIDGETS_MODULE can either be app for the Steam App widget, server for the Steam Server widget or - user for the Steam User widget. + STEAM_WIDGETS_MODULE can either be app for the Steam App widget, server for the Steam Server widget, + user for the Steam User widget or workshop for the Steam Workshop widget.

    @if (env('APP_SHOWNPMUSAGE', false)) @@ -546,6 +547,143 @@ document.addEventListener('DOMContentLoaded', function() { Sets the widget image visibility + + remove() + Removes the widget from the document + + + +

    + + +
    +

    + +

    Steam Workshop

    + +

    + When referenced the required Steam Workshop module, the minimum code to render a widget is as follows:
    +

    +			
    +<steam-workshop itemid="id"></steam-workshop>
    +			
    +		
    + +

    + This renders the following widget:
    + +

    + +

    +
    You can define these options:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AttributeValue
    itemidSpecifies the item ID of the Steam Workshop item
    viewsSpecifies the text of the views stats label
    subscriptionsSpecifies the text of the subscriptions stats label
    favoritesSpecifies the text of the favorites stats label
    authorSpecifies the author text. Use :creator to insert the creators Steam persona name
    viewtextSpecifies the text of the button which can be used to go to the Workshop item page
    show-image / showImageSpecifies if the workshop item preview image shall be displayed. Defaults to true/1
    style-border / style.borderSpecify border rounding: Either none, small or max
    style-shadow / style.shadowYou can specify false to prevent displaying box shadow or true to enable (default)
    +

    + +

    + You can also dynamically create Steam Workshop widgets via JavaScript:
    + +

    +			
    +<div id="workshop-widget"></div>
    +
    +<script>
    +document.addEventListener('DOMContentLoaded', function() {
    +    let widget = new SteamWorkshop('#workshop-widget', {
    +        itemid: 'id',
    +        //You can specify the same attributes as shown in the table above
    +    });
    +});
    +</script>
    +			
    +		
    +

    + +

    +
    The following methods are available for a Steam Workshop element / object:
    + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/views/layout.php b/app/views/layout.php index a0645d5..d2eaac8 100644 --- a/app/views/layout.php +++ b/app/views/layout.php @@ -22,6 +22,7 @@ + diff --git a/app/views/navbar.php b/app/views/navbar.php index 9ef273a..ea95625 100644 --- a/app/views/navbar.php +++ b/app/views/navbar.php @@ -28,6 +28,10 @@ Steam User + + + Steam Workshop +
    MethodDescription
    updateWidget()Updates the widget data and displays them
    changeLang(views, subscriptions, favorites, author, viewtext)Changes the language of the widget using the given information
    setImageVisibility(visibility)Sets the widget image visibility
    remove() Removes the widget from the document