├── tests
├── .gitignore
├── test.pdf
└── GoogleCloudPrintTest.php
├── .gitignore
├── src
├── config
│ └── config.php
├── Exceptions
│ ├── InvalidSourceException.php
│ ├── PrintTaskFailedException.php
│ └── InvalidCredentialsException.php
├── Facades
│ └── GoogleCloudPrint.php
├── PrintJob.php
├── LaravelServiceProvider.php
├── GoogleCloudPrint.php
├── PrintApi.php
└── PrintTask.php
├── phpunit.xml
├── composer.json
├── LICENSE
└── README.md
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | *.json
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | vendor/
3 | composer.lock
4 |
--------------------------------------------------------------------------------
/src/config/config.php:
--------------------------------------------------------------------------------
1 | env('GCP_CREDENTIALS_PATH'),
5 | ];
--------------------------------------------------------------------------------
/tests/test.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bnbwebexpertise/laravel-google-cloud-print/HEAD/tests/test.pdf
--------------------------------------------------------------------------------
/src/Exceptions/InvalidSourceException.php:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bnbwebexpertise/laravel-google-cloud-print",
3 | "description": "Google Cloud Print Service Provider for Laravel 5",
4 | "type": "Library",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Jérémy Gaulin",
9 | "email": "jeremy@bnb.re"
10 | }
11 | ],
12 | "autoload": {
13 | "psr-4": {
14 | "Bnb\\GoogleCloudPrint\\": "src/"
15 | }
16 | },
17 | "require": {
18 | "php": ">=5.6.0",
19 | "illuminate/config": "5.x",
20 | "illuminate/session": "5.x",
21 | "illuminate/support": "5.x",
22 | "google/apiclient": "^2.1"
23 | },
24 | "require-dev": {
25 | "phpunit/phpunit": "~4.6",
26 | "mockery/mockery": "0.9.*"
27 | },
28 | "minimum-stability": "stable"
29 | }
30 |
--------------------------------------------------------------------------------
/src/PrintJob.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright 2016 - B&B Web Expertise
7 | */
8 |
9 | namespace Bnb\GoogleCloudPrint;
10 |
11 | /**
12 | * Class PrintJob
13 | *
14 | * @property string id
15 | * @property string printerid
16 | * @property string status
17 | * @property string title
18 | *
19 | * @package Bnb\GoogleCloudPrint
20 | */
21 | class PrintJob
22 | {
23 |
24 | public function __construct($job)
25 | {
26 | $this->data = $job;
27 | }
28 |
29 |
30 | public function __get($attribute)
31 | {
32 | if (isset($this->data->{$attribute})) {
33 | return $this->data->{$attribute};
34 | }
35 |
36 | return null;
37 | }
38 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 B&B Web Expertise
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 |
--------------------------------------------------------------------------------
/src/LaravelServiceProvider.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom(__DIR__ . '/config/config.php', 'gcp');
25 |
26 | $this->app->singleton('google.print', function ($app) {
27 | return new GoogleCloudPrint($app['config']);
28 | });
29 | }
30 |
31 |
32 | /**
33 | * Boot the service provider.
34 | *
35 | * @return void
36 | */
37 | public function boot()
38 | {
39 | if (function_exists('config_path')) {
40 | $publishPath = config_path('gcp.php');
41 | } else {
42 | $publishPath = base_path('config/gcp.php');
43 | }
44 |
45 | $this->publishes([
46 | __DIR__ . '/config/config.php' => $publishPath
47 | ], 'config');
48 | }
49 |
50 |
51 | /**
52 | * Get the services provided by the provider.
53 | *
54 | * @return array
55 | */
56 | public function provides()
57 | {
58 | return ['google.print'];
59 | }
60 | }
--------------------------------------------------------------------------------
/src/GoogleCloudPrint.php:
--------------------------------------------------------------------------------
1 | config = $config;
30 | }
31 |
32 |
33 | protected function setUpClient()
34 | {
35 | if ($this->client === null) {
36 | $credentialsPath = $this->credentialsPath ?: $this->config->get('gcp.credentials');
37 |
38 | if ( ! preg_match('/^\//', $credentialsPath)) {
39 | $credentialsPath = base_path($credentialsPath);
40 | }
41 |
42 | putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentialsPath);
43 |
44 | $this->client = new Google_Client();
45 | $this->client->useApplicationDefaultCredentials();
46 | $this->client->addScope(['https://www.googleapis.com/auth/cloudprint']);
47 | }
48 | }
49 |
50 |
51 | protected function requireToken()
52 | {
53 | $this->setUpClient();
54 |
55 | if ($this->client->isAccessTokenExpired()) {
56 | $this->client->refreshTokenWithAssertion();
57 | }
58 |
59 | if ( ! ($accessToken = $this->client->getAccessToken())) {
60 | throw new InvalidCredentialsException();
61 | }
62 |
63 | return $accessToken;
64 | }
65 |
66 |
67 | public function setCredentialsPath($credentialsPath)
68 | {
69 | $this->client = null;
70 | $this->credentialsPath = $credentialsPath;
71 | }
72 |
73 |
74 | public function getAccessToken()
75 | {
76 | $accessToken = $this->requireToken();
77 |
78 | return $accessToken['access_token'];
79 | }
80 |
81 |
82 | public function asText()
83 | {
84 | return new PrintTask($this->getAccessToken(), 'text/plain');
85 | }
86 |
87 |
88 | public function asHtml()
89 | {
90 | return new PrintTask($this->getAccessToken(), 'text/html');
91 | }
92 |
93 |
94 | public function asPdf()
95 | {
96 | return new PrintTask($this->getAccessToken(), 'application/pdf');
97 | }
98 | }
--------------------------------------------------------------------------------
/src/PrintApi.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright 2016 - B&B Web Expertise
7 | */
8 |
9 | namespace Bnb\GoogleCloudPrint;
10 |
11 | class PrintApi
12 | {
13 |
14 | const URL_SEARCH = 'https://www.google.com/cloudprint/search';
15 | const URL_SUBMIT = 'https://www.google.com/cloudprint/submit';
16 | const URL_DELETE_JOB = 'https://www.google.com/cloudprint/deletejob';
17 | const URL_JOBS = 'https://www.google.com/cloudprint/jobs';
18 | const URL_PRINTER = 'https://www.google.com/cloudprint/printer';
19 | const URL_PROCESS_INVITE = 'https://www.google.com/cloudprint/processinvite';
20 |
21 | protected $accessToken;
22 |
23 |
24 | private function __construct()
25 | {
26 |
27 | }
28 |
29 |
30 | /**
31 | * @param string $accessToken OAuth2 offline access token
32 | * @param string $printer Printer ID
33 | * @param array $options Print request post fields
34 | * @param array $headers Print request headers
35 | *
36 | * @return array
37 | */
38 | public static function processInvite($accessToken, $printer)
39 | {
40 | $api = new self;
41 | $api->accessToken = $accessToken;
42 |
43 | $options = [
44 | 'printerid' => $printer,
45 | 'accept' => 'true',
46 | ];
47 |
48 | return $api->makeHttpCall(self::URL_PROCESS_INVITE, $options);
49 | }
50 |
51 |
52 | /**
53 | * @param string $accessToken OAuth2 offline access token
54 | * @param string $printer Printer ID
55 | * @param array $options Print request post fields
56 | * @param array $headers Print request headers
57 | *
58 | * @return array
59 | */
60 | public static function submit($accessToken, $printer, $options, $headers = [])
61 | {
62 | $api = new self;
63 | $api->accessToken = $accessToken;
64 |
65 | $options['printerid'] = $printer;
66 |
67 | if (empty($options['title'])) {
68 | $options['title'] = 'job-' . date('YmdHis') . '-' . rand(1000, 9999);
69 | }
70 |
71 | return $api->makeHttpCall(self::URL_SUBMIT, $options, $headers);
72 | }
73 |
74 |
75 | /**
76 | * Makes http calls to Google Cloud Print using curl
77 | *
78 | * @param string $url Http url to hit
79 | * @param array $postFields Array of post fields to be posted
80 | * @param array $headers Array of http headers
81 | *
82 | * @return mixed
83 | */
84 | private function makeHttpCall($url, $postFields = [], $headers = [])
85 | {
86 | $headers = array_merge($headers, [
87 | "Authorization: Bearer " . $this->accessToken
88 | ]);
89 |
90 | $curl = curl_init($url);
91 |
92 | if ( ! empty($postFields)) {
93 | curl_setopt($curl, CURLOPT_POST, true);
94 | curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields);
95 | }
96 |
97 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
98 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
99 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
100 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
101 |
102 | $response = curl_exec($curl);
103 |
104 | curl_close($curl);
105 |
106 | return $response;
107 | }
108 | }
--------------------------------------------------------------------------------
/tests/GoogleCloudPrintTest.php:
--------------------------------------------------------------------------------
1 | configMock = Mockery::mock('Illuminate\Contracts\Config\Repository')->shouldDeferMissing();
13 | }
14 |
15 |
16 | /**
17 | * @return GoogleCloudPrint
18 | */
19 | private function getService()
20 | {
21 | $this->configMock->shouldReceive('get')
22 | ->with('credentials')
23 | ->once()
24 | ->andReturn(__DIR__ . '/' . getenv('GCP_TEST_CREDENTIALS'));
25 |
26 | return new GoogleCloudPrint($this->configMock);
27 | }
28 |
29 |
30 | private function getPrinterId()
31 | {
32 | return $printerId = getenv('GCP_TEST_PRINTER_ID');
33 | }
34 |
35 |
36 | /** @test */
37 | public function it_authenticates()
38 | {
39 | $gcp = $this->getService();
40 |
41 | $this->assertNotEmpty($gcp->getAccessToken());
42 | }
43 |
44 |
45 | /** @test */
46 | public function it_prints_text()
47 | {
48 | $gcp = $this->getService();
49 | $printer = $this->getPrinterId();
50 |
51 | $printJob = $gcp
52 | ->asText()
53 | ->content(<<printer($printer)
83 | ->send();
84 |
85 | $this->assertNotNull($printJob);
86 | $this->assertEquals('IN_PROGRESS', $printJob->status);
87 | }
88 |
89 |
90 | /** @test */
91 | public function it_prints_pdf_from_file()
92 | {
93 | $gcp = $this->getService();
94 | $printer = $this->getPrinterId();
95 |
96 | $printJob = $gcp
97 | ->asPdf()
98 | ->file(__DIR__ . '/test.pdf')
99 | ->printer($printer)
100 | ->send();
101 |
102 | $this->assertNotNull($printJob);
103 | $this->assertEquals('IN_PROGRESS', $printJob->status);
104 | }
105 |
106 |
107 | /** @test */
108 | public function it_prints_html_from_url_with_range()
109 | {
110 | $gcp = $this->getService();
111 | $printer = $this->getPrinterId();
112 |
113 | $printJob = $gcp
114 | ->asHtml()
115 | ->url('https://opensource.org/licenses/MIT')
116 | ->range(1, 1)
117 | ->marginsInMillimeters(5, 5, 5, 5)
118 | ->printer($printer)
119 | ->send();
120 |
121 | $this->assertNotNull($printJob);
122 | $this->assertEquals('IN_PROGRESS', $printJob->status);
123 | }
124 |
125 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [DEPRECATED] Google Cloud Print service for Laravel 5
2 |
3 | ## Install
4 |
5 | Via composer :
6 |
7 | composer require bnbwebexpertise/laravel-google-cloud-print
8 |
9 | Then add the service provider class to your Laravel `config/app.php` :
10 |
11 | 'providers' => [
12 | // ...
13 | Bnb\GoogleCloudPrint\LaravelServiceProvider::class,
14 | // ...
15 | ],
16 |
17 | Also add the Facade alias if you intend to use it :
18 |
19 | 'aliases' => [
20 | // ...
21 | 'GoogleCloudPrint' => Bnb\GoogleCloudPrint\Facades\GoogleCloudPrint::class,
22 | // ...
23 | ],
24 |
25 | ## Configuration
26 |
27 | Set the env parameter `GCP_CREDENTIALS_PATH` to the absolute path
28 | (or relative to the laravel application root) of the servie account
29 | JSON file downloaded from Google Console.
30 |
31 | ### Google service setup
32 |
33 | Create a service account key (IAM) with a `***@re-speedy-diagnostic.iam.gserviceaccount.com`
34 | and download the JSON key file at [https://console.developers.google.com/apis/credentials](https://console.developers.google.com/apis/credentials).
35 | Copy the file into the project at the configured env path.
36 |
37 | You also need to allow print access to the generated email address on
38 | all the desired printers via the Google Cloud Print console at
39 | [https://www.google.com/cloudprint/#printers](https://www.google.com/cloudprint/#printers).
40 |
41 | This library will attempt to accept the invite if the Google API rejects
42 | the credentials. Indeed Google service accounts do not get the invitation
43 | email with the accept link and therefore need to use the API to complete
44 | the process.
45 |
46 | ## Usage
47 |
48 | ### Create a print task
49 |
50 | Either use the Facade or the shortcut with one of the three provided
51 | content type to get a print task object :
52 |
53 | ```
54 | $task = GoogleCloudPrint::asText()
55 | $task = GoogleCloudPrint::asHtml()
56 | $task = GoogleCloudPrint::asPdf()
57 |
58 | // or
59 |
60 | $task = app('google.print')->asText()
61 | $task = app('google.print')->asHtml()
62 | $task = app('google.print')->asPdf()
63 |
64 | ```
65 |
66 | #### Configure and send the print task
67 |
68 | Calling `->printer($printerId)` is required. The `$printerId` is the
69 | printer's UUID you get on the printer details page at Google Cloud Print
70 | console (or in the printer URL).
71 |
72 | The content can be provided in three way :
73 | - raw via `->content('A raw content')`.
74 | - local file via `->file('/path/to/my/file')`. An exception is thrown if the file is not accessible
75 | - url via `->url('http://acme.foo/bar')`. The content is downloaded locally before sending the print job. An exception is thrown if the URL does not begin with `http(s)://`
76 |
77 | You can set any other Cloud Job Ticket option via the `->ticket($key, $value)` method.
78 | Some helpers are provided :
79 | - range helper via `->range($start, $end)` (start and end pages are included).
80 | - margins helpers via the `->marginsInMillimeters($top, $right, $bottom, $left)` and `->marginsInCentimeters($top, $right, $bottom, $left)`.
81 |
82 |
83 | If the job is rejected an exception is thrown.
84 |
85 | #### Examples
86 |
87 | ```
88 | $printerId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
89 |
90 | // Printing HTML from an URL
91 | GoogleCloudPrint::asHtml()
92 | ->url('https://opensource.org/licenses/MIT')
93 | ->printer($printerId)
94 | ->send();
95 |
96 | // Printing page 3 to 10 of a PDF from a local file
97 | GoogleCloudPrint::asPdf()
98 | ->file('storage/document.pdf')
99 | ->range(3, 10)
100 | ->printer($printerId)
101 | ->send();
102 |
103 | // Printing plain text with a 1cm margin on each sides using
104 | GoogleCloudPrint::asText()
105 | ->content('This is a test')
106 | ->printer($printerId)
107 | ->marginsInCentimeters(1, 1, 1, 1)
108 | ->send();
109 | ```
110 |
--------------------------------------------------------------------------------
/src/PrintTask.php:
--------------------------------------------------------------------------------
1 | accessToken = $accessToken;
36 | $this->contentType = $contentType;
37 | }
38 |
39 |
40 | /**
41 | * @param string $raw The raw content to print
42 | *
43 | * @return self
44 | */
45 | public function content($raw)
46 | {
47 | $this->source = $raw;
48 |
49 | return $this;
50 | }
51 |
52 |
53 | /**
54 | * @param string $file An accessible file path
55 | *
56 | * @return PrintTask
57 | * @throws InvalidSourceException
58 | */
59 | public function file($file)
60 | {
61 | if ( ! file_exists($file)) {
62 | throw new InvalidSourceException();
63 | }
64 |
65 | $this->source = file_get_contents($file);
66 |
67 | return $this;
68 | }
69 |
70 |
71 | /**
72 | * @param string $url An absolute public URL (prefixed by http or https)
73 | *
74 | * @return self
75 | * @throws InvalidSourceException
76 | */
77 | public function url($url)
78 | {
79 | if ( ! preg_match('/^https?:\/\//', $url)) {
80 | throw new InvalidSourceException();
81 | }
82 |
83 | $this->source = file_get_contents($url);
84 |
85 | return $this;
86 | }
87 |
88 |
89 | /**
90 | * @param string $title The task title
91 | *
92 | * @return self
93 | */
94 | public function title($title)
95 | {
96 | $this->title = $title;
97 |
98 | return $this;
99 | }
100 |
101 |
102 | /**
103 | * @param string $printer The printer ID
104 | *
105 | * @return self
106 | */
107 | public function printer($printer)
108 | {
109 | $this->printer = $printer;
110 |
111 | return $this;
112 | }
113 |
114 |
115 | /**
116 | * @param array|string|string... $tags
117 | *
118 | * @return self
119 | */
120 | public function tags($tags)
121 | {
122 | if (is_array($tags)) {
123 | $this->tags = $tags;
124 | } elseif (func_num_args() > 1) {
125 | $this->tags = func_get_args();
126 | } else {
127 | $this->tags[] = $tags;
128 | }
129 |
130 | $this->tags = array_map('str_slug', $this->tags);
131 |
132 | return $this;
133 | }
134 |
135 |
136 | /**
137 | * @param int $start
138 | * @param int $end
139 | *
140 | * @return self
141 | */
142 | public function range($start, $end)
143 | {
144 | if ($start > $end) {
145 | $tmp = $start;
146 | $start = $end;
147 | $end = $tmp;
148 | }
149 |
150 | $this->ticket('page_range', [
151 | 'interval' => [
152 | [
153 | 'start' => $start,
154 | 'end' => $end
155 | ]
156 | ]
157 | ]);
158 |
159 | return $this;
160 | }
161 |
162 |
163 | /**
164 | * Sets the margins in millimeters
165 | *
166 | * @param int $top
167 | * @param int $right
168 | * @param int $bottom
169 | * @param int $left
170 | *
171 | * @return PrintTask
172 | */
173 | public function marginsInMillimeters($top, $right, $bottom, $left)
174 | {
175 | $this->ticket('margins', [
176 | 'top_microns' => $top * 1000,
177 | 'right_microns' => $right * 1000,
178 | 'bottom_microns' => $bottom * 1000,
179 | 'left_microns' => $left * 1000,
180 | ]);
181 |
182 | return $this;
183 | }
184 |
185 |
186 | /**
187 | * Sets the margins in centimeters
188 | *
189 | * @param int $top
190 | * @param int $right
191 | * @param int $bottom
192 | * @param int $left
193 | *
194 | * @return PrintTask
195 | */
196 | public function marginsInCentimeters($top, $right, $bottom, $left)
197 | {
198 | return $this->marginsInMillimeters($top * 10, $right * 10, $bottom * 10, $left * 10);
199 | }
200 |
201 |
202 | /**
203 | * @param string $key
204 | * @param mixed $value
205 | *
206 | * @return self
207 | */
208 | public function ticket($key, $value)
209 | {
210 | $this->printOptions[$key] = $value;
211 |
212 | return $this;
213 | }
214 |
215 |
216 | /**
217 | * @return PrintJob
218 | *
219 | * @throws PrintTaskFailedException
220 | */
221 | public function send()
222 | {
223 | $ticket = [
224 | 'version' => '1.0'
225 | ];
226 |
227 | if ( ! empty($this->printOptions)) {
228 | $ticket['print'] = $this->printOptions;
229 | }
230 |
231 | $job = PrintApi::submit($this->accessToken, $this->printer, [
232 | 'title' => $this->title,
233 | 'contentTransferEncoding' => 'base64',
234 | 'content' => base64_encode($this->source),
235 | 'contentType' => $this->contentType,
236 | 'tag' => join(',', $this->tags),
237 | 'ticket' => json_encode($ticket)
238 | ]);
239 |
240 | if ($job && ($job = json_decode($job))) {
241 |
242 | if ($job->success) {
243 | return new PrintJob($job->job);
244 | }
245 |
246 | if ($job->errorCode === 8 && $this->tryProcessInvite) {
247 | $this->tryProcessInvite = false;
248 |
249 | $invite = PrintApi::processInvite($this->accessToken, $this->printer);
250 |
251 | if ($invite) {
252 | $invite = json_decode($invite);
253 |
254 | if ($invite->success) {
255 | return $this->send();
256 | }
257 | }
258 | }
259 | }
260 |
261 | throw new PrintTaskFailedException(sprintf('The print job submission has failed : %s', json_encode($job ?: 'Unknown error')));
262 | }
263 | }
264 |
--------------------------------------------------------------------------------