├── .github └── workflows │ └── php.yml ├── LICENSE ├── README.md ├── composer.json └── src └── Embed.php /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Validate composer.json and composer.lock 18 | run: composer validate 19 | 20 | - name: Cache Composer packages 21 | id: composer-cache 22 | uses: actions/cache@v2 23 | with: 24 | path: vendor 25 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 26 | restore-keys: | 27 | ${{ runner.os }}-php- 28 | 29 | - name: Install dependencies 30 | if: steps.composer-cache.outputs.cache-hit != 'true' 31 | run: composer install --prefer-dist --no-progress --no-suggest 32 | 33 | # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" 34 | # Docs: https://getcomposer.org/doc/articles/scripts.md 35 | 36 | # - name: Run test suite 37 | # run: composer run-script test 38 | 39 | - name: php-codesniffer 40 | uses: pipeline-components/php-codesniffer@v0.12.3 41 | with: 42 | # Directory to check 43 | directory: src 44 | # Additional options 45 | options: -s -p --colors --extensions=php --standard=PSR1,PSR12,PSR2 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2021 Ivan Peevski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Library to embed [Metabase](http://www.metabase.com/) frames 2 | 3 | # Installation 4 | - Install via composer 5 | `composer require ipeevski/metabase-php` 6 | - Go to Metabase and enable embedding - https://[metabase_url]/admin/settings/embedding_in_other_applications 7 | - Note down the Metabase base url and the Embedding secret key 8 | 9 | # Usage 10 | ## Basic usage 11 | 12 | First, you need to find the dashboard or question you want to embed. Note down the id - it would be at the end of the URL (for example https://[metabase_url]/dashboard/1?date=past26weeks 13 | 14 | Note the integer after /dashboard/ - that's the ID of the dashboard. 15 | Also note the GET parameters at the end of the url - those are parameters you might want to pass to the dashboard too. 16 | 17 | 18 | ``` 19 | 'past26weeks']; 30 | 31 | $metabase = new \Metabase\Embed($metabaseUrl, $metabaseKey); 32 | // Generate the HTML to create an iframe with the embedded dashboard 33 | echo $metabase->dashboardIframe($dashboardId, $params); 34 | ``` 35 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ipeevski/metabase-php", 3 | "type": "library", 4 | "description": "Embedding Metabase", 5 | "keywords": ["metabase"], 6 | "homepage": "https://github.com/ipeevski/metabase-php", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Ivan Peevski", 11 | "email": "ipeevski@gmail.com", 12 | "homepage": "http://ipeevski.com", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "lcobucci/jwt": "^4.1.5" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Metabase\\": "src" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Embed.php: -------------------------------------------------------------------------------- 1 | url = $url; 45 | $this->key = $key; 46 | $this->border = $border; 47 | $this->title = $title; 48 | $this->width = $width; 49 | $this->height = $height; 50 | $this->expirationSeconds = $expirationSeconds; 51 | 52 | $this->jwtConfig = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText($this->key)); 53 | } 54 | 55 | /** 56 | * Get the embed URL for a Metabase question 57 | * 58 | * @param int $questionId The id of the question to embed 59 | * @param array $params An associate array with variables to be passed to the question 60 | * 61 | * @return string Embed URL 62 | */ 63 | public function questionUrl($questionId, $params = []) 64 | { 65 | return $this->url('question', $questionId, $params); 66 | } 67 | 68 | /** 69 | * Get the embed URL for a Metabase dashboard 70 | * 71 | * @param int $dashboardId The id of the dashboard to embed 72 | * @param array $params An associate array with variables to be passed to the dashboard 73 | * 74 | * @return string Embed URL 75 | */ 76 | public function dashboardUrl($dashboardId, $params = []) 77 | { 78 | return $this->url('dashboard', $dashboardId, $params); 79 | } 80 | 81 | /** 82 | * Use JWT to encode tokens 83 | * 84 | * @param array $resource Resource to encode (question or dashboard) 85 | * @param array $params An associate array with variables to be passed to the dashboard 86 | * 87 | * @return string Token 88 | */ 89 | private function encode($resource, $params) 90 | { 91 | $jwt = $this->jwtConfig->builder(); 92 | $jwt->withClaim('resource', $resource); 93 | if (empty($params)) { 94 | $jwt->withClaim('params', (object)[]); 95 | } else { 96 | $jwt->withClaim('params', $params); 97 | } 98 | if (!is_null($this->expirationSeconds)) { 99 | $jwt->expiresAt((new DateTimeImmutable())->modify('+' . $this->expirationSeconds . ' seconds')); 100 | } 101 | 102 | return $jwt->getToken($this->jwtConfig->signer(), $this->jwtConfig->signingKey()); 103 | } 104 | 105 | protected function url($resource, $id, $params) 106 | { 107 | // Generate auth token, using JWT 108 | $token = $this->encode([$resource => $id], $params); 109 | 110 | // Generate embed URL 111 | $url = $this->url . '/embed/' . $resource . '/' . $token->toString() . '#'; 112 | 113 | // Should border be included 114 | if ($this->border) { 115 | $url .= 'bordered=true&'; 116 | } else { 117 | $url .= 'bordered=false&'; 118 | } 119 | 120 | // Should title be included 121 | if ($this->title) { 122 | $url .= 'titled=true&'; 123 | } else { 124 | $url .= 'titled=false&'; 125 | } 126 | 127 | // Set selected theme (if any) 128 | if (!empty($this->theme)) { 129 | $url .= 'theme=' . $this->theme . '&'; 130 | } 131 | 132 | // Remove trailing & 133 | $url = rtrim($url, '&'); 134 | 135 | return $url; 136 | } 137 | 138 | /** 139 | * Generate the HTML to embed a question iframe with a given question id. 140 | * It assumes no iframe border. Size can be manipulated via 141 | * class $width/$height 142 | * 143 | * @param int $questionId The id of the question to embed 144 | * @param array $params An associate array with variables to be passed to the question 145 | * 146 | * @return string Code to embed 147 | */ 148 | public function questionIFrame($questionId, $params = []) 149 | { 150 | $url = $this->questionUrl($questionId, $params); 151 | return $this->iframe($url); 152 | } 153 | 154 | /** 155 | * Generate the HTML to embed a dashboard iframe with a given dashboard id. 156 | * It assumes no iframe border. Size can be manipulated via 157 | * class $width/$height 158 | * 159 | * @param int $dashboardId The id of the dashboard to embed 160 | * @param array $params An associate array with variables to be passed to the dashboard 161 | * 162 | * @return string Code to embed 163 | */ 164 | public function dashboardIFrame($dashboardId, $params = []) 165 | { 166 | $url = $this->dashboardUrl($dashboardId, $params); 167 | return $this->iframe($url); 168 | } 169 | 170 | /** 171 | * Generate the HTML to embed an iframe with a given URL. 172 | * It assumes no iframe border. Size can be manipulated via 173 | * class $width/$height 174 | * 175 | * @param string $iframeUrl The URL to create an iframe for 176 | * 177 | * @return string Code to embed 178 | */ 179 | protected function iframe($iframeUrl) 180 | { 181 | return ''; 187 | } 188 | } 189 | --------------------------------------------------------------------------------