@ -0,0 +1,43 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-green.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
Google Meet Calendar Integration |
|||
================================ |
|||
A seamless integration solution for Google Meet within your calendar, |
|||
simplifying scheduling and access to virtual meetings. |
|||
|
|||
License |
|||
------- |
|||
GNU AFFERO GENERAL PUBLIC LICENSE, Version 3 (AGPLv3) |
|||
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
------- |
|||
Developer: (V18) Nivedhya T, Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import controllers |
|||
from . import models |
@ -0,0 +1,46 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
{ |
|||
'name': "Google Meet Calendar Integration", |
|||
'version': '18.0.1.0.0', |
|||
"category": 'Productivity', |
|||
'summary': """A seamless integration solution for Google Meet within your calendar, |
|||
simplifying scheduling and access to virtual meetings.""", |
|||
'description': """Enhance your scheduling efficiency with the Google Meet |
|||
Calendar Integration.This feature-rich application streamlines the process of |
|||
integrating Google Meet links into your calendar events, ensuring a smooth |
|||
and hassle-free virtual meeting experience.""", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['contacts', 'calendar'], |
|||
'data': [ |
|||
'views/res_company_views.xml', |
|||
'views/calendar_event_views.xml' |
|||
], |
|||
'images': ['static/description/banner.jpg'], |
|||
'license': "AGPL-3", |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False |
|||
} |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import odoo_google_meet_integration |
@ -0,0 +1,71 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
import datetime |
|||
import requests |
|||
from odoo import http |
|||
from odoo import _ |
|||
from odoo.exceptions import UserError |
|||
from odoo.http import request |
|||
|
|||
|
|||
class GoogleMeetAuth(http.Controller): |
|||
"""Controller handling Google Meet authentication for Odoo users.""" |
|||
@http.route('/google_meet_authentication', type="http", auth="public", |
|||
website=True) |
|||
def get_auth_code(self, **kw): |
|||
""" Retrieve the authentication code for Google Meet.""" |
|||
user_id = request.uid |
|||
company_id = http.request.env['res.users'].sudo().search( |
|||
[('id', '=', user_id)], limit=1).company_id |
|||
if kw.get('code'): |
|||
company_id.write( |
|||
{'hangout_company_authorization_code': kw.get('code')}) |
|||
client_id = company_id.hangout_client_id |
|||
client_secret = company_id.hangout_client_secret |
|||
redirect_uri = company_id.hangout_redirect_uri |
|||
data = { |
|||
'code': kw.get('code'), |
|||
'client_id': client_id, |
|||
'client_secret': client_secret, |
|||
'redirect_uri': redirect_uri, |
|||
'grant_type': 'authorization_code' |
|||
} |
|||
response = requests.post( |
|||
'https://accounts.google.com/o/oauth2/token', data=data, |
|||
headers={ |
|||
'content-type': 'application/x-www-form-urlencoded'}) |
|||
if response.json() and response.json().get('access_token'): |
|||
company_id.write({ |
|||
'hangout_company_access_token': |
|||
response.json().get('access_token'), |
|||
'hangout_company_access_token_expiry': |
|||
datetime.datetime.now() + datetime.timedelta( |
|||
seconds=response.json().get('expires_in')), |
|||
'hangout_company_refresh_token': |
|||
response.json().get('access_token'), |
|||
}) |
|||
return "Authentication Success. You Can Close this window" |
|||
else: |
|||
raise UserError( |
|||
_('Something went wrong during the token generation.' |
|||
'Maybe your Authorization Code is invalid') |
|||
) |
@ -0,0 +1,6 @@ |
|||
## Module <odoo_google_meet_integration> |
|||
|
|||
#### 10.10.2024 |
|||
#### Version 18.0.1.0.0 |
|||
#### ADD |
|||
- Initial commit for Google Meet Calendar Integration |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import calendar_event |
|||
from . import res_company |
@ -0,0 +1,169 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
import json |
|||
import pytz |
|||
import random |
|||
import requests |
|||
from odoo import api, fields, models |
|||
from odoo.exceptions import ValidationError |
|||
from dateutil.relativedelta import relativedelta |
|||
from datetime import timedelta |
|||
|
|||
|
|||
TIMEOUT = 20 |
|||
|
|||
|
|||
class CalendarEvent(models.Model): |
|||
""" Extends the Odoo Calendar Event model to include Google Meet |
|||
integration fields. |
|||
""" |
|||
_inherit = 'calendar.event' |
|||
|
|||
is_google_meet = fields.Boolean('Google Meet', default=False, |
|||
help="Boolean field indicating if the " |
|||
"event has a Google Meet integration.") |
|||
google_meet_url = fields.Char('Google Meet URL', |
|||
help='Joinging Meeting URL') |
|||
google_meet_code = fields.Char('Google Meet Code', |
|||
help='Joining Meeting Code') |
|||
google_event_id = fields.Char('Google Event ID', |
|||
help='Event ID of the google meet') |
|||
|
|||
def action_google_meet_url(self): |
|||
""" Opens the Google Meet URL associated with the event.""" |
|||
meet_url = self.google_meet_url |
|||
if meet_url: |
|||
url = self.google_meet_url |
|||
else: |
|||
url = 'https://meet.google.com/' |
|||
return { |
|||
'type': 'ir.actions.act_url', |
|||
'target': 'new', |
|||
'url': url, |
|||
} |
|||
|
|||
@api.model_create_multi |
|||
def create(self, vals): |
|||
""" Overrides the create method to handle the creation of Google |
|||
Meet events. |
|||
""" |
|||
events = super(CalendarEvent, self).create(vals) |
|||
for event in events: |
|||
if event.is_google_meet: |
|||
self._create_google_meet(event) |
|||
return events |
|||
|
|||
def write(self, vals): |
|||
"""Overrides the write method to handle updates for Google Meet |
|||
events.""" |
|||
events = super(CalendarEvent, self).write(vals) |
|||
for event in self: |
|||
if event.is_google_meet: |
|||
if not event.google_event_id: |
|||
self._create_google_meet(event) |
|||
return events |
|||
|
|||
def _create_google_meet(self, cal_event): |
|||
"""Creating an event from Google calendar""" |
|||
start_dt = fields.Datetime.now() |
|||
finish_dt = fields.Datetime.now().replace(tzinfo=pytz.timezone('UTC')) |
|||
end_date_user = finish_dt.astimezone(pytz.timezone( |
|||
self.env.user.tz or 'UTC')).replace(tzinfo=None) |
|||
difference = relativedelta(end_date_user, start_dt) |
|||
diff_hrs = difference.hours |
|||
diff_min = difference.minutes |
|||
start = cal_event.start |
|||
stop = cal_event.stop |
|||
start_dt = start + timedelta(hours=diff_hrs, minutes=diff_min) |
|||
start_date = start_dt.isoformat() |
|||
stop_dt = stop + timedelta(hours=diff_hrs, minutes=diff_min) |
|||
stop_date = stop_dt.isoformat() |
|||
current_uid = self._context.get('uid') |
|||
user_id = self.env['res.users'].browse(current_uid) |
|||
company_id = user_id.company_id |
|||
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' |
|||
request_id = ''.join( |
|||
random.SystemRandom().choice(chars) for _ in range(16)) |
|||
url = 'https://www.googleapis.com/calendar/v3/calendars/primary/events?conferenceDataVersion=1&sendNotifications=True' |
|||
header = { |
|||
'Authorization': |
|||
'Bearer %s' % company_id.hangout_company_access_token, |
|||
'Accept': 'application/json', |
|||
'Content-Type': 'application/json' |
|||
} |
|||
event = { |
|||
'summary': cal_event.name, |
|||
'location': cal_event.location or None, |
|||
'conferenceDataVersion': 1, |
|||
'start': { |
|||
'dateTime': start_date, |
|||
'timeZone': user_id.tz, |
|||
}, |
|||
'end': { |
|||
'dateTime': stop_date, |
|||
'timeZone': user_id.tz, |
|||
}, |
|||
'attendees': [{'email': partner.email} for partner in |
|||
cal_event.partner_ids], |
|||
|
|||
'conferenceData': { |
|||
"createRequest": { |
|||
"conferenceSolutionKey": { |
|||
"type": "hangoutsMeet" |
|||
}, |
|||
'requestId': request_id |
|||
} |
|||
}, |
|||
} |
|||
result = requests.post(url, headers=header, timeout=TIMEOUT, |
|||
data=json.dumps(event)).json() |
|||
if result.get('error'): |
|||
company_id.google_meet_company_refresh_token() |
|||
result = requests.post(url, headers=header, timeout=TIMEOUT, |
|||
data=json.dumps(event)).json() |
|||
if result.get('hangoutLink'): |
|||
cal_event.google_event_id = result['id'] |
|||
cal_event.google_meet_url = result['hangoutLink'] |
|||
cal_event.google_meet_code = result['conferenceData'][ |
|||
'conferenceId'] |
|||
else: |
|||
raise ValidationError("Failed to create event," |
|||
"Please check your authorization connection.") |
|||
|
|||
@api.onchange('is_google_meet') |
|||
def _delete_google_meet(self): |
|||
"""Delete an event from google calendar""" |
|||
if not self.is_google_meet: |
|||
event_id = self.google_event_id |
|||
if event_id: |
|||
current_uid = self._context.get('uid') |
|||
user_id = self.env['res.users'].browse(current_uid) |
|||
company_id = user_id.company_id |
|||
url = 'https://www.googleapis.com/calendar/v3/calendars/primary' \ |
|||
'/events/%s' % event_id |
|||
header = { |
|||
'Authorization': |
|||
'Bearer %s' % company_id.hangout_company_access_token} |
|||
requests.delete(url, headers=header, timeout=TIMEOUT) |
|||
self.google_meet_url = '' |
|||
self.google_meet_code = '' |
|||
self.google_event_id = '' |
@ -0,0 +1,105 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
import requests |
|||
from odoo import fields, models, _ |
|||
from odoo.exceptions import ValidationError, UserError |
|||
|
|||
TIMEOUT = 20 |
|||
|
|||
|
|||
class ResCompany(models.Model): |
|||
""" Inherit the company form view""" |
|||
_inherit = "res.company" |
|||
|
|||
hangout_client_id = fields.Char( |
|||
"Client Id", help='Google Developer Console Client ID') |
|||
hangout_client_secret = fields.Char( |
|||
"Client Secret", help='Google Developer Console Client Secret') |
|||
hangout_redirect_uri = fields.Char( |
|||
"Authorized redirect URIs", |
|||
help='GoogleAuthorized redirect URIs') |
|||
hangout_company_access_token = fields.Char( |
|||
'Access Token', copy=False,help="Hangout access token") |
|||
hangout_company_access_token_expiry = fields.Datetime( |
|||
string='Token expiry', help="Hangout access token expiry date") |
|||
hangout_company_refresh_token = fields.Char( |
|||
'Refresh Token', copy=False, help="Hangout refresh token") |
|||
hangout_company_authorization_code = fields.Char( |
|||
string="Authorization Code", help="Hangout authorization code") |
|||
|
|||
def google_meet_company_authenticate(self): |
|||
"""Method for authentication""" |
|||
if not self.hangout_client_id: |
|||
raise ValidationError(_("Please Enter Client ID")) |
|||
client_id = self.hangout_client_id |
|||
if not self.hangout_redirect_uri: |
|||
raise ValidationError(_("Please Enter Client Secret")) |
|||
redirect_url = self.hangout_redirect_uri |
|||
calendar_scope = 'https://www.googleapis.com/auth/calendar' |
|||
calendar_event_scope = 'https://www.googleapis.com/auth/calendar.events' |
|||
url = ( |
|||
"https://accounts.google.com/o/oauth2/v2/auth?response_type=code" |
|||
"&access_type=offline&client_id={}&redirect_uri={}&scope={}+{} " |
|||
).format(client_id, redirect_url, calendar_scope, |
|||
calendar_event_scope) |
|||
return { |
|||
"type": 'ir.actions.act_url', |
|||
"url": url, |
|||
"target": "new" |
|||
} |
|||
|
|||
def google_meet_company_refresh_token(self): |
|||
"""Method to get the refresh token""" |
|||
if not self.hangout_client_id: |
|||
raise UserError( |
|||
_('Client ID is not yet configured.')) |
|||
client_id = self.hangout_client_id |
|||
|
|||
if not self.hangout_client_secret: |
|||
raise UserError( |
|||
_('Client Secret is not yet configured.')) |
|||
client_secret = self.hangout_client_secret |
|||
if not self.hangout_company_refresh_token: |
|||
raise UserError( |
|||
_('Refresh Token is not yet configured.')) |
|||
refresh_token = self.hangout_company_refresh_token |
|||
data = { |
|||
'client_id': client_id, |
|||
'client_secret': client_secret, |
|||
'refresh_token': refresh_token, |
|||
'grant_type': 'refresh_token', |
|||
} |
|||
response = requests.post( |
|||
'https://accounts.google.com/o/oauth2/token', data=data, |
|||
headers={ |
|||
'content-type': 'application/x-www-form-urlencoded'}, |
|||
timeout=TIMEOUT) |
|||
if response.json() and response.json().get('access_token'): |
|||
self.write({ |
|||
'hangout_company_access_token': |
|||
response.json().get('access_token'), |
|||
}) |
|||
else: |
|||
raise UserError( |
|||
_('Something went wrong during the token generation.' |
|||
' Please request again an authorization code.') |
|||
) |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 664 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 767 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 697 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 776 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 119 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 112 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 117 KiB |