[MERGE] latest trunk

bzr revid: abo@openerp.com-20120816092351-rmfsvd0pab2npr25
This commit is contained in:
Antonin Bourguignon 2012-08-16 11:23:51 +02:00
commit 59dadc9a80
153 changed files with 12705 additions and 873 deletions

View File

@ -19,10 +19,10 @@
#
##############################################################################
import partner
import account
import installer
import project
import partner
import account_invoice
import account_bank_statement
import account_bank

View File

@ -1351,11 +1351,11 @@ class account_invoice_line(osv.osv):
'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'),
'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Account')),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
'price_subtotal': fields.function(_amount_line, string='Subtotal', type="float",
digits_compute= dp.get_precision('Account'), store=True),
'quantity': fields.float('Quantity', required=True),
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Account')),
'quantity': fields.float('Quantity', digits_compute= dp.get_precision('Product Unit of Measure'), required=True),
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]),
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),

View File

@ -273,6 +273,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@ -426,6 +427,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -38,7 +38,7 @@
<!--Email template -->
<record id="email_template_edi_invoice" model="email.template">
<field name="name">Automated Invoice Notification Mail</field>
<field name="email_from">${object.user_id.user_email or object.company_id.email or 'noreply@localhost'}</field>
<field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field>
<field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field>
<field name="email_to">${object.partner_id.email or ''}</field>
<field name="model_id" ref="account.model_account_invoice"/>
@ -58,7 +58,7 @@
% if object.origin:
&nbsp;&nbsp;Order reference: ${object.origin}<br />
% endif
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.user_email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
&nbsp;&nbsp;Your contact: <a href="mailto:${object.user_id.email or ''}?subject=Invoice%20${object.number}">${object.user_id.name}</a>
</p>
<p>
@ -133,7 +133,7 @@ A new invoice is available for ${object.partner_id.name}:
% if object.origin:
| Order reference: ${object.origin}
% endif
| Your contact: ${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''}
| Your contact: ${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''}
You can view the invoice document, download it and pay online using the following link:
${ctx.get('edi_web_url_view') or 'n/a'}
@ -160,7 +160,7 @@ Thank you for choosing ${object.company_id.name}!
--
${object.user_id.name} ${object.user_id.user_email and '<%s>'%(object.user_id.user_email) or ''}
${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''}
${object.company_id.name}
% if object.company_id.street:
${object.company_id.street or ''}

10523
addons/account/i18n/es_UY.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,7 @@ class analytic_account(osv.osv):
('name', 'not ilike', 'maintenance'),
('partner_id', '!=', False),
('user_id', '!=', False),
('user_id.user_email', '!=', False),
('user_id.email', '!=', False),
('state', 'in', ('draft', 'open')),
'|', ('date', '<', time.strftime('%Y-%m-%d')), ('date', '=', False),
]
@ -70,7 +70,7 @@ class analytic_account(osv.osv):
for user, data in users.iteritems():
subject = '[OPENERP] Reporting: Analytic Accounts'
body = Template(MAKO_TEMPLATE).render_unicode(user=user, partners=data)
tools.email_send('noreply@openerp.com', [user.user_email, ], subject, body)
tools.email_send('noreply@openerp.com', [user.email, ], subject, body)
return True

View File

@ -111,6 +111,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -240,6 +240,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@ -408,6 +409,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -147,6 +147,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@ -301,6 +302,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -297,7 +297,6 @@ class account_analytic_account(osv.osv):
def create_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_subscribe(cr, uid, [obj.id], [obj.user_id.id], context=context)
self.message_append_note(cr, uid, [obj.id], body=_("Contract for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name), context=context)
account_analytic_account()

View File

@ -45,6 +45,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -255,7 +255,7 @@ class users(osv.osv):
cr, SUPERUSER_ID, conf, login, entry)
if user_id:
cr.execute("""UPDATE res_users
SET date=now() AT TIME ZONE 'UTC'
SET login_date=now() AT TIME ZONE 'UTC'
WHERE login=%s""",
(tools.ustr(login),))
cr.commit()

View File

@ -0,0 +1,3 @@
import controllers
import auth_oauth
import res_users

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP S.A (<http://www.openerp.com>).
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,16 +19,31 @@
#
##############################################################################
from osv import osv, fields
class res_users(osv.osv):
_inherit = 'res.users'
_columns = {
'partner_id': fields.many2one('res.partner',
string='Related Partner'),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
{
'name': 'OAuth2 Authentication',
'version': '1.0',
'category': 'Tools',
'description': """Allow users to login through Google OAuth2.""",
'author': 'Victor Tabuenca',
'maintainer': 'OpenERP s.a.',
'website': 'http://www.openerp.com',
'depends': ['base', 'web'],
'data': [
'auth_oauth_data.xml'
],
'update_xml': [
'auth_oauth_view.xml'
],
'js': [
'static/src/js/auth_oauth.js',
],
'css': [
'static/lib/zocial/css/zocial.css',
],
'qweb': [
'static/src/xml/auth_oauth.xml',
],
'installable': True,
'auto_install': False,
}

View File

@ -0,0 +1,22 @@
from openerp.osv import osv, fields
class auth_oauth_providers(osv.osv):
"""Class defining the configuration values of an OAuth2 provider"""
_name = 'auth.oauth.provider'
_description = 'OAuth2 provider'
_order = 'name'
_columns = {
'name' : fields.char('Provider name', required=True), # Name of the OAuth2 entity, Google, LinkedIn, etc
'client_id' : fields.char('Client ID', required=True), # Our identifier
'auth_endpoint' : fields.char('Authentication URL', required=True), # OAuth provider URL to authenticate users
'scope' : fields.char('Scope'), # OAUth user data desired to access
'validation_endpoint' : fields.char('Validation URL'), # OAuth provider URL to validate tokens
'data_endpoint' : fields.char('Data URL'),
'css_class' : fields.char('CSS class'),
'body' : fields.char('Body'),
'active' : fields.boolean('Active'),
'sequence' : fields.integer(),
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<openerp>
<data noupdate="1">
<record id="provider_facebook" model="auth.oauth.provider">
<field name="name">Facebook Graph</field>
<field name="client_id">facebook_client_id</field>
<field name="auth_endpoint">https://www.facebook.com/dialog/oauth</field>
<field name="scope"></field>
<field name="validation_endpoint">https://graph.facebook.com/me/permissions</field>
<field name="data_endpoint"></field>
<field name="css_class">zocial facebook</field>
<field name="body">Sign in with facebook</field>
<field name="active">True</field>
</record>
<record id="provider_google" model="auth.oauth.provider">
<field name="name">Google OAuth2</field>
<field name="client_id">108010644258-duuhmp6pu7li4tsmnqg7j9rvdeklg0ki.apps.googleusercontent.com</field>
<field name="auth_endpoint">https://accounts.google.com/o/oauth2/auth</field>
<field name="scope">https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile</field>
<field name="validation_endpoint">https://www.googleapis.com/oauth2/v1/tokeninfo</field>
<field name="data_endpoint">https://www.googleapis.com/oauth2/v1/userinfo</field>
<field name="css_class">zocial google</field>
<field name="body">Sign in with google</field>
<field name="active">True</field>
</record>
<record id="provider_twitter" model="auth.oauth.provider">
<field name="name">Twitter OAuth2</field>
<field name="client_id">108010644258-duuhmp6pu7li4tsmnqg7j9rvdeklg0ki.apps.twitterusercontent.com</field>
<field name="auth_endpoint">https://api.twitter.com/oauth/request_token</field>
<field name="scope"></field>
<field name="validation_endpoint">https://api.twitter.com/oauth/authorize</field>
<field name="data_endpoint"></field>
<field name="css_class">zocial twitter</field>
<field name="body">Sign in with twitter</field>
<field name="active">True</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="view_oauth_provider_form">
<field name="name">auth.oauth.provider.form</field>
<field name="model">auth.oauth.provider</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="arch" version="7.0">
<sheet>
<group>
<field name="name" />
<field name="client_id" />
<field name="active" />
</group>
<group>
<field name="auth_endpoint" />
<field name="scope" />
<field name="validation_endpoint" />
<field name="data_endpoint" />
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_oauth_provider_list">
<field name="name">auth.oauth.provider.list</field>
<field name="model">auth.oauth.provider</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="arch" version="7.0">
<field name="name" />
<field name="client_id" />
<field name="active" />
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="action_oauth_provider">
<field name="name">Providers</field>
<field name="res_model">auth.oauth.provider</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_oauth_providers" parent="base.menu_users" name="OAuth Providers" action="action_oauth_provider" sequence="30"/>
</data>
</openerp>

View File

@ -0,0 +1,3 @@
import main
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,47 @@
import logging
import simplejson
import werkzeug.urls
import werkzeug.utils
import openerp.modules.registry
import openerp.addons.web.controllers.main
import openerp.addons.web.common.http as openerpweb
_logger = logging.getLogger(__name__)
class OAuthController(openerpweb.Controller):
_cp_path = '/auth_oauth'
@openerpweb.jsonrequest
def list_providers(self, req, dbname):
registry = openerp.modules.registry.RegistryManager.get(dbname)
with registry.cursor() as cr:
providers = registry.get('auth.oauth.provider')
l = providers.read(cr, 1, providers.search(cr, 1, []))
return l
@openerpweb.httprequest
def signin(self, req, **kw):
state = simplejson.loads(kw['state'])
dbname = state['d']
provider = state['p']
registry = openerp.modules.registry.RegistryManager.get(dbname)
with registry.cursor() as cr:
try:
u = registry.get('res.users')
credentials = u.auth_oauth(cr, 1, provider, kw)
cr.commit()
return openerp.addons.web.controllers.main.login_and_redirect(req, *credentials)
except AttributeError:
# auth_signup is not installed
_logger.error("auth_signup not installed on database %s: oauth sign up cancelled."%dbname)
url = "/#action=login&oauth_error=1"
except Exception,e:
# signup error
_logger.exception('oops')
url = "/#action=login&oauth_error=2"
return openerp.addons.web.controllers.main.set_cookie_and_redirect(req, url)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,70 @@
import logging
import urllib
import urllib2
import simplejson
import openerp
from openerp.osv import osv, fields
_logger = logging.getLogger(__name__)
class res_users(osv.Model):
_inherit = 'res.users'
_columns = {
'oauth_provider_id': fields.many2one('auth.oauth.provider','OAuth Provider'),
'oauth_uid': fields.char('OAuth User ID', help="Oauth Provider user_id"),
'oauth_access_token': fields.char('OAuth Token', readonly=True),
}
def auth_oauth_rpc(self, cr, uid, endpoint, access_token, context=None):
params = urllib.urlencode({'access_token':access_token})
url = endpoint + '?' + params
f = urllib2.urlopen(url)
response = f.read()
return simplejson.loads(response)
def auth_oauth(self, cr, uid, provider, params, context=None):
# Advice by Google (to avoid Confused Deputy Problem)
# if validation.audience != OUR_CLIENT_ID:
# abort()
# else:
# continue with the process
access_token = params.get('access_token')
p = self.pool.get('auth.oauth.provider').browse(cr, uid, provider, context=context)
validation = self.auth_oauth_rpc(cr, uid, p.validation_endpoint, access_token)
if validation.get("error"):
raise openerp.exceptions.AccessDenied
login = validation['email']
oauth_uid = validation['user_id']
name = self.auth_oauth_rpc(cr, uid, p.data_endpoint, access_token)['name']
credentials = (cr.dbname, login, access_token)
res = self.search(cr, uid, [("oauth_uid", "=", oauth_uid)])
if res:
self.write(cr, uid, res[0], {'oauth_access_token':access_token})
else:
# New user
new_user = {
'name': name,
'login': login,
'user_email': login,
'oauth_provider_id': 1,
'oauth_uid': oauth_uid,
'oauth_access_token': access_token,
'active': True,
}
self.auth_signup_create(cr, uid, new_user)
return credentials
def check_credentials(self, cr, uid, password):
try:
return super(res_users, self).check_credentials(cr, uid, password)
except openerp.exceptions.AccessDenied:
res = self.search(cr, 1, [('id','=',uid),('oauth_access_token','=',password)])
if not res:
raise
#

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_users_form" model="ir.ui.view">
<field name="name">res.users.form</field>
<field name="model">res.users</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//page[@string='Access Rights']" position="after">
<page string="Oauth">
<group>
<field name="oauth_provider"/>
<field name="oauth_uid"/>
<field name="oauth_access_token"/>
</group>
</page>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,31 @@
# Zocial CSS social buttons
I basically rewrote this entire set so they are full vector buttons, meaning:
- @font-face icons
- custom font file for all social icons
- icon font use private unicode spaces for accessibility
- em sizing based on button font-size
- support for about 83 different services
- buttons and icons supported
- no raster images (sweet)
- works splendidly on any browser supporting @font-face
- CSS3 degrades gracefully in IE8 and below etc.
- also includes generic icon-less primary and secondary buttons
## How to use these buttons
<button class='zocial facebook'>Button label here</button>
or
<a class="zocial twitter'>Button label</a>
- Can be any element e.g. `a`, `div`, `button` etc.
- Add class of `.zocial`
- Add class for name of service e.g. `.dropbox`, `.twitter`, `.github`
- Done :-)
Check out [zocial.smcllns.com](http://zocial.smcllns.com) for demo and code examples.
Problems, questions or requests to [@smcllns](http://twitter.com/smcllns)

View File

@ -0,0 +1,129 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata></metadata>
<defs>
<font id="ZocialRegular" horiz-adv-x="2048" >
<font-face units-per-em="2048" ascent="1638" descent="-410" />
<missing-glyph horiz-adv-x="512" />
<glyph unicode=" " horiz-adv-x="512" />
<glyph unicode="&#x09;" horiz-adv-x="512" />
<glyph unicode="&#xa0;" horiz-adv-x="512" />
<glyph unicode="&#xa3;" horiz-adv-x="3291" />
<glyph unicode="&#x2000;" horiz-adv-x="987" />
<glyph unicode="&#x2001;" horiz-adv-x="1974" />
<glyph unicode="&#x2002;" horiz-adv-x="987" />
<glyph unicode="&#x2003;" horiz-adv-x="1974" />
<glyph unicode="&#x2004;" horiz-adv-x="658" />
<glyph unicode="&#x2005;" horiz-adv-x="493" />
<glyph unicode="&#x2006;" horiz-adv-x="329" />
<glyph unicode="&#x2007;" horiz-adv-x="329" />
<glyph unicode="&#x2008;" horiz-adv-x="246" />
<glyph unicode="&#x2009;" horiz-adv-x="394" />
<glyph unicode="&#x200a;" horiz-adv-x="109" />
<glyph unicode="&#x202f;" horiz-adv-x="394" />
<glyph unicode="&#x205f;" horiz-adv-x="493" />
<glyph unicode="&#xe000;" horiz-adv-x="1024" d="M0 0z" />
<glyph unicode="&#xe001;" horiz-adv-x="2650" d="M0 823q0 125 37 236t102 196.5t154.5 156t192 119.5t216.5 83t227.5 49.5t225.5 15.5q59 0 88 -2q232 -12 463 -84q140 -45 272.5 -113.5t255.5 -165t214.5 -209t146.5 -255t55 -293.5q0 -27 -2 -39q-9 -128 -56.5 -243.5t-122 -205.5t-174.5 -164.5t-210.5 -126.5 t-233.5 -85q-222 -62 -469 -62h-16q-172 0 -346.5 25.5t-298.5 83.5q-11 4 -14 12q0 4 6 4q2 0 7 -1t7 -1q101 -24 182 -24q34 0 70 6t73.5 19.5t61 41t23.5 64.5q0 23 -13 49q-18 33 -119.5 68.5t-233.5 68.5t-155 41q-163 54 -284 135q1 1 7 18.5t11.5 30.5t14.5 25t18 12 h367q15 0 28.5 -28t24.5 -61t36 -61t60 -28q36 0 63 24t27 60q0 22 -172.5 407t-216.5 474q-13 30 -33 30q-15 0 -26 -18l-6 -12q-16 -31 -107 -234l-219 -487l-14 -31q-90 103 -142.5 226.5t-52.5 252.5zM446 483q0 10 111 265l10 6q13 0 67 -131t54 -142q0 -8 -10 -8h-221 q-11 0 -11 10zM1075 231q0 -92 96 -92q97 0 97 92v795q0 92 -97 92q-96 0 -96 -92v-795zM1454 231q0 -92 96 -92q97 0 97 92v371l188 -379q11 -25 34 -42t50 -17q31 0 56.5 16.5t37.5 42.5l187 379v-371q0 -92 96 -92q98 0 98 92v795q0 92 -96 92q-71 0 -96 -59l-283 -619 l-272 619q-27 59 -99 59q-94 0 -94 -92v-795z" />
<glyph unicode="&#xe002;" horiz-adv-x="2066" d="M0 -348v1026h1026v-1026h-1026zM1042 674v1026h1024v-1026h-1024z" />
<glyph unicode="&#xe003;" horiz-adv-x="1994" d="M0 -147l397 1824h897q81 0 159 -23.5t148.5 -72t117 -122.5t64 -174t-10.5 -228q-59 -278 -263 -438t-479 -160h-375l-129 -606h-526zM205 -371h526l129 604h377q179 0 332 70t261 206.5t148 321.5q57 277 -88 449q68 -153 25 -348q-59 -278 -262.5 -438t-478.5 -160 h-375l-129 -604h-443zM731 807h170l86 375h256q5 0 35 -6q-18 60 -65 95.5t-113 35.5h-256zM987 809q98 3 183 75t108 173q1 4 2 12t2 12h-233z" />
<glyph unicode="&#xe004;" horiz-adv-x="1959" d="M0 -137v1052q0 180 41 320t125.5 242.5t220.5 156.5t318 54h589h533q-11 -11 -108 -109.5t-205 -207t-223 -223.5t-193.5 -190t-85.5 -75q-13 0 -22 10t-9 22v320h-98q-79 0 -135 -5.5t-107 -20.5t-83 -41.5t-56 -68.5t-34.5 -102t-10.5 -141v-537zM137 -377 q11 11 108 109.5t205 207t223 223.5t193.5 190t85.5 75q13 0 22 -10t9 -23v-319h98q79 0 135 5.5t106.5 20.5t82.5 41.5t55.5 68.5t34 101.5t10.5 141.5v536l459 457v-1053q0 -180 -41 -320t-125 -242t-220 -156t-318 -54h-590h-533z" />
<glyph unicode="&#xe005;" horiz-adv-x="1886" d="M0 453v551q0 52 38 90.5t91 38.5q54 0 91.5 -38t37.5 -91v-551q0 -54 -37.5 -91.5t-91.5 -37.5t-91.5 37.5t-37.5 91.5zM354 248v856h1176v-856q0 -54 -38 -91.5t-91 -37.5h-918q-54 0 -91.5 37.5t-37.5 91.5zM354 1200h1176q0 157 -83 282t-216.5 191t-288.5 66 t-288.5 -66t-216.5 -191t-83 -282zM561 1847q0 17 14 17q7 0 15 -4l100 -183l-31 -16q-2 4 -29 52t-48 88t-21 46zM592 195q0 56 38.5 92.5t96.5 36.5q53 0 91 -38t38 -91v-414q0 -57 -39 -93t-96 -36q-54 0 -91.5 37.5t-37.5 91.5v414zM602 1464q0 32 24 56t58 24 q32 0 56 -24t24 -56q0 -34 -24 -58t-58 -24q-32 0 -56 24t-24 58zM1028 195q0 56 39.5 92.5t95.5 36.5q54 0 91.5 -38t37.5 -91v-414q0 -57 -39 -93t-96 -36q-54 0 -91.5 37.5t-37.5 91.5v414zM1120 1464q0 32 24 56t58 24q32 0 56 -24t24 -56q0 -34 -24 -58t-58 -24 q-32 0 -56 24t-24 58zM1194 1677q2 4 28.5 58t49.5 91.5t37 37.5t14 -19v-4l-98 -182zM1626 453v548q0 54 37.5 93t91.5 39t91.5 -39t37.5 -93v-548q0 -54 -38 -91.5t-91 -37.5q-54 0 -92 37.5t-37 91.5z" />
<glyph unicode="&#xe006;" horiz-adv-x="1687" d="M0 659q0 222 59 414q58 190 170 326q115 139 277 209q165 73 364 73q262 0 445 -125q185 -121 278 -321q95 -198 95 -401q0 -189 -97 -246q-95 -56 -270 -56h-866q0 -175 61 -303q59 -133 156 -196q103 -68 217 -68q81 0 145 21q65 20 127 67q65 46 115 97q52 52 135 141 q29 33 96 33q71 0 111 -43q43 -43 43 -119q0 -68 -47 -160q-44 -87 -144 -174q-92 -83 -237 -139t-330 -56q-427 0 -665 277t-238 749zM455 819h802q-15 257 -122 385q-106 127 -279 127q-167 0 -272 -129q-108 -129 -129 -383z" />
<glyph unicode="&#xe007;" horiz-adv-x="1169" d="M0 -150v1610q0 91 64 155t155 64h731q91 0 155 -64t64 -155v-1610q0 -91 -64 -155t-155 -64h-731q-91 0 -155 64t-64 155zM68 70h1030v1245h-1030v-1245zM408 1497q0 -20 13.5 -33.5t33.5 -13.5h260q20 0 33.5 13.5t13.5 33.5q0 19 -13.5 32t-33.5 13h-260 q-20 0 -33.5 -13t-13.5 -32zM496 -147q0 -38 28 -65.5t66 -27.5t65 27.5t27 65.5t-27 65t-65 27t-66 -27t-28 -65z" />
<glyph unicode="&#xe008;" horiz-adv-x="2265" d="M0 324v979q0 94 29 122.5t123 28.5h1962q93 0 121 -28.5t28 -122.5v-979q0 -94 -27.5 -122t-121.5 -28h-832v-76h227v-118h-755v118h227v76h-829q-94 0 -123 28.5t-29 121.5zM76 307h2111v1057h-2111v-1057z" />
<glyph unicode="&#xe009;" d="M0 1192q0 52 33 93.5t84 53.5l403 97q-129 -147 -129 -344q0 -381 518 -580q44 -17 86.5 -37t93 -52t87 -66t61.5 -80t25 -93q0 -59 -36.5 -103t-89 -63.5t-110.5 -19.5q-119 0 -248 46.5t-215 123.5l-162 -367q113 -61 295 -125q-22 -5 -64.5 -17.5t-78.5 -20t-66 -7.5 q-52 0 -93.5 33t-53.5 84l-336 1407q-4 24 -4 37zM858 1098q0 53 37.5 91.5t87.5 54.5t102 16q100 0 225.5 -36t180.5 -96l125 349q-188 100 -381 129q10 2 94 24.5t143 35.5t89 13q53 0 94 -32.5t53 -83.5l336 -1407q4 -24 4 -37q0 -53 -33 -94.5t-84 -53.5l-297 -69 q97 146 97 311q0 118 -42 217t-117 171t-164.5 122.5t-199.5 87.5q-42 14 -81 30t-91 44t-89 57.5t-63 71t-26 84.5z" />
<glyph unicode="&#xe00a;" horiz-adv-x="2424" d="M0 -2q0 86 48.5 169t130.5 146.5t199 102.5t245 39h110q-86 82 -86 176q0 58 33 119q-20 -2 -59 -2q-213 0 -348.5 132.5t-135.5 327.5q0 122 78.5 230t205.5 171.5t267 63.5h635l-141 -102h-201q96 -37 150.5 -139.5t54.5 -227.5q0 -96 -49 -181.5t-131 -150.5 q-75 -59 -98 -93.5t-23 -86.5q0 -39 55 -98.5t111 -99.5q124 -88 172.5 -180t48.5 -232q0 -87 -42 -168t-118.5 -146t-197 -104t-265.5 -39q-177 0 -324 44.5t-236 131t-89 197.5zM229 66q0 -140 119.5 -240t298.5 -100q243 0 353 89.5t110 239.5q0 39 -8 66 q-6 22 -14.5 41.5t-25.5 39t-28 32.5t-39.5 34.5t-41.5 30.5t-52 35.5t-53 36.5q-81 24 -158 24q-120 1 -226.5 -43t-170.5 -120.5t-64 -165.5zM365 1210q22 -161 123.5 -277.5t224.5 -119.5q80 -3 141 46.5t88.5 138t13.5 194.5q-22 161 -122.5 274.5t-223.5 116.5 q-123 4 -194 -104.5t-51 -268.5zM1501 1161v103h410v409h102v-409h412v-103h-412v-411h-102v411h-410z" />
<glyph unicode="&#xe00b;" horiz-adv-x="2265" d="M0 1378v43l4 4h510l4 -4v-41q0 -16 -20 -16l-54 -2q-67 -3 -67 -41q0 -25 14 -57q104 -250 455 -992l12 -2l227 453q-27 61 -45 100l-155 307q-15 26 -25 46.5t-13 27.5l-3 8q-4 9 -12 25q-44 87 -61 104q-19 16 -64 23q-29 3 -29 14v43l6 4h403l11 -2v-43q0 -16 -21 -16 l-30 -4q-27 -4 -39.5 -10t-11 -24.5t9 -36.5t28.5 -62l150 -307l166 331q34 64 10 88q-13 18 -76 23l-18 2q-9 0 -15 6q-6 3 -6 12v39l6 4q118 2 381 0l4 -4v-41q0 -16 -18 -16q-45 -1 -62 -8.5t-34 -32.5q-48 -73 -62 -98l-215 -402l-6 -14l262 -537l17 -6l413 985 q24 58 -6 84q-15 14 -30.5 20t-49.5 7l-39 2q-6 0 -12 6q-8 4 -8 12v41l6 4h469l4 -4v-41q0 -15 -18 -18q-94 -3 -136 -39q-41 -33 -69 -100q-168 -388 -296 -678.5t-172 -386.5l-44 -97q-21 -48 -46 -62t-49 3t-46 62q-84 169 -250 538q-207 -429 -269 -541 q-28 -49 -54.5 -64t-49 1.5t-41.5 60.5q-65 153 -265.5 583t-266.5 587q-34 85 -58 106t-106 27q-29 3 -29 14z" />
<glyph unicode="&#xe00c;" horiz-adv-x="1923" d="M-0.5 1438q-2.5 18 3.5 34t15 29t27 26.5t35 23.5t44 22t48 21t54 22.5t55 23.5q111 46 199 33.5t110 -71.5q51 -136 209.5 -652.5t236.5 -742.5q528 173 668 215q2 0 24 4t28 4.5t24 0t26.5 -5.5t20.5 -15.5t20 -26.5q48 -96 68 -203t0 -151q-39 -30 -122 -65.5 t-154 -59.5t-184.5 -59.5t-149.5 -47.5q-21 -7 -78.5 -27t-108 -37t-118.5 -39t-124.5 -36t-110.5 -23.5t-91.5 -7t-52.5 18.5q-38 38 -66.5 107t-58.5 178t-39 135q-85 254 -230.5 690t-214.5 643q-10 21 -12.5 39z" />
<glyph unicode="&#xe00d;" horiz-adv-x="1593" d="M6 -285v1588h189q0 116 11 156q24 90 127 99q13 1 27 1q24 0 44.5 -3t36 -10.5t27.5 -14t21 -21t15 -23.5t10 -29t6 -30.5t3 -35t1 -35v-37.5v-17h113v17q0 114 16 157q33 81 148 82q27 0 49.5 -4.5t38.5 -11t29 -19.5t21 -23.5t13.5 -31t8.5 -33t4.5 -38.5t1.5 -39.5 v-42.5v-13h110v13v42.5t1.5 39.5t4.5 38.5t8.5 33.5t13.5 30.5t21 23.5t29 19.5t38.5 11t49.5 4.5q24 0 44.5 -3t36 -10.5t27.5 -14t21 -21t15 -23.5t10 -29t6 -30.5t3 -35t1 -35v-37.5v-17h188v-1588h-1589zM139 78v-230h285v230h-285zM139 123h285v244h-285v-244zM139 412 h285v266h-285v-266zM139 721h285v229h-285v-229zM285 1137q0 -37 18.5 -52.5t56.5 -15.5t57 15.5t19 52.5v266q0 37 -18.5 52t-57.5 15q-38 0 -56.5 -15t-18.5 -52v-266zM467 78v-230h311v230h-311zM467 123h311v244h-311v-244zM467 412h311v266h-311v-266zM467 721h311v229 h-311v-229zM725 1137q0 -37 19 -52.5t57 -15.5q78 0 78 68v266q0 37 -19.5 52t-58.5 15t-57.5 -15t-18.5 -52v-266zM821 78v-230h312v230h-312zM821 123h312v244h-312v-244zM821 412h312v266h-312v-266zM821 721h312v229h-312v-229zM1165 1137q0 -68 78 -68q38 0 57 15.5 t19 52.5v266q0 37 -18.5 52t-57.5 15t-58.5 -15t-19.5 -52v-266zM1176 78v-230h286v230h-286zM1176 123h286v244h-286v-244zM1176 412h286v266h-286v-266zM1176 721h286v229h-286v-229z" />
<glyph unicode="&#xe00e;" horiz-adv-x="2680" d="M0 225v371h459v-367q0 -59 41.5 -100t99.5 -41q60 0 101.5 41t41.5 100v867q0 158 81.5 291t219 209.5t297.5 76.5t297.5 -77t219 -211t81.5 -293v-189l-274 -82l-182 86v164q0 58 -41.5 99.5t-100.5 41.5q-60 0 -101.5 -41t-41.5 -100v-854q0 -161 -80.5 -297.5 t-218 -215.5t-299.5 -79q-122 0 -233.5 47.5t-191.5 127.5t-127.5 191.5t-47.5 233.5zM1483 219v375l182 -86l274 82v-377q0 -60 41.5 -101.5t100.5 -41.5t100 41.5t41 101.5v383h459v-371q0 -122 -47.5 -233.5t-127.5 -191.5t-190.5 -127.5t-232.5 -47.5 q-162 0 -300.5 79.5t-219 216.5t-80.5 298z" />
<glyph unicode="&#xe00f;" horiz-adv-x="2576" d="M-10 401h243q11 -82 61.5 -131t131.5 -49q97 0 156 67t59 167q0 98 -57.5 161.5t-153.5 63.5q-41 0 -70 -10.5t-46 -25t-32 -29t-37 -25t-52 -10.5q-109 0 -164 4q18 106 57.5 328t58.5 337h676v-200h-497q-37 -187 -50 -275h7q34 43 102.5 65.5t134.5 22.5 q112 0 197 -55t131 -156q50 122 147.5 192t227.5 70q77 0 140 -18t122.5 -63t93 -77.5t98.5 -104.5q15 -17 23 -25q107 123 157 166q126 110 278 119q16 1 32 1q184 0 296.5 -123.5t112.5 -310.5q0 -124 -48.5 -225t-144 -162t-220.5 -61q-237 0 -463 268 q-22 -21 -69.5 -67.5t-69 -66.5t-64.5 -52.5t-76.5 -46.5t-83 -25.5t-104.5 -11.5q-129 0 -227.5 67.5t-151.5 188.5q-62 -128 -168.5 -195.5t-247.5 -67.5q-125 0 -223.5 39.5t-160.5 127.5t-62 214zM1044 463q0 -87 52 -140t139 -53q152 0 317 195q-93 95 -131 125 q-101 82 -194 82q-81 0 -132 -63t-51 -146zM1841 463q41 -44 69 -70.5t70.5 -59t87.5 -48t93 -15.5q87 0 137.5 56.5t50.5 144.5q0 87 -52 144t-138 57q-41 0 -78 -12t-71 -37t-56.5 -46t-58.5 -59t-54 -55z" />
<glyph unicode="&#xe010;" horiz-adv-x="1585" d="M0 938q0 102 33 204.5t103 198.5t167.5 169.5t237 118t301.5 44.5q163 0 304.5 -56.5t236 -150t148.5 -215.5t54 -252q0 -229 -79 -412.5t-224.5 -288.5t-331.5 -105q-92 0 -173 43t-113 104l-82 -324q-10 -38 -26.5 -78.5t-35.5 -77t-39 -69.5t-40 -61.5t-34 -48 t-26 -35.5l-12 -15q-6 -7 -16.5 -4.5t-12.5 11.5q0 2 -3.5 28.5t-6.5 56.5t-5.5 77t-2 91.5t5.5 97t16 97.5q23 95 152 641q-17 33 -27 79t-11 76l-1 29q0 131 66 218.5t161 87.5q76 0 118 -50.5t42 -126.5q0 -64 -48.5 -223t-56.5 -194q-21 -92 36 -160t151 -68 q108 0 194 86t132 228.5t46 312.5q0 199 -129.5 325t-359.5 126q-130 0 -240.5 -46.5t-184 -124.5t-114.5 -180t-41 -213q0 -136 78 -231q16 -20 19 -31t-2 -35q-3 -10 -12.5 -47t-12.5 -47q-4 -22 -20.5 -30t-36.5 1q-120 49 -181 169.5t-61 279.5z" />
<glyph unicode="&#xe011;" d="M0.5 624q-5.5 136 30.5 273q34 135 101.5 255t159 212.5t205.5 162.5t240 105t262 40.5t273 -30.5q135 -34 255 -101.5t212.5 -159t162.5 -205.5t105 -240t40.5 -262t-30.5 -273q-34 -135 -101.5 -255t-159 -212.5t-205.5 -162.5t-240 -105t-262 -40.5t-273 30.5 q-135 34 -255 101.5t-212.5 159t-162.5 205.5t-105 240t-40.5 262zM498 342l147 -37q14 -3 40.5 -10t39.5 -10l-47 -187l113 -28l45 184q8 -2 40.5 -10t49.5 -13l-45 -184l112 -27l45 187q98 -17 175.5 -16t135.5 42t87 125q65 185 -105 273q124 27 144 164q14 96 -47 157.5 t-179 100.5l47 186l-112 27l-45 -181q-17 5 -49.5 13t-40.5 10l45 180l-113 29l-45 -184q-28 4 -72 16l-155 39l-29 -121l80 -18q57 -15 53 -66l-53 -211q4 0 12 -4q-2 1 -6 2t-6 2l-74 -295q-10 -36 -51 -26l-68 16q-6 2 -14 4zM872 389l62 248q109 -26 122 -30 q190 -60 163 -173q-6 -25 -22.5 -42.5t-35.5 -26t-48 -11t-51.5 -2t-56 6.5t-51.5 10t-47.5 11.5t-34.5 8.5zM963 750l55 225q7 -2 24 -6q45 -10 73 -18.5t71 -28.5t60 -51.5t7 -73.5q-6 -26 -21.5 -43.5t-39 -24t-46 -9t-53.5 2t-49.5 8t-47 11t-33.5 8.5z" />
<glyph unicode="&#xe012;" horiz-adv-x="2312" d="M0 1223h158l219 -744l147 500l-71 244h157l219 -744l220 744h157h436v-74l-223 -385q119 -41 176 -137q62 -103 62 -234q0 -167 -88 -280t-230 -113q-104 0 -184 68q-79 70 -115 182l123 51q26 -68 72 -111q41 -38 104 -38q64 0 107 69q45 68 45 172q0 108 -47 170 q-55 70 -170 70h-62v74l211 364h-254l-14 -24l-311 -1047h-15l-225 758l-227 -758h-15zM1675 369l37 -103q31 -69 68 -119q88 -120 192 -137q93 -17 180 37q42 26 72 64q41 41 57 73q9 14 23 41l-23 121q-17 -32 -33.5 -57t-23.5 -34l-8 -9q-71 -76 -125 -101 q-40 -18 -69.5 -22.5t-69.5 0.5q-56 4 -123 49q-1 1 -16.5 14t-25.5 22t-24 24t-22 28q-27 37 -66 109zM1731 768q0 173 67 299q32 62 76 99t88 40q71 7 143 -53q37 -30 76 -102l90 178l25 -156l-90 -174q-16 33 -39 61.5t-38 41.5l-15 12q-36 25 -66 31.5t-66 -1.5 q-48 -9 -96 -60.5t-71 -121.5q-39 -113 -39 -217q-2 -82 10 -129q-24 45 -38 108t-16 104z" />
<glyph unicode="&#xe013;" horiz-adv-x="2394" d="M0 772q0 106 42.5 202t114 166t170.5 111t208 41q175 0 312.5 -98t192.5 -254l136 131l323 -311l895 913v-829l-547 -422l70 -74l-743 -723l-648 629q-143 2 -264 72t-191.5 188.5t-70.5 257.5zM76 772q0 -184 132.5 -313t321.5 -129q70 0 134 18l450 -399l1198 942 l2 618l-1184 -1220l-184 299q41 87 41 184q0 183 -134 312.5t-323 129.5t-321.5 -129.5t-132.5 -312.5zM174 768q0 144 101.5 245t244.5 101q145 0 245.5 -100.5t100.5 -245.5q0 -143 -101 -244.5t-245 -101.5q-142 0 -244 102t-102 244z" />
<glyph unicode="&#xe014;" horiz-adv-x="1454" d="M33 1268h1431l-135 -1454l-581 -166l-582 166zM166 1391v272h92v-90h82v90h90v-272h-90v90h-82v-90h-92zM299 971l49 -545l2 4h615l-21 -231l-196 -56l-199 58l-12 139h-181l25 -279l367 -104l366 104l51 547h-653l-16 183h686l16 180h-899zM471 1571v92h250v-92h-80 v-180h-90v180h-80zM760 1391v272h96l55 -94l58 94h96v-272h-88v139l-66 -103v3l-61 96v-135h-90zM1112 1391v272h90v-180h127v-92h-217z" />
<glyph unicode="&#xe015;" horiz-adv-x="1931" d="M-0.5 -39.5q2.5 52.5 14.5 94.5q48 171 87 270q201 511 613 844q63 51 130 97v28q-122 -51 -243.5 -134.5t-208 -166t-156 -158t-104.5 -122.5l-34 -47q-2 89 17 174t51.5 151t72.5 124.5t80.5 99.5t74 71.5t54.5 45.5l21 14q7 4 20.5 11.5t57.5 28t89 37t112.5 34 t132.5 25t143 4t150 -23.5q83 60 168 104.5t147 64.5t112.5 31.5t76.5 13.5l26 1q59 6 103 -6t68.5 -37.5t38.5 -59.5t16 -73t-1 -78.5t-11 -75.5t-16 -64t-14 -45l-6 -17l-10 10q44 116 42.5 191t-36 115.5t-75 59.5t-73.5 22l-34 3q-9 0 -24.5 -1t-65 -10.5t-100.5 -25.5 t-124 -51.5t-141 -83.5q111 -18 203.5 -61t155 -99.5t112.5 -125t78 -139.5t48 -140.5t27 -130.5t10 -108t2 -74l-1 -27h-1141q-4 -63 9.5 -114t38 -82.5t55 -54.5t61.5 -33.5t56.5 -17t42.5 -7.5h16q9 0 23.5 1t53.5 11.5t71 28.5t64 58t44 95h598q-26 -95 -71 -176.5 t-99 -138.5t-117 -103t-126.5 -74.5t-125 -49.5t-115 -30.5t-94 -15t-63.5 -6.5h-23q-160 0 -219 12q-77 -61 -161 -105.5t-150 -65t-120.5 -32t-83.5 -13.5l-30 -1q-76 0 -132.5 31.5t-85 80t-42 107t-11 111zM154.5 -55q-0.5 -48 5.5 -87t15 -58l9 -19q4 -4 11.5 -9.5 t34.5 -18.5t58.5 -21t84 -9.5t109.5 9.5t135.5 42.5t162.5 82.5q-16 4 -43.5 11.5t-103.5 36.5t-141 64.5t-136.5 97t-109.5 132.5q-45 -69 -68 -137.5t-23.5 -116.5zM733 813h543q3 74 -23.5 129t-65.5 80t-79 39.5t-68 16.5l-28 1q-6 0 -16 -0.5t-39.5 -6t-56.5 -14.5 t-59.5 -29t-55.5 -47t-38 -71t-14 -98z" />
<glyph unicode="&#xe016;" horiz-adv-x="1843" d="M86 1190q3 33 31 65l245 246q12 10 25 8t19 -16l198 -373q16 -33 -10 -63l-90 -90q-25 -25 -29 -64q0 -38 26 -92t63 -104t74 -94t63 -71l26 -26q10 -10 27.5 -27t68 -60.5t96.5 -76.5t100.5 -60t92.5 -26q39 0 66 27l106 106q11 11 29.5 14t32.5 -5l358 -211 q14 -8 16 -21t-8 -24l-246 -246q-32 -28 -65 -31q-73 -10 -160 9.5t-167.5 59t-161 89.5t-146 102.5t-117 96t-79.5 70.5l-29 28q-11 11 -29 29.5t-69 78t-97 119t-100.5 144.5t-91.5 162.5t-57.5 165.5t-10.5 161z" />
<glyph unicode="&#xe017;" d="M0 651q0 208 81 397.5t218.5 327t327 218.5t397.5 81t397.5 -81t327 -218.5t218.5 -327t81 -397.5t-81 -397.5t-218.5 -327t-327 -218.5t-397.5 -81t-397.5 81t-327 218.5t-218.5 327t-81 397.5zM264 651q0 -29 2 -48.5t8.5 -43.5t22.5 -40.5t41 -24.5q14 -5 35 -9 q85 -3 174 82q82 73 119 185q50 155 20 409q-4 24 -4 35q-6 12 0 18q3 6 27 -2q80 -20 159 -59q96 -44 209 -143q94 -82 178 -183q79 -94 164 -213q83 -116 180 -157q47 -22 80.5 -17t56 21.5t36 49t19 66.5t5.5 74q0 98 -24 197q-4 18 -17 57q-29 89 -84 172 q-106 161 -277.5 253.5t-363.5 92.5q-137 0 -256 -45q-164 -57 -288 -182.5t-181 -290.5q-3 -9 -9 -28.5t-9 -28.5q-26 -106 -23 -197z" />
<glyph unicode="&#xe018;" horiz-adv-x="2498" d="M0 1370q-3 10 5 19.5t18 11.5q22 4 62.5 13.5t57.5 12.5q1 34 2 100.5t2 100.5q3 25 27 25q11 0 20 -8t9 -21q-4 -122 -4 -186q23 5 71 15l72 15q38 7 113.5 20.5t113.5 20.5q830 142 1626 148q12 0 19.5 -10t5.5 -21q-1 -23 -4 -68t-4 -67q39 -3 262 -23q11 0 18.5 -9 t6.5 -19q-46 -282 -139.5 -845t-139.5 -845q76 -12 156 -24q16 -3 21.5 -17t-1 -26.5t-20.5 -12.5h-6q-26 4 -79.5 13.5t-80.5 13.5q-3 -28 -10 -64q-3 -18 -21 -22q-2 -2 -8 -2h-8q-615 154 -1229 239q-255 5 -305 5q-56 0 -338 -5q0 -1 1 -3t1 -3q1 -13 -8.5 -21.5 t-22.5 -8.5q-21 0 -24 22q-1 9 -4.5 31.5t-4.5 34.5h-26q-16 1 -21.5 14.5t2 27t23.5 13.5h2h14l-12 111q-15 109 -41.5 320.5t-44 342.5t-51 311.5t-74.5 329.5zM61 1354q43 -158 89 -412q-7 167 -7 428q-30 -4 -82 -16zM199 1167q3 -403 43 -868q6 -78 22 -223 q2 -24 9.5 -68.5t9.5 -54.5q136 -6 392.5 -26.5t373.5 -26.5q27 -1 137.5 -7t149 -8.5t134 -8t148.5 -9.5t133.5 -10.5t149 -13.5t137.5 -15q22 328 66 932t61 842q-953 -9 -1966 -220q-3 -108 0 -215zM387 834q0 -140 87 -228t222 -88q142 0 224 88h4q-27 -144 -133 -227 q-84 -69 -207 -78q-82 -7 -107 -4v-154q72 0 117 4q217 16 350 140q178 168 178 477q0 193 -96 314.5t-270 121.5q-159 0 -264 -108t-105 -258zM580 846q0 92 46 150.5t119 58.5h3q89 0 133.5 -73t42.5 -185q0 -32 -11 -54q-54 -86 -165 -86q-77 0 -122.5 53.5t-45.5 135.5z M762 -131l196 -21l764 -81q292 -63 428 -99q1 6 3.5 18.5t3.5 18.5q-177 28 -338 51q-508 71 -1057 113zM1155 834q0 -140 88 -228t221 -88q141 0 226 88h2q-27 -143 -131 -227q-87 -69 -209 -78q-82 -7 -107 -4v-154q72 0 117 4q217 16 350 140q178 163 178 477 q0 193 -95.5 314.5t-268.5 121.5q-161 0 -266 -108t-105 -258zM1348 846q0 92 46.5 150.5t118.5 58.5h3q89 0 133.5 -73t42.5 -185q0 -32 -11 -54q-54 -86 -165 -86q-77 0 -122.5 53.5t-45.5 135.5zM2079 -227q12 -2 41.5 -6.5t46.5 -6.5q40 245 132 802t140 855 q-22 2 -104 9.5t-127 9.5q-12 -173 -55 -750.5t-64 -892.5q0 -10 -8 -18q-2 0 -2 -2z" />
<glyph unicode="&#xe019;" horiz-adv-x="1708" d="M0 -369l854 2048l854 -2048h-756v277l348 174l-26 113l-322 -162v137l201 106l-29 117l-172 -92v178h-188v-321l-176 121l-33 -132l209 -143v-373h-764z" />
<glyph unicode="&#xe01a;" horiz-adv-x="2299" d="M-2 106v762q0 11 7.5 19t19.5 8h360v264q0 11 7.5 20t19.5 9h200v-1053q0 -11 -7 -20t-17 -9h-590zM221 276h137q12 0 19.5 8.5t7.5 20.5v422h-137q-12 0 -19.5 -9t-7.5 -20v-422zM682 106v762q0 11 7.5 19t19.5 8h200v-760q0 -11 -7 -20t-17 -9h-203zM682 1018v141 q0 12 7.5 20.5t17.5 8.5h202v-141q0 -11 -7 -20t-17 -9h-203zM987 106v762q0 11 7.5 19t19.5 8h590v-1032q0 -11 -8 -19t-19 -8h-590l2 141q0 11 7.5 20t17.5 9h360v100h-387zM1212 276h138q10 0 17 8.5t7 20.5v422h-137q-10 0 -17.5 -9t-7.5 -20v-422zM1681 -23 q0 11 7.5 20t19.5 9h360v100h-387v762q0 11 7.5 19t19.5 8h588v-1032q0 -11 -7.5 -19t-17.5 -8h-590v141zM1905 276h137q12 0 19 8.5t7 20.5v422h-137q-11 0 -18.5 -9t-7.5 -20v-422z" />
<glyph unicode="&#xe01b;" d="M0 674q0 151 105.5 256.5t254.5 105.5q129 0 234 -88l733 367q0 4 -1 11t-1 11q0 149 106 255t257 106q149 0 254.5 -106t105.5 -255q0 -151 -105.5 -256.5t-254.5 -105.5q-132 0 -234 86l-733 -367q0 -3 1 -9.5t1 -10.5q0 -3 -1 -10.5t-1 -10.5l733 -366q102 86 234 86 q149 0 254.5 -106t105.5 -255q0 -151 -105.5 -256.5t-254.5 -105.5q-151 0 -257 105.5t-106 256.5q0 3 1 10t1 11l-733 366q-102 -86 -234 -86q-149 0 -254.5 106t-105.5 255z" />
<glyph unicode="&#xe01c;" d="M0 653q0 208 81 397.5t218.5 327t327 218.5t397.5 81t397.5 -81t327 -218.5t218.5 -327t81 -397.5q0 -207 -78.5 -394t-218.5 -327q-122 149 -297.5 233.5t-374.5 84.5q-220 0 -408 -101.5t-309 -275.5q-169 144 -265.5 347t-96.5 433zM336 948q0 -36 25 -61t61 -25 q12 0 18 2q294 119 611 119q316 0 610 -119q3 0 9 -1t9 -1q35 0 60.5 25.5t25.5 60.5q0 53 -47 76q-326 129 -667 129q-348 0 -674 -131q-41 -26 -41 -74zM430 643l12 -33q16 -26 47 -26q7 0 19 4q258 112 543 112q287 0 546 -114q8 -4 15 -4q31 0 47 26l10 35q0 26 -20 43 q-282 127 -598 127q-315 0 -600 -127q-21 -21 -21 -43zM528 348q11 -28 33 -34.5t31 1.5q221 105 459 105q228 0 448 -98q51 -26 72 24q10 26 0 47t-29 31q-215 108 -491 108q-264 0 -500 -112q-13 -7 -22.5 -25.5t-0.5 -46.5zM653 -301q75 99 186.5 156t239.5 57 q114 0 217 -47t179 -129q-212 -107 -451 -107q-192 0 -371 70z" />
<glyph unicode="&#xe01d;" horiz-adv-x="2467" d="M-10 662q0 87 49.5 161t128 115t164.5 41q128 0 219 -74h2q281 168 686 168l4 4l148 553l501 -76q2 1 4 4l2 3q12 18 13 20q25 35 31 41q71 68 176 68t176 -69.5t71 -174.5t-71 -179.5t-176 -74.5q-119 0 -183 79t-67 197q-13 2 -42 6t-75.5 11.5t-75.5 11.5 q-212 30 -221 31q-11 -32 -59 -211q-51 -174 -66 -242l2 -2q149 -2 305 -43t269 -119q52 38 73.5 52t63 27.5t90.5 13.5q145 0 245.5 -101.5t100.5 -247.5q0 -97 -54 -179.5t-143 -125.5q-1 -93 -32.5 -177.5t-82 -149t-122 -122.5t-144 -98.5t-157.5 -76.5q-6 -2 -49 -21.5 t-66 -26.5q-68 -20 -189.5 -38.5t-191.5 -18.5q-247 0 -477 74q-26 18 -80.5 44t-68.5 34q-71 38 -129.5 76.5t-116.5 91t-97.5 108.5t-65 128t-26.5 151h-2q-72 38 -127 106l-18 25q-31 57 -40 95.5t-9 107.5zM80 637q0 -121 98 -209q17 116 100.5 226.5t194.5 197.5 q-64 43 -149 43q-97 0 -170.5 -80t-73.5 -178zM289 338q0 -87 41 -169.5t108 -146.5t152 -117t172 -86.5t170 -49.5q158 -29 297 -29q244 0 461.5 76t369.5 235h-2q69 91 97 153t28 146q0 86 -35.5 167.5t-94 144.5t-135 116.5t-157 89.5t-162.5 58q-169 47 -366 47 q-119 0 -244 -22t-251.5 -72t-225.5 -121.5t-161 -180.5t-62 -239zM649 487q0 80 49.5 129.5t128.5 49.5t134 -49.5t55 -129.5q0 -77 -56.5 -129.5t-132.5 -52.5q-77 0 -127.5 52.5t-50.5 129.5zM719 162h108q53 -93 162 -141t225 -48q118 0 222.5 48t171.5 141h104 q-30 -72 -86.5 -129t-125.5 -90.5t-142 -50.5t-144 -17q-69 0 -142 17.5t-143 51t-126.5 90.5t-83.5 128zM1411 487q0 82 50.5 130.5t131.5 48.5q75 0 127 -52t52 -127q0 -74 -52.5 -128t-126.5 -54q-79 0 -130.5 51.5t-51.5 130.5zM1954 1446q0 -68 47.5 -116t116.5 -48 q70 0 113.5 46.5t43.5 117.5q0 66 -45.5 112t-111.5 46q-65 0 -114.5 -47t-49.5 -111zM1991 870q107 -74 185.5 -183t111.5 -234q104 85 104 180q0 115 -70 197.5t-182 82.5q-81 0 -149 -43z" />
<glyph unicode="&#xe01e;" horiz-adv-x="1570" d="M4 55q0 40 25 131q48 178 177.5 323.5t301.5 211.5q-76 61 -119 147.5t-43 182.5q0 116 59 213.5t158.5 153t214.5 55.5t214.5 -55.5t158.5 -153t59 -213.5q0 -96 -41.5 -181.5t-115.5 -146.5q217 -78 362.5 -260.5t145.5 -405.5q0 -85 -36 -144t-114 -73q-6 -19 -27 -53 q-8 -24 -30.5 -44.5t-57 -35t-69 -25t-81 -19t-77 -13t-73.5 -10t-56 -7.5q-4 0 -14.5 -2t-14.5 -2h-245q-103 0 -263.5 34.5t-183.5 90.5q-36 36 -51 86q-48 10 -86 43q-42 17 -60 66.5t-18 105.5z" />
<glyph unicode="&#xe01f;" horiz-adv-x="1581" d="M8 631v211q0 90 52.5 204t132 215.5t171 187.5t171.5 130q160 90 383 90q71 0 143.5 -12.5t145.5 -41.5t129 -71t91 -107.5t35 -144.5q0 -26 -4 -50t-14.5 -49t-18.5 -43t-28 -43.5t-29 -38t-35 -39t-34.5 -34.5t-38.5 -36t-35 -33h108h236q4 -148 4 -221q0 -108 -5 -218 t-25.5 -250t-55.5 -200q-71 -124 -193.5 -221t-261.5 -144q-81 -30 -114 -30h-173q-7 0 -98 34q-124 55 -185 140.5t-61 216.5q0 7 1 21.5t1 21.5q-275 142 -368 440q-27 91 -27 115zM307 897q0 -82 19.5 -166t58 -162t107.5 -127t157 -49q25 0 81 31t101 31q29 0 105 -22 t76 -40q0 -26 -13.5 -34.5t-40.5 -8.5q-51 0 -88.5 -4.5t-82.5 -18.5t-70 -47.5t-25 -84.5q0 -81 43.5 -137t108.5 -79.5t145 -23.5q96 0 188.5 34t149.5 101q53 62 69.5 144t16.5 192q0 38 -2 113t-2 112q0 98 10 164h-395q-2 -16 -2 -47q0 -24 2 -71t2 -70q0 -73 -16 -115 q-10 -2 -35 -2q-60 0 -109 25.5t-80 66t-52 94t-29.5 107.5t-8.5 108q0 52 8.5 108t29 115.5t50 106.5t77.5 77t106 30q19 0 43 -7q-9 -52 -9 -69q0 -71 39.5 -118.5t108.5 -47.5q70 0 117 47.5t47 118.5q0 64 -36.5 114t-93.5 78t-118.5 42t-120.5 14q-181 0 -326 -78 t-228 -221t-83 -324z" />
<glyph unicode="&#xe020;" horiz-adv-x="1761" d="M2 739.5q-15 -169.5 38 -368t171 -377.5q38 -58 71.5 -100.5t78.5 -87t96.5 -68.5t105.5 -25q45 -1 87 12.5t72.5 30t82.5 30t113 13.5q61 1 112.5 -12.5t81 -30t71 -30t85.5 -13.5q54 1 106 27t99.5 75t80.5 91.5t74 103.5q64 98 133 248q-85 32 -149.5 93.5t-99 135 t-42 158t14 163t77 149t138.5 116.5q-77 99 -181.5 154t-211.5 55q-78 0 -149 -22t-131 -44t-112 -22q-54 0 -119 22t-136 44t-138 22q-118 0 -234.5 -69t-195.5 -189q-75 -115 -90 -284.5zM852 1268q-16 85 14 170t88 155q56 68 143 115t169 53q13 -89 -15.5 -175 t-83.5 -156q-56 -72 -142 -117.5t-173 -44.5z" />
<glyph unicode="&#xe021;" horiz-adv-x="2039" d="M0 276v756q0 175 87 324t236 236t324 87h453q134 -16 284 -116.5t213 -235.5q3 -6 10.5 -20t10 -19.5t8 -19t8 -25.5t7 -32t8.5 -45.5t8 -59.5q5 -44 7.5 -59t8.5 -43.5t15 -44t22 -27.5q22 -20 83.5 -24.5t121 -6.5t74.5 -14l28 -23l17 -35l6 -28l-4 -525 q-1 -174 -87.5 -322.5t-235 -235.5t-322.5 -87h-744q-175 0 -324 87t-236 235.5t-87 322.5zM530 287q0 -51 36.5 -87t88.5 -36h729q51 0 87 36t36 87t-36 87t-87 36h-729q-52 0 -88.5 -36t-36.5 -87zM530 1028q0 -51 36.5 -88t88.5 -37h359q51 0 87 37t36 88t-36 87t-87 36 h-359q-52 0 -88.5 -36t-36.5 -87z" />
<glyph unicode="&#xe022;" d="M0 655q0 201 78 392q74 182 225 333q296 299 719 299q429 0 731 -299q147 -147 219 -331q76 -183 76 -394q0 -210 -74 -393q-72 -181 -219 -325q-153 -150 -340 -228q-194 -78 -393 -78q-198 0 -389 78q-183 76 -332 225t-225 332t-76 389zM184 655q0 -160 66 -317 q65 -154 186 -272q123 -123 273 -183q147 -63 317 -63q168 0 320 63q150 63 278 185q240 234 240 587q0 173 -64 322q-62 155 -180 270q-245 248 -594 248q-350 0 -590 -246q-120 -120 -186 -274q-66 -157 -66 -320zM440 655q0 146 86 232q83 86 215 86q190 0 273 -148 l-137 -71q-22 48 -56 63q-32 19 -59 19q-137 0 -137 -181q0 -82 34 -131q37 -49 103 -49q89 0 127 86l127 -63q-44 -74 -113 -117q-70 -43 -158 -43q-137 0 -221 84t-84 233zM1032 655q0 146 86 232t215 86q191 0 271 -148l-136 -71q-21 47 -53 63q-36 19 -61 19 q-137 0 -137 -181q0 -86 34 -131q34 -49 103 -49q87 0 125 86l129 -63q-45 -75 -115 -117q-70 -43 -154 -43q-142 0 -223 84q-84 84 -84 233z" />
<glyph unicode="&#xe023;" d="M0 655q0 208 81 397.5t218.5 327t327 218.5t397.5 81t397.5 -81t327 -218.5t218.5 -327t81 -397.5t-81 -397.5t-218.5 -327t-327 -218.5t-397.5 -81t-397.5 81t-327 218.5t-218.5 327t-81 397.5zM170 655q0 -154 56 -299.5t159 -263.5q99 192 311.5 366.5t415.5 219.5 q-24 57 -59 129q-353 -113 -762 -113q-80 0 -119 2q0 -7 -1 -20.5t-1 -20.5zM197 866q45 -4 133 -4q341 0 649 92q-155 276 -342 461q-163 -83 -278.5 -227.5t-161.5 -321.5zM502 -18q111 -87 246.5 -134t275.5 -47q151 0 301 58q-41 353 -160 678q-188 -40 -379.5 -206 t-283.5 -349zM815 1481q182 -188 334 -465q280 118 420 297q-115 95 -255.5 145.5t-289.5 50.5q-103 0 -209 -28zM1223 870q17 -36 69 -165q153 14 330 14q126 0 252 -6q-7 135 -59.5 263t-141.5 232q-132 -198 -450 -338zM1343 561q104 -302 142 -622q162 105 263.5 268 t123.5 354q-150 10 -273 10q-113 0 -256 -10z" />
<glyph unicode="&#xe024;" horiz-adv-x="1714" d="M0 1047q0 80 31 118q20 30 53 43q40 19 96 19h193q31 0 53.5 22.5t22.5 53.5q-1 43 -3 90v120q0 50 25 91q30 44 102 63q27 8 83.5 11.5t133.5 -4t132 -29.5q34 -14 60 -49.5t32 -77.5q169 5 280 -9q196 -23 265 -49q38 -12 67.5 -46t40.5 -91q21 -111 37.5 -429 t3.5 -448q-33 -350 -88 -520q-28 -87 -57 -141.5t-56 -86.5t-74.5 -46t-87.5 -17.5t-120 -3.5q-114 0 -181 10t-113.5 40.5t-63.5 82.5t-17 138q0 74 14 123.5t40.5 76t65.5 39t89.5 13.5t111.5 0q8 0 11 -3t1 -10.5t-5 -16t-6 -22t-3 -26.5t2 -26l3 -19.5t1.5 -12.5t-3 -8 t-11.5 -2h-19h-21.5t-20 -0.5t-20.5 -2.5t-17.5 -5.5t-16 -8.5t-11.5 -12.5t-8.5 -18t-2.5 -23.5q0 -20 3.5 -35t8 -25.5t16.5 -17t21 -10t31 -5t36 -2t45 -0.5h11q116 0 149 27.5t33 130.5q0 66 -8 114.5t-20.5 77t-33 46.5t-41 25t-50.5 11q-176 18 -244 41 q-111 41 -109 236v8q0 5 -5 5t-5 -5q0 -138 -37 -246l-16 -49q-9 -19 -21.5 -30.5t-23.5 -17t-32.5 -5.5t-34 1.5t-42.5 7t-45 7.5q-86 10 -213.5 39.5t-191.5 57.5q-30 13 -45 30t-31 52q-35 73 -73.5 264.5t-47.5 271.5q-8 68 -8 105zM12 1290v4l375 369q0 2 2 2l-6 -12 q-23 -44 -23 -99q0 -45 2 -211q0 -8 -5.5 -14t-14.5 -6h-190q-67 0 -121 -23q-11 -6 -19 -10zM1176 696q44 6 94 6q92 -3 166 -36q0 173 -125 178q-35 1 -61 -11.5t-40.5 -36t-22.5 -47.5t-11 -53z" />
<glyph unicode="&#xe025;" horiz-adv-x="2156" d="M0 635q0 212 150 362t362 150t362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362zM1130 635q0 212 150 362t362 150t362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362z" />
<glyph unicode="&#xe026;" horiz-adv-x="1323" d="M0 -2q0 86 48.5 169t130.5 146.5t199 102.5t245 39h110q-86 82 -86 176q0 58 33 119q-20 -2 -59 -2q-213 0 -348.5 132.5t-135.5 327.5q0 122 78.5 230t205.5 171.5t267 63.5h635l-141 -102h-201q96 -37 150.5 -139.5t54.5 -227.5q0 -96 -49 -181.5t-131 -150.5 q-75 -59 -98 -93.5t-23 -86.5q0 -39 55 -98.5t111 -99.5q124 -88 172.5 -180t48.5 -232q0 -87 -42 -168t-118.5 -146t-197 -104t-265.5 -39q-177 0 -324 44.5t-236 131t-89 197.5zM229 66q0 -140 119.5 -240t298.5 -100q243 0 353 89.5t110 239.5q0 39 -8 66 q-6 22 -14.5 41.5t-25.5 39t-28 32.5t-39.5 34.5t-41.5 30.5t-52 35.5t-53 36.5q-81 24 -158 24q-120 1 -226.5 -43t-170.5 -120.5t-64 -165.5zM365 1210q22 -161 123.5 -277.5t224.5 -119.5q80 -3 141 46.5t88.5 138t13.5 194.5q-22 161 -122.5 274.5t-223.5 116.5 q-123 4 -194 -104.5t-51 -268.5z" />
<glyph unicode="&#xe027;" horiz-adv-x="1798" d="M0 397q0 -299 207 -526q205 -225 532 -225q325 0 531 225q207 223 207 526q0 160 -56 299q-81 -50 -174 -69q45 -105 45 -234q0 -157 -73 -291q-76 -133 -203 -206q-127 -74 -277 -74q-151 0 -278 74q-130 76 -201 206q-76 134 -76 291q0 241 164 414q164 170 391 170 q106 0 201 -39q8 83 55 176q-116 43 -256 43q-327 0 -544 -242q-195 -219 -195 -518zM573 -121q24 -4 52.5 -6t45.5 -2h17q151 95 254.5 222.5t148.5 254.5t64 259t8 245t-26.5 202t-32.5 140l-17 51q-44 140 -88 244t-66 138l-22 34q59 -31 100.5 -69.5t60.5 -75.5t28 -75 t6.5 -69.5t-7 -56.5t-10.5 -39l-6 -14q14 -192 6 -352t-34.5 -289t-64.5 -231.5t-83.5 -180t-92 -134.5t-90 -94.5t-77 -60t-53.5 -32.5zM1139.5 1115q-9.5 58 2.5 101t29 78t33 54l17 18q25 28 80.5 54t98.5 38l42 12q46 10 89 36.5t71 57.5t50 61t32 49l10 20 q52 -72 78 -159.5t26.5 -161t-6 -137t-16.5 -100.5l-11 -36q-16 -43 -40 -81t-46.5 -62.5t-42.5 -43t-32 -26.5l-13 -8q-49 -29 -102 -37.5t-122.5 15.5t-135.5 89q86 47 157 96t115 89.5t77.5 79t49.5 67t25.5 50t11.5 32.5l2 12q-36 -81 -99 -149.5t-127.5 -107.5 t-123.5 -66.5t-96 -37.5l-37 -10q-37 56 -46.5 114z" />
<glyph unicode="&#xe028;" horiz-adv-x="997" d="M-4 -303q208 12 266 55q62 44 62 219v1364q0 167 -62 217q-58 49 -266 58v57h1001v-57q-207 -9 -268 -58t-61 -217v-1364q0 -176 61 -219t268 -55v-57h-1001v57z" />
<glyph unicode="&#xe029;" horiz-adv-x="2527" d="M0 297q0 162 98 345.5t279 365.5q118 117 244.5 203.5t237.5 129.5t203.5 42t143.5 -52q91 -91 29 -295q-9 -29 6 -36t34 0l19 8q197 81 348 81.5t218 -85.5q63 -90 -2 -252q-12 -32 -2 -43.5t42 -20.5q58 -18 110 -47t98 -71t73 -101t27 -127q0 -79 -37.5 -163 t-106 -163.5t-171.5 -150.5t-227 -124.5t-280 -84.5t-323 -31q-158 0 -311.5 29.5t-288.5 87.5t-238 137.5t-163 187.5t-60 230zM256 199q13 -136 125.5 -240.5t293.5 -154t388 -29.5q154 15 290.5 68.5t231.5 130.5t146 175t41 199q-14 135 -127 239.5t-293.5 153.5 t-386.5 29q-207 -20 -375 -103.5t-258 -208t-76 -259.5zM559.5 200.5q1.5 69.5 34.5 135.5q42 87 126 147.5t182.5 79.5t201.5 -4q102 -26 169.5 -95t82 -156.5t-24.5 -184.5q-42 -96 -131.5 -161.5t-194.5 -82t-214 14.5q-74 23 -127.5 70.5t-79.5 107t-24.5 129z M698.5 110q12.5 -49 61.5 -71q50 -23 105.5 -5t86.5 68q30 47 16 96.5t-63 69.5q-50 21 -104.5 3t-83.5 -66q-31 -46 -18.5 -95zM988 271.5q6 -18.5 26 -25.5q19 -8 40 -0.5t31 26.5q11 19 5 38t-25 26q-19 6 -40.5 -1.5t-31.5 -25.5q-11 -19 -5 -37.5zM1645 1544 q-9 38 13 72t60 43q173 36 344 -17.5t293 -189.5q123 -136 159.5 -313.5t-17.5 -339.5v-2q-12 -37 -48.5 -55.5t-74.5 -6.5t-56 48t-6 73v2q39 115 13 242t-113 223q-88 97 -209.5 134.5t-243.5 10.5q-40 -8 -73.5 14t-40.5 62zM1726 1223q-7 34 11.5 63t52.5 37 q85 18 168 -8t143 -92q59 -67 77 -154t-9 -166q-10 -32 -41.5 -48t-63.5 -5q-33 10 -48.5 40.5t-4.5 63.5q27 91 -35 156q-60 69 -151 49q-33 -7 -62 12t-37 52z" />
<glyph unicode="&#xe02a;" horiz-adv-x="2224" d="M0 -373v2048h526v-760h469q101 146 289.5 417.5t237.5 342.5h585l-671 -977q0 -25 197 -293t394 -523l197 -255h-702q-98 136 -293 409.5t-293 409.5h-410v-819h-526z" />
<glyph unicode="&#xe02b;" horiz-adv-x="2129" d="M-10 1432q0 101 71.5 168t186.5 67t183.5 -66.5t70.5 -168.5t-69 -170t-189 -68h-2q-112 0 -182 68t-70 170zM16 -360v1368h455v-1368h-455zM723 -360h455v763q0 72 16 111q28 71 86.5 118.5t146.5 47.5q118 0 174 -82t56 -227v-731h454v784q0 154 -39 273t-110 193 t-165 112t-208 38q-86 0 -156 -21t-119 -58.5t-78.5 -71t-57.5 -76.5v-4h-2l2 4v195h-455q6 -131 0 -1368z" />
<glyph unicode="&#xe02c;" horiz-adv-x="2760" d="M0 565q0 71 4 215q53 17 98 17h37q34 -48 81.5 -144t64.5 -125q4 17 16.5 74.5t20.5 86.5t22 75.5t32 82t40 64.5q75 -12 116 -8q49 -111 93.5 -333.5t79.5 -317.5q-2 0 -8 2t-7 2q-19 0 -52.5 -11.5t-45.5 -13.5q-81 206 -127 416q-24 -43 -27 -110t-4.5 -137 t-21.5 -115l-11 4q-16 0 -45 -4t-43 -4h-37q-60 107 -116 182q-6 -12 -6 -31q0 -18 5 -52t5 -52l-2 -25q-64 -31 -107 -31q-14 0 -20 2q-35 113 -35 291zM727 410q0 39 10 86t29 93.5t53.5 77.5t77.5 31q21 0 57.5 -10t46.5 -12q19 -87 19 -166q0 -58 -12 -113 q-25 9 -78.5 10.5t-75.5 6.5q-10 -42 -10 -76q0 -42 20.5 -75.5t61.5 -33.5q18 0 53 10.5t53 10.5q34 0 47 -29q-87 -33 -168 -33q-95 0 -139.5 62t-44.5 160zM846 467l16 2q5 0 15.5 -4t15.5 -4l14 6q4 28 4 45q0 37 -10 82q-45 -51 -55 -127zM1071 356q0 252 160 373 q67 -6 123 -47q-1 -36 -16.5 -139t-10.5 -160q-40 -4 -59 -4q-16 0 -88 10q-11 -26 -11 -47q0 -45 35.5 -77t79.5 -32q54 0 84 48q36 -12 43 -35q-46 -43 -129 -43q-47 0 -94 16t-82 52t-35 85zM1186 444q7 -12 28 -12h19q18 40 18 100q0 26 -12 80q-6 -17 -20.5 -51.5 t-23 -62.5t-9.5 -54zM1389 483q-3 54 47 54q13 0 36.5 -2.5t34.5 -2.5q6 30 6 64q0 60 -15 177t-15 177q0 89 20 148q18 -10 41 -9.5t45 -9t51 -43.5q-12 -54 -12 -137q0 -41 8 -166t2 -194h13q12 -1 17 7t4.5 18t1 18t7.5 8q16 0 37.5 -17.5t46 -44t31.5 -32.5 q-18 -14 -83.5 -18.5t-78.5 -8.5q-2 -25 -2 -74q0 -32 1 -67.5t2.5 -78.5t2.5 -67q-16 -2 -46.5 -4t-45.5 -4v21q1 49 -6 138t-8 124q-13 3 -66 6t-77 20zM1829 500q0 40 4 118q22 -3 41 -10.5t43.5 -21t36.5 -19.5l-2 -55q0 -121 18 -238q31 11 53 37t33.5 58t17.5 72 t7.5 74t1.5 69v22q6 -2 18 -2q43 -4 101 -51q0 -6 1 -19.5t1 -19.5q0 -31 -8.5 -61t-15.5 -45t-29.5 -51.5t-28.5 -47.5q-34 -27 -70 -86q-44 -14 -78 -14q-145 0 -145 291zM2238 279q12 11 50 20q21 189 48 277q66 217 220 217q69 0 156 -50q34 -72 34 -153 q0 -135 -95.5 -243t-229.5 -140q-4 -66 -4 -98q0 -149 37 -240q-17 4 -76 4q-31 0 -45 4q-18 45 -26 129.5t-21 158.5t-48 114zM2423 330q80 21 134 98t54 162q0 61 -31 115q-36 -33 -63 -77t-45 -100.5t-27 -94.5t-22 -103z" />
<glyph unicode="&#xe02d;" horiz-adv-x="2037" d="M0 291q0 59 40 99t99 40q57 0 97 -40.5t40 -98.5q0 -57 -40 -98t-97 -41q-58 0 -98.5 41t-40.5 98zM0 1018q0 57 40.5 98t98.5 41q57 0 97 -41t40 -98t-40 -98t-97 -41q-58 0 -98.5 41t-40.5 98zM399 459q0 58 40.5 98.5t97.5 40.5q59 0 99 -40t40 -99q0 -57 -40.5 -97 t-98.5 -40q-57 0 -97.5 40t-40.5 97zM399 860q0 58 40.5 98.5t97.5 40.5q59 0 99 -40.5t40 -98.5q0 -57 -40.5 -97t-98.5 -40q-57 0 -97.5 40t-40.5 97zM518 -229q0 57 40.5 97t98.5 40q57 0 98.5 -40t41.5 -97q0 -58 -41.5 -99t-98.5 -41q-58 0 -98.5 40.5t-40.5 99.5z M518 1540q0 58 40 98.5t99 40.5q57 0 98.5 -40.5t41.5 -98.5q0 -57 -41.5 -97t-98.5 -40q-58 0 -98.5 40t-40.5 97zM676 182q0 57 40.5 98.5t98.5 41.5q57 0 97 -41.5t40 -98.5q0 -58 -40 -98.5t-97 -40.5q-59 0 -99 40.5t-40 98.5zM676 1139q0 57 40.5 98t98.5 41 q57 0 97 -41t40 -98q0 -58 -40 -99t-97 -41q-58 0 -98.5 40.5t-40.5 99.5zM1073 182q0 57 41 98.5t98 41.5t98.5 -41.5t41.5 -98.5q0 -58 -41.5 -98.5t-98.5 -40.5t-98 40.5t-41 98.5zM1073 1139q0 57 41 98t98 41t98.5 -41t41.5 -98q0 -58 -41.5 -99t-98.5 -41t-98 41 t-41 99zM1233 1540q0 58 40.5 98.5t98.5 40.5q57 0 97 -40.5t40 -98.5q0 -57 -40 -97t-97 -40q-58 0 -98.5 40t-40.5 97zM1249 -229q0 57 41 97t99 40q57 0 97 -40t40 -97q0 -58 -40 -99t-97 -41q-59 0 -99.5 40.5t-40.5 99.5zM1352 459q0 58 40 98.5t97 40.5q59 0 99 -40 t40 -99q0 -57 -40.5 -97t-98.5 -40q-57 0 -97 40t-40 97zM1352 860q0 58 40 98.5t97 40.5q59 0 99 -40.5t40 -98.5q0 -57 -40.5 -97t-98.5 -40q-57 0 -97 40t-40 97zM1761 291q0 58 40 98.5t97 40.5q59 0 99.5 -40.5t40.5 -98.5q0 -57 -41 -98t-99 -41q-57 0 -97 41t-40 98z M1761 1018q0 57 40 98t97 41q58 0 99 -41t41 -98t-41 -98t-99 -41q-57 0 -97 41t-40 98z" />
<glyph unicode="&#xe02e;" horiz-adv-x="1744" d="M2.5 588q-7.5 98 4 232.5t50.5 250.5q5 14 14 37.5t44 90.5t76.5 127t112 132t149.5 121.5t190.5 78.5t233.5 19q176 0 323.5 -60t246.5 -158.5t167.5 -228.5t99.5 -267t31 -277q0 -163 -27.5 -305t-76.5 -247.5t-103.5 -181.5t-120.5 -136q-114 -102 -249.5 -140 t-319.5 -43q-57 -2 -122 9t-139.5 36t-148 71t-141 107.5t-125.5 152t-96 198.5q-6 18 -16 51t-30 132.5t-27.5 197.5zM460 386.5q7 -111.5 15 -162t14 -64.5q12 -98 39 -175.5t61 -123.5t72 -79t74 -46.5t65 -20.5t46 -7l18 1q90 2 156.5 32.5t110.5 83.5t74 139t46 183.5 t27 233.5q7 288 1 464t-17 253l-11 76q-14 61 -26.5 105.5t-33.5 97.5t-48.5 89.5t-65.5 68.5t-90 48t-117 16q-49 7 -93 -3.5t-77 -33.5t-62 -54.5t-49.5 -68t-37.5 -73.5t-27 -71t-17 -60.5t-10 -42.5l-3 -16q-13 -94 -20.5 -225t-9.5 -215l-1 -85q-10 -153 -3 -264.5z " />
<glyph unicode="&#xe02f;" horiz-adv-x="2148" d="M0 424q19 285 344 354q15 108 43 231.5t80 262t111 198.5q98 101 245.5 155t298.5 54q175 0 335.5 -79.5t240.5 -225.5q39 -74 84.5 -268t62.5 -315q33 -13 70 -13q32 0 46.5 14t14.5 46q0 23 -12 69t-10 72v8q37 2 85 -51t79.5 -117.5t31.5 -101.5q0 -40 -19 -72 t-39.5 -50t-66.5 -35.5t-66.5 -22.5t-73.5 -17q-11 -3 -17 -4q12 -396 -158 -571l51 -51q9 -5 52 -24.5t72 -38t38 -36.5q-133 -104 -270 -104q-68 0 -154 35q-181 -76 -369 -76q-243 0 -479 125q-70 -15 -112 -39q-13 -8 -54.5 -37.5t-75.5 -48.5t-57 -19q-6 37 -6 76 q0 161 109 258q-123 203 -127 527l-15 2q-64 1 -161 -36.5t-132 -37.5q-8 0 -20 4zM475 233q0 -3 6 -27.5t8 -27.5q24 -83 72.5 -161.5t110.5 -120.5q88 -60 216.5 -96t239.5 -36q135 0 298 58t245 147q139 149 139 527q0 206 -46.5 412.5t-141.5 363.5q-37 60 -56 77 q-33 27 -85 27q-102 0 -219 -125q-157 97 -287 97q-77 0 -143 -35t-114 -96.5t-87.5 -140t-65 -171.5t-44 -184t-28 -185t-14 -166.5t-4.5 -136.5zM846 1067q0 44 31.5 76.5t74.5 32.5q44 0 76.5 -32.5t32.5 -76.5t-32 -75t-77 -31q-43 0 -74.5 31t-31.5 75zM961 1104 q0 -9 7 -17t17 -8q9 0 17 8t8 17q0 10 -8 18t-17 8q-10 0 -17 -7.5t-7 -18.5zM1153 981l4 4l-2 -6zM1163 991l107 107l104 -107q-46 -14 -102 -14q-60 0 -109 14zM1167 946q55 -10 103 -10q45 0 100 10l-100 -100zM1382 979v6l2 -4zM1438 1067q0 44 32 76.5t76 32.5 t76.5 -32.5t32.5 -76.5t-32 -75t-77 -31q-44 0 -76 31t-32 75zM1552 1104q0 -9 8 -17t17 -8q10 0 18.5 8t8.5 17q0 10 -8 18t-19 8q-9 0 -17 -8t-8 -18z" />
<glyph unicode="&#xe030;" horiz-adv-x="1779" d="M6 -334v1960q246 43 608 43q286 0 482.5 -49.5t339.5 -157.5q338 -257 338 -757q0 -274 -89 -477.5t-241 -331.5q-303 -258 -924 -258q-292 0 -514 28zM457 -2q39 -8 157 -8q209 -1 360 73t236.5 232.5t85.5 392.5q0 316 -167 475.5t-467 159.5q-135 0 -205 -18v-1307z " />
<glyph unicode="&#xe031;" d="M0 -94q0 112 80 192t192 80q114 0 193.5 -79.5t79.5 -192.5q0 -114 -79.5 -193.5t-193.5 -79.5q-113 0 -192.5 79.5t-79.5 193.5zM0 592v391q183 0 358.5 -48.5t323.5 -136t273 -212.5t212.5 -273t136 -323.5t48.5 -358.5h-394q0 192 -72.5 367.5t-207.5 310.5 q-137 137 -311.5 210t-366.5 73zM0 1286v393q278 0 543 -74t489.5 -206t414 -321.5t321.5 -414t206 -489.5t74 -543h-393q0 225 -59.5 439.5t-166.5 396t-260 334t-334.5 259.5t-396 166.5t-438.5 59.5z" />
<glyph unicode="&#xe032;" horiz-adv-x="2019" d="M0 1120q0 114 44 217.5t119 178.5t178.5 119t217.5 44q159 0 297 -86q80 15 164 15q196 0 374.5 -76.5t308 -205.5t206 -307.5t76.5 -373.5q0 -95 -23 -207q57 -120 57 -248q0 -114 -44 -217.5t-119 -178.5t-178.5 -119t-217.5 -44q-138 0 -264 66q-91 -16 -176 -16 q-195 0 -373.5 76.5t-307.5 205.5t-205.5 307.5t-76.5 374.5q0 93 19 193q-76 133 -76 282zM453 371q0 -85 61 -174q146 -213 504 -213q81 0 158 13.5t151.5 44.5t130 77.5t89.5 118.5t34 161q0 80 -28 143.5t-75 104.5t-108 71.5t-128.5 49.5t-134.5 34.5t-128 30 t-108 32.5t-75 46t-28 66q0 36 24 63.5t61 41.5t73 21t69 7q60 0 105 -13.5t70 -35.5t43.5 -48t32.5 -52t30 -48t42.5 -35.5t63.5 -13.5q56 0 98 39t42 94q0 54 -31 111q-32 59 -86.5 102.5t-121.5 67t-133.5 34.5t-135.5 11q-74 0 -145 -11t-143 -38t-125.5 -68 t-87.5 -106.5t-34 -147.5q0 -78 27.5 -138.5t73.5 -98.5t105.5 -66.5t126 -46t132.5 -33t125.5 -31.5t105.5 -36t73.5 -53t27.5 -78q0 -46 -26.5 -80.5t-68.5 -53t-85 -27.5t-86 -9q-67 0 -116 16.5t-76.5 42.5t-47 57.5t-34 63.5t-31 58t-44 42.5t-67.5 16.5 q-58 0 -99.5 -35t-41.5 -92z" />
<glyph unicode="&#xe033;" horiz-adv-x="2519" d="M0 -178q62 -6 123 -6q360 0 643 221q-168 3 -300.5 102.5t-182.5 255.5q41 -8 96 -8q69 0 137 19q-180 36 -298 177.5t-118 327.5v7q110 -61 236 -64q-106 70 -169 184t-63 246q0 138 72 260q193 -238 470 -380.5t595 -159.5q-14 68 -14 118q0 140 69 259t188.5 188 t260.5 69q110 0 207.5 -43t169.5 -120q178 37 327 127q-28 -90 -86.5 -164.5t-138.5 -122.5q150 17 295 82q-101 -154 -256 -269v-67q0 -147 -31 -296.5t-94.5 -293.5t-153.5 -275t-214 -242t-269.5 -192t-327.5 -126.5t-380 -45.5q-427 0 -793 232z" />
<glyph unicode="&#xe034;" horiz-adv-x="1759" d="M-31 211q0 92 13 332q8 108 78 177t178 75q205 10 616 10t616 -10q108 -6 178 -75t78 -177q11 -204 11 -332q0 -129 -11 -332q-8 -108 -78 -177t-178 -75q-205 -10 -616 -10t-616 10q-108 6 -178 75t-78 177q-13 241 -13 332zM106 500h125v-668h121v668h125v112h-371 v-112zM240 1735h122l78 -312h9l73 312h123l-94 -303q-6 -21 -16.5 -50t-18 -53t-12.5 -51v-324h-121q0 3 1 85.5t1 150t-2 74.5q-4 28 -11.5 52.5t-18.5 53.5t-17 51zM477 -78q0 -98 74 -98q65 0 125 72v-64h106v576h-106v-439q-43 -43 -64 -43q-28 0 -28 39v443h-107v-486z M639 1100v299q0 69 45 107t115 38q67 0 109 -41t42 -108v-297q0 -74 -41 -116t-114 -42q-72 0 -114 44t-42 116zM750 1085q0 -21 12 -34t33 -13q23 0 35 14t12 38v313q0 20 -13.5 31.5t-33.5 11.5q-18 0 -31.5 -11.5t-13.5 -29.5v-320zM887 -168h108v43q42 -51 103 -51 q96 0 96 127v319q0 146 -104 146q-51 0 -95 -56v252h-108v-780zM995 -59v360q23 23 45 23q23 0 34 -17t11 -41v-303q0 -49 -38 -49q-29 0 -52 27zM1042 1044v486h107v-440q0 -39 29 -39q25 0 63 43v436h107v-576h-107v64q-60 -72 -125 -72q-74 0 -74 98zM1276 -6 q0 -79 39 -128.5t117 -49.5q159 0 159 178v43h-110q0 -4 1 -27.5t0 -34t-5 -27.5t-15 -24.5t-28 -7.5t-27.5 7t-14.5 21.5t-5 25t-1 28.5v108h205v148q0 77 -38 122.5t-115 45.5q-73 0 -117.5 -47.5t-44.5 -120.5v-260zM1386 197v61q0 64 48 64q47 0 47 -64v-61h-95z" />
<glyph unicode="&#xe035;" horiz-adv-x="2347" d="M0 1122q112 134 225.5 238t195.5 158t148 88.5t99 43.5l34 9q59 11 107 -4.5t81 -50.5t60.5 -91t44 -116.5t32 -136t25.5 -141t23.5 -140.5t26.5 -125q66 -258 105 -364.5t85 -106.5q49 0 136 114t192 310q33 62 45.5 123.5t4.5 109.5t-35.5 83t-68 50t-98 4.5 t-122.5 -49.5q39 228 219 384q149 130 335 160q184 30 307 -54q147 -101 139 -325q-1 -50 -11 -105q-27 -152 -90 -308.5t-143.5 -289t-179 -260.5t-193.5 -228.5t-190 -188t-165.5 -143t-122.5 -89.5q-63 -41 -129 -47.5t-120 15t-103.5 61t-84 85.5t-58.5 94 q-33 70 -120 354t-168.5 541.5t-109.5 290.5q-3 3 -8.5 7.5t-27 9.5t-49 0.5t-75 -28t-102.5 -67.5z" />
<glyph unicode="&#xe036;" horiz-adv-x="2525" d="M0 -150l215 760q71 41 175 64t172 26l69 2q102 0 213 -20q8 -2 22.5 -5t55 -14.5t76 -25t76 -35t65.5 -45.5l-213 -758q-75 56 -164 92t-141 44l-52 8q-71 6 -133 6q-12 0 -33 -0.5t-80 -6t-111.5 -15t-113 -30t-98.5 -47.5zM258 827l215 760q72 41 176 64t172 25l68 3 q104 0 213 -20q8 -2 22.5 -5t55 -14.5t76 -25t76 -35t65.5 -45.5l-213 -760q-75 56 -164 92t-141 44l-52 8q-71 6 -131 6q-12 0 -33 -0.5t-80.5 -6t-112 -14.5t-113.5 -29t-99 -47zM1141 -223l213 760q76 -56 165 -92t140 -44l51 -8q70 -6 133 -6q12 0 33 0.5t80.5 6 t112 14.5t112.5 28.5t98 46.5l-213 -759q-72 -41 -176 -64t-172 -26l-67 -3q-99 0 -213 21q-8 2 -22.5 5t-55.5 14.5t-76.5 25t-76.5 35t-66 45.5zM1386 750l213 757q76 -56 165 -92t140 -43l52 -8q70 -6 133 -6q12 0 33 0.5t80 6t111.5 14.5t113 28.5t98.5 46.5l-213 -758 q-72 -42 -176.5 -65.5t-173.5 -26.5l-68 -2q-102 0 -211 23q-23 4 -60 13t-117 41.5t-120 70.5z" />
<glyph unicode="&#xe037;" horiz-adv-x="1841" d="M-102 246h448l354 585l-266 469h-448l266 -469zM639 430l709 1245h475l-707 -1245l455 -803h-475z" />
<glyph unicode="&#xe038;" horiz-adv-x="2824" d="M0 1421l12 -39q7 -12 15 -20.5t12 -11.5l4 -2l39 -21h227l588 -704q23 -25 23 -52v-393q0 -16 -6 -24.5t-12 -9.5l-5 -1q-25 0 -151.5 -2.5t-139.5 -3.5q-20 0 -20 -18v-113h1056v117q-1 6 -5.5 9.5t-8.5 3.5l-4 1h-295q-2 0 -5 1t-8 8.5t-5 21.5v414q-2 5 3 13.5 t11 14.5l6 6q24 24 541 510h272q19 1 37 15l35 39q12 14 12 37v28q-2 5 -6 8t-7 3l-3 1h-893l-14 -12q-2 -13 2.5 -26t10.5 -20l5 -7l47 -47l37 -15h235l-423 -399l-467 596h84q27 -2 45 8l139 43l6 3t9.5 10t4.5 18v29q0 6 -4.5 9.5t-9.5 4.5h-4q-1 0 -79 0.5t-186 0.5 t-237.5 0.5t-238 0.5t-183.5 -0.5t-78 -1.5q-16 -2 -20 -14v-17zM2437 1645l379 -15l-170 -1161l-125 6zM2464 82l8 225l225 -8l-8 -225z" />
<glyph unicode="&#xe039;" horiz-adv-x="1781" d="M2 28q-14 46 86 115q13 31 33.5 72.5t33 67t13.5 32.5q1 3 6 28.5t13.5 65t19 87.5t26 106.5t31.5 110.5q26 84 63 153.5t59.5 97t35.5 37.5q11 9 13 13t-7 10q-7 5 -20.5 18t-35.5 45.5t-39 72.5t-26.5 103t-1.5 132q2 17 8 16q10 -1 19 -59v-2q3 -28 15 -64t32.5 -77 t55 -76t75.5 -52q3 -1 17 4.5t50.5 16.5t90 23.5t152 26t218.5 22.5q3 75 15 155q-14 10 -22.5 27.5t-10.5 29.5l-2 13q-10 -18 -18.5 -23.5t-22.5 -3.5q-12 1 -15 55t3 101q3 40 38.5 65t65.5 27q4 2 11 5t26.5 7.5t36.5 3.5q29 30 92 39.5t111 3.5l49 -6q43 5 80.5 1 t69.5 -14t55 -20t36 -18l13 -8q62 3 92 -55q14 -22 24.5 -52.5t15 -68.5t-11.5 -64l-5 -1q-10 5 -23 36q-7 -24 -18.5 -43.5t-18.5 -27.5l-8 -7q1 -32 -2.5 -63t-12.5 -58t-14.5 -42.5t-16.5 -36.5l-10 -21q-7 -16 5 -43q12 -32 46 -176t31 -201q-3 -63 -30 -133.5 t-56 -99.5q8 -70 -43 -168q-13 -23 -35 -91t-30 -108q-20 -104 -2 -127q2 -3 20 -25.5t29.5 -41.5t9.5 -31q-3 -19 -33.5 -38.5t-52.5 -19.5q-25 0 -56 14.5t-40 39.5q-9 27 -10.5 68t4.5 67q5 19 11.5 47.5t16.5 85t16 109.5t5 114t-12 105q-11 -5 -30.5 -12t-85 -18.5 t-134.5 -10.5q-12 -62 -27 -116.5t-24 -78.5l-9 -24q-10 -17 -10 -232q0 -84 29 -211q18 -13 27 -29t8 -26v-10q-4 -26 -32 -42t-61 -16q-34 0 -64 23t-30 57q0 7 3 102t1 144q-1 22 -2 55.5t-2 50.5t-3.5 39.5t-6.5 40.5q-53 202 -55 293q-15 0 -27 10q-28 -33 -55 -61.5 t-40 -40.5l-13 -12q2 -11 8 -32.5t10 -38t4 -21.5q0 -16 29 -29q24 -10 51.5 -31t26.5 -41q-2 -25 -48.5 -28.5t-93.5 18.5q-12 5 -80 217q-6 27 -7 32t5 28q34 98 31 131q-2 30 -46.5 66t-80.5 36q-11 0 -24.5 -5.5t-29.5 -17t-28 -21.5t-30.5 -25.5t-26.5 -22.5 q-31 -23 -77.5 -66t-49.5 -45q-11 -6 -76 -125t-84 -166q-1 -2 -4.5 -16t-7.5 -25t-10.5 -27.5t-16 -29t-22 -24.5t-30 -18t-38.5 -5q-35 2 -43 28z" />
<glyph unicode="&#xe03a;" horiz-adv-x="2045" d="M0 608q0 153 47 318.5t127 296.5q85 143 231.5 248t314 156.5t337.5 51.5q134 0 269 -46t249.5 -125.5t209.5 -184t157 -223.5q51 -87 76.5 -205.5t25.5 -232.5q0 -230 -85 -425.5t-232 -328t-347.5 -206t-427.5 -71.5q38 68 124.5 223t152.5 270.5t131 219.5l2 4 q7 11 24.5 38t25.5 40t19.5 35t18.5 42.5t12 43.5q13 54 13 98q0 162 -99 291l563 166q-583 0 -876 -2q-14 2 -45 2q-133 0 -243 -69.5t-167.5 -187t-42.5 -249.5q-4 18 -4 57q0 92 31 158l-418 412l406 -699q40 -147 162.5 -240.5t275.5 -93.5q27 0 67 7l-135 -566 q-120 0 -241.5 41t-226 111.5t-193 161t-154.5 192.5q-135 209 -135 471zM639 647q0 -157 111 -269t268 -112t268 112t111 269t-111 268t-268 111t-268 -111t-111 -268z" />
<glyph unicode="&#xe03b;" horiz-adv-x="1701" d="M0 -317q0 55 63 55q17 0 28 0.5t38 5t46.5 12.5t44.5 25.5t40.5 42t27 64t11.5 89.5v1258q0 55 -7.5 97.5t-26.5 83.5t-58 65.5t-96 29.5q-18 2 -30 7t-18 9t-9 15t-3 15.5v19.5q-4 51 47 51q9 0 127.5 -3t280 -6t276.5 -3q236 0 403.5 11t248 21.5t106.5 10.5 q35 0 49.5 -20t14.5 -44q0 -6 -12.5 -52.5t-12.5 -84.5q0 -32 7 -96t7 -90q0 -37 -24 -56.5t-49 -19.5q-21 0 -31.5 12t-11.5 45v24q4 17 0.5 34.5t-7 34.5t-21 33.5t-42 31t-70 26.5t-105.5 21t-148 14t-197 5q-100 0 -163 -5.5t-100 -14t-53.5 -29t-20 -39t-3.5 -56.5 q0 -59 1 -275t1 -237q0 -39 13 -54t46 -15h477q78 0 134.5 44.5t62.5 135.5q3 34 11 50t17 19t29 3q29 0 46.5 -23.5t13.5 -69.5q-1 -17 -10 -95t-9 -132q0 -50 9.5 -136.5t9.5 -117.5q0 -34 -23 -51t-49 -6.5t-35 45.5q-6 25 -8.5 35t-12.5 35.5t-20 38.5t-29.5 32.5 t-43 29.5t-59.5 17.5t-79 7.5h-424q-36 0 -52.5 -22t-16.5 -78v-543q0 -40 8 -70.5t29.5 -60.5t67.5 -46.5t114 -16.5q20 0 87.5 -1t106 -0.5t106 2.5t115 9t102.5 18t98.5 30t76.5 44q48 38 69.5 94t41.5 166q6 31 42 36q43 7 70 -31q23 -31 21 -83q-2 -24 -34 -129 t-32 -153q0 -12 1.5 -43.5t1 -47.5t-4.5 -37t-16 -30.5t-31 -9.5q-16 0 -32.5 4.5t-34.5 10t-73 10t-140 4.5q-136 0 -660.5 -3.5t-583.5 -3.5q-38 0 -50.5 11.5t-12.5 38.5z" />
<glyph unicode="&#xe03c;" horiz-adv-x="2129" d="M4 100v1180q0 3 6 39l695 -594l-693 -666q-8 28 -8 41zM96 1401q15 6 35 6h1870q19 0 37 -6l-697 -596l-92 -74l-182 -149l-182 149l-92 74zM98 -20l699 669l270 -219l270 219l699 -669q-17 -7 -35 -7h-1870q-15 0 -33 7zM1430 725l692 594q6 -18 6 -39v-1180 q0 -20 -6 -41z" />
<glyph unicode="&#xe03d;" horiz-adv-x="2265" d="M0 424v309q0 22 14.5 36.5t36.5 14.5h543l-236 -411h-307q-22 0 -36.5 14.5t-14.5 36.5zM250 -76l135 -78zM271 13.5q-6 20.5 3 39.5l623 1082q10 19 30.5 24t39.5 -6l133 -76q20 -10 25.5 -31t-5.5 -40l-620 -1082q-10 -19 -31.5 -24t-40.5 6l-133 78q-18 9 -24 29.5z M901 373l236 411h137l192 -411h-565zM1060 1653q2 19 13 24q21 11 73 -63.5t146.5 -237.5t126.5 -215q57 -90 154.5 -268.5t184.5 -343.5t91 -172q22 -39 -4.5 -74t-62.5 -49q-3 -1 -13 -5.5t-13 -5.5t-11.5 -4t-12.5 -3t-12 -0.5t-14.5 1.5t-15.5 5t-19 8q-18 9 -71.5 110 t-119.5 247t-114 254.5t-84 193.5q-10 22 -28 62.5t-34.5 78t-37.5 85t-38.5 90t-34.5 86.5t-28 80t-17 66.5t-4 49.5zM1733 784h481q21 0 36 -15t15 -36v-309q0 -21 -15 -36t-36 -15h-272q-8 31 -17 47q-16 31 -49 94q-129 243 -143 270zM1759 136q6 22 27 32l76 39 q20 10 42 2.5t33 -27.5l64 -123q10 -22 4 -44.5t-25 -34.5l-55 -33q-20 -11 -43 -5.5t-35 25.5l-82 127q-12 20 -6 42zM1915 -176q-6 24 3 43t14 25.5t18 19.5l34 14q100 29 141 -61q35 -76 19 -234q-7 22 -44.5 39.5t-85.5 42t-72 58.5q-21 29 -27 53z" />
<glyph unicode="&#xe03e;" horiz-adv-x="2220" d="M-2 4v182v111v424h287v-424h1648v424h289v-424v-111v-182h-2224z" />
<glyph unicode="&#xe03f;" d="M0 610q0 217 81 413.5t218.5 339t327 226.5t397.5 84t397.5 -84t327 -226.5t218.5 -339t81 -413.5q0 -7 -1 -20.5t-1 -20.5q-7 209 -91 398t-220 325.5t-321.5 216.5t-389.5 80t-389.5 -80t-321.5 -216.5t-220 -325.5t-91 -398q0 7 -1 20.5t-1 20.5zM375 612 q0 183 87 338.5t236.5 245.5t325.5 90t325.5 -90t236.5 -245.5t87 -338.5q0 -7 -1 -20.5t-1 -20.5q-10 174 -98.5 319t-234 229.5t-314.5 84.5t-314.5 -84.5t-234 -229.5t-98.5 -319q0 7 -1 20.5t-1 20.5zM715 213q0 53 5.5 97t13.5 76.5t23.5 58t30 42t38 28.5t42.5 18 t49 9.5t51 4t56 0.5t56 -0.5t51 -4t49 -9.5t42.5 -18t38 -28.5t30 -42t23.5 -58t13.5 -76.5t5.5 -97q0 -159 -41.5 -294.5t-112.5 -214.5t-155 -79t-155 79t-112.5 214.5t-41.5 294.5zM811 786q0 91 62.5 156.5t150.5 65.5t150.5 -65.5t62.5 -156.5t-62.5 -156t-150.5 -65 t-150.5 65t-62.5 156z" />
<glyph unicode="&#xe040;" horiz-adv-x="2250" d="M2 89q11 15 33 3q243 -141 529 -219t585 -78q400 0 793 150q9 4 28.5 12t28.5 12q31 12 48.5 -12t-11.5 -45q-46 -34 -94 -62q-176 -105 -394 -162t-424 -57q-318 0 -603 111.5t-511 314.5q-19 17 -8 32zM618 674q0 142 70 241.5t191 151.5q113 48 272 68q54 7 180 16v35 v28v32.5t-1.5 28.5t-4.5 32.5t-8.5 26.5t-13.5 28q-21 29 -58.5 45t-77.5 16h-16q-67 -6 -116.5 -42.5t-63.5 -102.5q-7 -42 -41 -47l-236 29q-41 10 -32 51q18 95 67 166.5t118 113t144 62.5t160 26h51q99 0 198.5 -29.5t166.5 -90.5q19 -18 34.5 -37t26 -42t18.5 -41 t12.5 -46.5t7.5 -45t4 -50.5t1.5 -48.5t0 -53.5t-0.5 -52v-377q0 -53 15.5 -97.5t29 -64t47.5 -61.5q24 -40 -4 -62q-142 -124 -184 -159q-25 -19 -59 -5q-23 20 -40 36t-31 32.5t-22 27t-23 31.5t-24 33q-114 -123 -225 -155q-73 -21 -172 -21q-158 0 -259.5 97t-101.5 276 zM971 715q0 -79 39.5 -127.5t107.5 -48.5q6 0 18 2t15 2q85 22 133 110q23 39 34 85.5t12 74.5t1 94v51q-119 0 -180 -16q-180 -52 -180 -227zM1828.5 55.5q3.5 8.5 14.5 16.5q54 35 99 47q99 26 191.5 19.5t109.5 -29.5q8 -12 8 -37v-15q0 -72 -39 -169t-109 -156 q-15 -12 -26.5 -6.5t-5.5 22.5q101 236 67 281q-28 33 -196 20q-52 -6 -95 -12q-11 -1 -16.5 4.5t-2 14z" />
<glyph unicode="&#xe041;" horiz-adv-x="2371" d="M0 418v1261h432v-616q322 184 754 184q241 0 461 -64t378.5 -172t252.5 -257.5t94 -313.5q0 -132 -60 -255t-169.5 -222.5t-255.5 -174.5t-327 -116t-374 -41q-237 0 -453.5 62t-374.5 167.5t-254 251t-102 306.5h-2zM432 440q0 -147 101 -271t274.5 -195.5t378.5 -71.5 t378 71.5t274 195.5t101 271q0 146 -100.5 270t-274 196.5t-378.5 72.5t-378.5 -72.5t-274.5 -196.5t-101 -270zM686 440q0 -97 67 -179t182 -129.5t251 -47.5q135 0 249.5 47.5t181 129.5t66.5 179t-66.5 179t-181 130t-249.5 48q-136 0 -251 -48t-182 -130t-67 -179z" />
<glyph unicode="&#xe042;" horiz-adv-x="2267" d="M-4 514q0 140 68.5 259t186.5 188t257 69l2 -2q0 5 -1 14t-1 13q0 119 45.5 226.5t123 185t184 123.5t224.5 46q211 0 371 -135.5t197 -339.5h35q118 0 224.5 -46t184 -123.5t123 -185t45.5 -226.5q0 -158 -77 -292t-210 -212t-290 -78q-17 0 -25 2v-2h-1137h-18h-19h-10 v2q-201 11 -342 160t-141 354z" />
<glyph unicode="&#xe043;" horiz-adv-x="2074" d="M-25 586l439 334l-439 331l637 416l416 -356l426 356l619 -395l-406 -338l406 -348l-629 -379l-416 319l-416 -319zM412 72v116l192 -112l416 317l2 -2v-727zM422 924l602 -379l606 401l-602 346zM1022 -336l2 727l4 2l414 -317l205 123v-129z" />
<glyph unicode="&#xe044;" horiz-adv-x="2881" d="M0 371q0 45 4.5 79t19.5 72.5t40 66.5t69 54.5t105 43t148.5 26.5t197.5 10q175 0 303 -27q76 -16 119 -37v-329h-600q0 -33 1.5 -53.5t11 -53t27 -51t53 -33t85.5 -14.5q174 0 174 125h129h4h115v-182q-59 -29 -115 -39q-145 -31 -307 -31q-109 0 -195 9.5t-147 26 t-105 43t-70.5 55.5t-41.5 69.5t-20 79.5t-5 90zM406 440h364v30.5t-3 29.5t-9 32t-19.5 26t-33 24t-50.5 13.5t-71 6.5q-38 0 -67.5 -6.5t-47.5 -15.5t-31 -25t-19 -27.5t-9.5 -32t-3.5 -28.5v-27zM1006 68v182h159q-3 -104 -151 -178q-4 -4 -8 -4zM1006 68q4 0 8 4 q148 74 151 178h-159v80h165q6 53 1 102.5t-20.5 90.5t-52 76t-93.5 60v375h219v-416h4q34 50 76.5 70.5t103.5 20.5q87 0 141 -54q-82 -60 -82 -209q0 -96 38 -163t118 -86q-3 -63 -12 -110.5t-31 -96t-66.5 -75t-109.5 -26.5q-67 0 -111.5 24t-74.5 79h-2v-86h-211v162z M1006 330v329q57 -25 93.5 -60t52 -76t20.5 -90.5t-1 -102.5h-165zM1225 307q0 -158 17.5 -220t72.5 -62q57 0 75.5 62t18.5 220v9q0 49 -0.5 72.5t-3 60t-8.5 54t-16 36.5t-26.5 26.5t-39.5 7.5q-17 0 -30 -4.5t-22 -16t-16 -22t-11 -32.5t-6.5 -37.5t-3.5 -47t-1 -50 v-56.5zM1468 446q0 149 82 209q22 -23 37.5 -60t22.5 -64t10.5 -81.5t3.5 -71.5v-75v-106q-80 19 -118 86t-38 163zM1485 772q0 142 82 193q69 53 207 53q7 0 100 -6l219 -447v-209q0 -28 3.5 -79.5t4.5 -69.5h-205l-10 106h-4q-66 -118 -205 -118q-37 0 -53 2v106v75 t-3.5 71.5t-10.5 81.5t-22.5 64t-37.5 60q89 68 326 60v63q0 59 -17.5 88t-72.5 29q-28 0 -45.5 -9.5t-25 -29.5t-9.5 -37.5t-2 -46.5h-219zM1683 457q0 -127 84 -127q23 0 41.5 8t30 20t19.5 32.5t12 37.5t5.5 43.5t1.5 42.5t-0.5 42.5t-0.5 35.5q-9 -1 -31.5 -1.5 t-31 -1.5t-27 -2.5t-26 -4.5t-21.5 -8t-20 -11.5t-14.5 -16.5t-12 -22.5t-6.5 -29t-3 -37.5zM1821 1118h315l205 -475l223 475h293l-387 -735v-389h-289v389l-88 182v187q0 42 -4 73.5t-17.5 64.5t-37 55.5t-64 40.5t-96.5 26zM1874 1012q56 -8 96.5 -26t64 -40.5t37 -55.5 t17.5 -64.5t4 -73.5v-187z" />
<glyph unicode="&#xe045;" horiz-adv-x="1673" d="M573 580v391h316v196q0 212 138.5 362t334.5 150h313v-391h-313q-31 0 -55.5 -34t-24.5 -83v-200h393v-391h-393v-949h-393v949h-316z" />
<glyph unicode="&#xe046;" horiz-adv-x="1923" d="M0 -55q0 -129 127 -221.5t307 -92.5q181 0 308.5 92.5t127.5 221.5q0 131 -127.5 223t-308.5 92q-2 0 -6 -1t-6 -1q-96 92 0 207q145 8 245.5 112t100.5 248q0 57 -14 103q57 10 90 28v236q-107 -71 -238 -66q-94 64 -207 64q-154 0 -262 -107t-108 -258q0 -97 48.5 -179 t131.5 -130q-20 -25 -33 -53.5t-20.5 -67.5t4.5 -87t45 -97q-94 -42 -149.5 -112.5t-55.5 -153.5zM248 829q0 67 43.5 115.5t103.5 48.5q61 0 104.5 -48t43.5 -116t-43.5 -115.5t-104.5 -47.5q-60 0 -103.5 48t-43.5 115zM254 -55q0 55 52.5 93t127.5 38t128.5 -38t53.5 -93 q0 -54 -53.5 -92.5t-128.5 -38.5t-127.5 38.5t-52.5 92.5zM932 1513q0 69 48.5 117.5t117.5 48.5t118.5 -48.5t49.5 -117.5t-49.5 -118t-118.5 -49t-117.5 49t-48.5 118zM971 188v981h274v-981h-274zM1341 961h113v-582v-2q8 -61 39.5 -105.5t71 -65t77.5 -32t64 -12.5 l25 -2q42 -1 77 5.5t54.5 16.5t33 20t18.5 18l5 7l4 203q-60 -22 -104.5 -26.5t-64.5 5t-31.5 22t-12.5 23.5l-2 11q0 6 -2 8v488h186v208h-186v191h-252v-191h-113v-208z" />
<glyph unicode="&#xe047;" horiz-adv-x="1560" d="M-82 1354v37q13 25 36.5 44.5t43 30.5t62.5 22.5t61.5 15.5t73.5 14q48 9 73 13.5t68 10t67.5 5t57 -5.5t52.5 -17t38.5 -33t28.5 -51q22 -65 0 -103q-13 -22 -50.5 -33t-73.5 -20.5t-46 -26.5q-9 -14 -9 -131q0 -51 -8.5 -218t0.5 -220q8 -98 25 -108q19 -4 29.5 -3 t33.5 7.5t29 7.5q24 5 87.5 19t88 19t73.5 14.5t92.5 15.5t94.5 12q4 0 57 3t84 7.5t41 13.5q12 10 16.5 32t3 39t-6 46.5t-4.5 34.5q-18 157 0 332q0 4 2 16.5t3 19t2.5 19.5t1.5 21t0.5 19.5t0 19.5t-2 17t-4.5 15t-7 10q-8 8 -47 15t-44 10q-57 24 -93 71.5t-19 110.5 q9 26 71 58q42 21 89 34t123 25.5t102 17.5l71 -1q70 0 70 1q23 1 51.5 -35.5t45 -85t6.5 -77.5q-7 -22 -69.5 -48.5t-72.5 -37.5q-14 -18 -20 -59t-6.5 -75.5t1 -98t1.5 -83.5v-846q0 -10 -0.5 -32.5t-0.5 -39.5t0.5 -36.5t2.5 -32t6 -16.5q9 -8 46 -9t46 -4 q41 -13 69 -72.5t1 -105.5q-4 -7 -20 -14.5t-39.5 -16t-35.5 -14.5q-70 -33 -164.5 -56t-175.5 -17q-52 3 -109 62.5t-50 111.5q5 37 65 61q8 3 47.5 8t51.5 17t13.5 43.5t-4 84t-5.5 66.5v107q0 8 0.5 22t0.5 25v22.5t-1.5 19.5t-3.5 11q-16 23 -203 0q-238 -35 -363 -74 q-5 -1 -35 -9.5t-47.5 -14t-39.5 -17.5t-31 -26t-11.5 -47.5t-0.5 -63.5t6.5 -73.5t5.5 -55.5q1 -10 -0.5 -33.5t-1 -42t5.5 -26.5q8 -12 29.5 -16t45 -4t27.5 -1q35 -12 66.5 -53.5t25.5 -83.5q-4 -37 -47 -66.5t-108.5 -47t-123 -26.5t-114.5 -14t-98 5q-43 11 -83.5 58 t-37.5 87q7 51 113 86q5 2 24.5 6.5t24.5 9.5q16 20 0 158q-21 177 -25 492q-1 81 -6.5 241.5t-5.5 265.5q0 12 0.5 35.5t0.5 40.5t-0.5 36t-2.5 32t-6 18q-8 12 -46.5 18.5t-47.5 10.5q-39 17 -61.5 43.5t-43.5 77.5z" />
<glyph unicode="&#xe048;" d="M0 655q0 208 81 397.5t218.5 327t327 218.5t397.5 81t397.5 -81t327 -218.5t218.5 -327t81 -397.5t-81 -397.5t-218.5 -327t-327 -218.5t-397.5 -81t-397.5 81t-327 218.5t-218.5 327t-81 397.5zM205 655q0 -167 65 -318.5t174.5 -261t261 -174.5t318.5 -65t318.5 65 t261 174.5t174.5 261t65 318.5t-65 319t-174.5 261.5t-261 174.5t-318.5 65t-318.5 -65t-261 -174.5t-174.5 -261.5t-65 -319zM580 295q0 -63 44 -107.5t107 -44.5q67 0 116.5 49.5t49.5 116.5v17v534l410 76v-426q-30 10 -58 10q-67 0 -116.5 -49t-49.5 -117 q0 -63 43.5 -107t106.5 -44q64 0 114.5 46.5t51.5 110.5v9v16v782l-594 -102v-614q-30 10 -57 10q-69 0 -118.5 -49t-49.5 -117z" />
<glyph unicode="&#xe049;" horiz-adv-x="1564" d="M0 -375v2048h1565v-1673l-1170 6v-381h-395zM391 360h817v922h-817v-922z" />
<glyph unicode="&#xe04a;" d="M0 535v91v7.5v6.5v8v7q0 239 11 332q27 222 150 364q48 55 111 103q307 231 752 225h10v-1591h254v426q8 -12 77 -124.5t108 -168.5q59 -83 129.5 -109t187.5 -26h74v242q-37 0 -50.5 0.5t-38 3t-35 8t-28 16.5t-31.5 28t-32 42q-21 28 -42.5 62.5t-46.5 77t-40 65.5 l350 487h-303l-279 -387v928q279 -45 488 -205q40 -31 73.5 -63t59.5 -68t46 -67.5t34.5 -74.5t25 -73.5t16.5 -80.5t10 -79.5t5.5 -86.5t1.5 -86v-91v-29q0 -239 -11 -332q-27 -221 -150 -363q-47 -55 -111 -103q-300 -226 -752 -226t-752 226q-40 31 -73.5 62.5t-59.5 68 t-46 68t-34.5 74.5t-25 73.5t-16.5 80.5t-10 79.5t-5.5 86t-1.5 85.5zM166 213q76 -78 172 -107.5t221 -29.5q77 0 147 20t127 58.5t91 102t34 144.5q0 91 -27.5 154t-71 93t-96.5 47t-106.5 23t-97 13t-71 26t-27.5 54q0 53 42.5 74.5t100.5 21.5q117 0 178 -61l160 160 q-122 122 -348 122q-103 0 -187.5 -35t-138 -110.5t-53.5 -179.5q0 -77 22.5 -133t59.5 -86.5t82 -49t93.5 -26.5t90.5 -13t76.5 -13t48.5 -23q21 -24 21 -62q0 -94 -148 -94q-157 0 -231 74z" />
<glyph unicode="&#xe04b;" horiz-adv-x="2727" d="M-2 655q0 221 108.5 407.5t295 295t407.5 108.5q254 0 450 -130t300 -369q30 -75 264 -615q26 -58 45 -94.5t48 -75.5t61 -61t79 -37.5t107 -17.5q149 -4 243.5 73.5t94.5 196.5q0 37 -7.5 66t-27 52t-39 38.5t-57.5 32.5t-67 27t-83 28q-101 33 -172.5 63.5t-133 70.5 t-98 85.5t-57 105.5t-20.5 135q0 186 118 298t320 112q260 0 391 -236l-172 -88q-93 127 -229 127q-95 0 -157 -58.5t-62 -147.5q0 -25 5 -46t11.5 -38t21.5 -33t27 -27t36 -23.5t40.5 -20t48.5 -18.5t51 -17.5t58 -18.5t61 -19q111 -36 184 -74t126.5 -91t77.5 -124 t24 -169q0 -135 -71.5 -244t-195.5 -169.5t-276 -59.5q-105 0 -189 25t-148.5 76t-112.5 119t-91 163l-28 67l-260 590q-68 157 -214.5 253.5t-324.5 96.5q-160 0 -296 -79t-215 -215t-79 -296t79 -296t215 -214.5t296 -78.5q168 0 307.5 86.5t214.5 230.5l105 -242 q-112 -139 -276.5 -218t-350.5 -79q-221 0 -407.5 108.5t-295 295t-108.5 407.5z" />
<glyph unicode="&#xe04c;" horiz-adv-x="2478" d="M14 -240v1837h379l19 -16h1689l19 16h356v-1843h-350v15h-1751v-9h-361zM375 31v-164h1751v151l-707 531l-162 -133l-176 143zM375 154l629 469l-629 516v-985zM545 1466l712 -610l711 610h-1423zM1497 612l629 -471v992z" />
<glyph unicode="&#xe04d;" d="M0 1016l647 661v-252l617 -567l383 94l-400 -426l801 -897l-909 803l-385 -393l61 346l-522 647z" />
<glyph unicode="&#xe04e;" horiz-adv-x="2217" d="M0 354q-2 129 50 239t137 182t188.5 129t207 88.5t189.5 51.5t140 26l53 7v-252q-117 -9 -214.5 -40.5t-159 -74.5t-108.5 -93t-71 -98t-38.5 -87.5t-17.5 -63.5l-4 -24q-17 -78 8 -147.5t78 -119t121 -91t139 -67.5t131 -44t98 -25l38 -8v-211q-199 20 -360 71 t-258.5 116t-170 139t-104.5 144t-50.5 127t-19.5 92zM1044 -369v1848l291 200v-1888zM1405 842q52 1 106.5 -11.5t95 -31.5t73.5 -38t50 -33l17 -13l-170 -70l641 -160v502l-219 -125q-52 55 -132.5 97.5t-158 63.5t-147.5 34.5t-113 16.5l-43 3v-235z" />
<glyph unicode="&#xe04f;" d="M0 -96v1503q0 114 79 193t193 79h1504q114 0 193 -79t79 -193v-1503q0 -114 -79 -193.5t-193 -79.5h-1504q-114 0 -193 79.5t-79 193.5zM78 -39q0 -104 74 -178t178 -74h1388q104 0 178 74t74 178v1389q0 104 -74 178t-178 74h-1388q-104 0 -178 -74t-74 -178v-1389z M489 27v61h101q36 0 62.5 15.5t39 38t19 45t7.5 37.5l1 16v12v59v772q0 45 -12.5 78.5t-30.5 48.5t-36.5 23t-31.5 9l-12 1h-102v66h714q68 0 125 -16.5t93 -42.5t64 -58t42.5 -63.5t23.5 -57.5t11 -43l2 -16v-127q0 -63 -18.5 -113.5t-48.5 -81.5t-66 -54t-72 -33t-66 -16 t-48 -7h-19h-293v-344q0 -40 14.5 -69.5t35 -43t41.5 -21.5t35 -9l15 -1h97v-65h-687zM936 692h223q8 0 20.5 1t44.5 12t56 29.5t44 59t19 95.5l-2 125q0 8 -1 21t-12 46.5t-29.5 60.5t-60 51t-97.5 28h-205v-529z" />
<glyph unicode="&#xe050;" horiz-adv-x="1820" d="M0 750q0 150 46 291t130 254.5t195.5 199t250.5 132t287 46.5q186 0 354.5 -73t291 -196.5t194.5 -294.5t72 -359q0 -244 -117 -452t-315 -333q2 -5 7 -13.5t24 -32.5t43 -45t66.5 -43.5t92.5 -35.5v-155q-82 -18 -151 -13t-120 20.5t-94.5 48.5t-74.5 71t-58 88 q-102 -27 -215 -27q-185 0 -353.5 73t-290 197t-193.5 294.5t-72 357.5zM453 629q0 -156 62 -288.5t169 -210t234 -77.5q50 0 106 17q-4 6 -11 17t-30 38t-48 48.5t-62.5 39.5t-75.5 20v162v19q141 8 257 -43.5t199 -139.5q61 78 95 181t34 217v237q0 156 -62 288.5 t-169 210t-233 77.5q-127 0 -234 -77.5t-169 -210t-62 -288.5v-237z" />
<glyph unicode="&#xe051;" horiz-adv-x="2459" d="M0 655q0 143 39.5 278t110.5 247t170.5 205.5t217 158t253 100t276.5 35.5q178 0 332 -51v-618q-129 71 -271 71q-82 0 -173.5 -27t-157.5 -73q-93 -67 -165.5 -168.5t-72.5 -196.5q0 -184 117.5 -288.5t304.5 -104.5q159 0 309.5 85t222.5 222q9 16 26 54t17 49 q3 137 3 293t-1.5 346.5t-1.5 294.5q40 -20 78 -44.5t92 -62.5t74 -51q127 -87 284 -137.5t308 -50.5q41 0 54.5 -15.5t13.5 -60.5q0 -55 -33.5 -112t-85 -102.5t-108 -80.5t-107.5 -53q8 -63 8 -142q0 -214 -87 -405.5t-232 -326.5t-340.5 -213.5t-407.5 -78.5 q-141 0 -276.5 35.5t-253 100t-217 158t-170.5 205.5t-110.5 247t-39.5 278z" />
<glyph unicode="&#xe052;" horiz-adv-x="2361" d="M0 289q0 30 7 88.5t20 64.5q13 -6 21.5 -66t8.5 -87q0 -28 -8.5 -87.5t-21.5 -64.5q-13 6 -20 63.5t-7 88.5zM131 289q0 51 10.5 150t12.5 126q0 5 4 9t8 4q8 0 12 -13q3 -29 10 -86t11 -104t4 -86q0 -49 -11.5 -144.5t-13.5 -121.5q-4 -15 -12 -15q-5 0 -8.5 4.5 t-3.5 10.5q-2 25 -12.5 120.5t-10.5 145.5zM297 289q0 50 9.5 150.5t10.5 111.5q0 7 4.5 12.5t10.5 5.5q16 0 16 -18q2 -24 9 -81t10.5 -100t3.5 -81q0 -40 -3.5 -86.5t-10.5 -106.5t-9 -86q0 -16 -16 -16q-6 0 -10.5 5t-4.5 11q-1 13 -10.5 119.5t-9.5 159.5zM485 289 q0 103 9 302.5t10 223.5q0 8 5.5 14.5t12.5 6.5q9 0 15 -6.5t6 -14.5q1 -33 10.5 -228t9.5 -298q0 -53 -9.5 -158t-10.5 -119q0 -8 -6 -14t-15 -6q-7 0 -12.5 6t-5.5 14q-2 31 -7 87t-8.5 103t-3.5 87zM678 289q0 114 6.5 327t7.5 263q2 9 9 16.5t16 7.5t15.5 -7.5 t8.5 -16.5q1 -71 9 -275.5t8 -314.5q0 -51 -8 -146t-9 -127q-2 -9 -8.5 -16.5t-15.5 -7.5t-16 7.5t-9 16.5q-1 21 -7.5 120.5t-6.5 152.5zM872 289q0 108 6 315.5t7 237.5q2 12 10 21t18 9t18.5 -9t10.5 -21q1 -61 4.5 -174.5t5.5 -205.5t2 -173q0 -39 -2 -84t-5.5 -100.5 t-4.5 -84.5v3q-1 -13 -9.5 -22t-19.5 -9t-19 8t-9 20q-1 11 -7 114t-6 155zM1067 287q0 107 2 227t5.5 266.5t4.5 225.5q2 15 11 25.5t22 10.5q12 0 22 -10.5t11 -25.5q1 -79 4.5 -225.5t5.5 -266.5t2 -227q0 -37 -2 -80.5t-5.5 -97.5t-4.5 -82q-2 -14 -11.5 -24.5 t-21.5 -10.5q-13 0 -22 10.5t-11 24.5q-1 28 -4.5 82t-5.5 97.5t-2 80.5zM1230 722q0 169 3 318q-1 20 26 36t66 24t71 12t50 4q198 0 345.5 -148t164.5 -366q59 27 112 27q120 0 204.5 -94t84.5 -226t-84.5 -225.5t-204.5 -93.5h-804q-12 0 -21.5 12.5t-9.5 26.5 q0 112 -1.5 318t-1.5 375z" />
<glyph unicode="&#xe053;" horiz-adv-x="1261" d="M0 840v290q124 42 207 113q83 69 137 176q55 106 72 260h293v-518h485v-321h-485v-525q0 -177 18 -229q18 -50 70 -80q68 -41 155 -41q158 0 310 101v-322q-132 -64 -238 -88q-112 -25 -229 -25q-140 0 -250 35q-114 40 -183 103q-75 63 -106 139q-29 71 -29 215v717 h-227z" />
<glyph unicode="&#xe054;" horiz-adv-x="1875" d="M0.5 862q-5.5 39 3.5 70q8 73 32 128t53 82t57 43.5t46 19.5l19 3q50 4 98 -19t80.5 -60t54 -78.5t25.5 -75.5q3 -23 4.5 -46t1 -66t-11.5 -80t-30 -75.5t-57 -66.5t-91 -39t-100 0t-78 38t-55.5 64t-35 78t-16 80zM130.5 64q12.5 62 36 108.5t60 87.5t67 66t56.5 45 t29 24t53.5 46t109.5 106t101 135q30 49 78 89t111 60.5t128.5 15t136 -53.5t128.5 -138q6 -9 17.5 -24t51 -62t82.5 -93t111 -109.5t139 -118.5q54 -42 81 -102.5t27 -116.5t-7.5 -106t-18.5 -81l-12 -30l-8 -16t-26.5 -36.5t-48.5 -48.5t-74 -44t-102.5 -30.5t-135.5 -0.5 t-171 38q-73 24 -161.5 26.5t-140.5 -6.5l-52 -9q-8 -2 -21 -4.5t-56 -9.5t-81.5 -10.5t-96 -6t-99.5 3.5q-46 -1 -86 14t-68.5 39t-52 53.5t-38 59.5t-24.5 54.5t-14 39.5l-4 16q-17 68 -4.5 130zM426 147q-15 -98 20 -180q7 -13 20 -30t52.5 -44.5t89.5 -37.5h260v628 l-116 2v-170h-140q-39 0 -72.5 -17.5t-53.5 -42t-34.5 -49t-19.5 -42.5zM475 1364q0 131 64.5 223t156.5 92t157 -92t65 -223t-65 -223t-157 -92t-156.5 92t-64.5 223zM549 47q-12 43 8 96q4 11 12 22.5t26.5 27t41.5 22.5h113v-256h-105q-33 0 -57 22t-32 44zM932 -31 q0 -36 31 -64t61 -38l31 -10h299v444h-129v-334h-123q-18 3 -30 12.5t-14 18.5l-3 8v297l-123 -2v-332zM1039 1236q-7 54 1 110q6 61 49 131.5t107 116t119 32.5q89 -21 152.5 -117.5t48.5 -195.5q-8 -51 -31.5 -103t-59.5 -96.5t-88.5 -69.5t-111.5 -18q-59 8 -100 38.5 t-60 74t-26 97.5zM1405 752q0 307 221 307q56 0 101.5 -20.5t72 -50.5t45 -66.5t25 -65.5t6.5 -51q0 -4 0.5 -25t0.5 -35.5t-2 -41t-5.5 -46t-11 -45.5t-18 -45t-27 -39t-38 -32.5t-51 -21t-65.5 -9.5q-51 -1 -91 8t-66 24t-45 40t-29 48t-15.5 56t-6.5 56t-1 55z" />
<glyph unicode="&#xe055;" horiz-adv-x="2070" d="M0 -43v1389q0 134 88 233.5t221 99.5h1428q144 0 239 -76.5t95 -218.5v-120q-190 35 -228 41q-10 1 -33.5 3t-42.5 4.5t-34 6.5q-22 6 -61 20.5t-69.5 21.5t-66.5 7q-125 0 -270 -96q-66 -6 -194.5 -14.5t-231.5 -19t-195 -28.5q-137 -26 -168 -40q-46 -21 -59 -68 q-10 -39 -10 -174v-258l-7 -37l1078 -27l163 56q-86 -154 -143 -271l-217 -96l-831 -14q-7 -6 -21.5 -8t-21.5 -9q8 -51 14 -80q8 -35 32.5 -59t60.5 -36.5t66.5 -17.5t69.5 -8q422 -26 686 -26q78 0 246.5 41t259.5 41q133 0 228 -60v-110q0 -156 -80 -237t-236 -81h-1481 q-125 0 -199.5 98t-74.5 228zM1413 1075q0 29 20.5 50.5t49.5 21.5t50 -21.5t21 -50.5q0 -28 -21 -48.5t-50 -20.5t-49.5 20.5t-20.5 48.5z" />
<glyph unicode="&#xe056;" d="M0 653q0 208 81 397.5t218.5 327t327 218.5t397.5 81t397.5 -81t327 -218.5t218.5 -327t81 -397.5t-81 -397.5t-218.5 -327t-327 -218.5t-397.5 -81t-397.5 81t-327 218.5t-218.5 327t-81 397.5zM80 653q0 -277 146 -505.5t386 -344.5l-450 1235q-82 -184 -82 -385z M233 1171q8 0 21 -0.5t23 -0.5t18 1q98 1 252 13q24 2 35.5 -16.5t3 -38.5t-32.5 -23l-109 -10l344 -1022l207 618l-147 404q-49 6 -98 10q-24 2 -32.5 22t3 39t35.5 17q69 -5 131 -8.5t89 -3.5l28 -1q99 1 251 13q24 2 35.5 -16.5t3.5 -38.5t-32 -23l-107 -10l340 -1016 l94 315q6 18 19 60t18 59t14 49.5t13 51.5t7 42.5t3 42.5q0 134 -80 263q-5 8 -26 43.5t-27 47.5t-18.5 39t-16.5 48t-4 43q0 67 46 117.5t114 50.5q2 0 6 -1t6 -1q-127 118 -291.5 182.5t-347.5 64.5q-244 0 -452.5 -114.5t-338.5 -311.5zM758 -254q124 -37 266 -37 q165 0 313 53q-1 2 -3 6.5t-3 6.5l-291 796zM1499 -164q214 125 341.5 342.5t127.5 474.5q0 246 -115 453q7 -51 7 -96q0 -128 -72 -338z" />
<glyph unicode="&#xe057;" horiz-adv-x="2052" d="M0 655q0 -178 58.5 -342t164.5 -297q-28 82 -14.5 178t59.5 199.5t106.5 202.5t128 188t123.5 156.5t93 107.5l37 39q-302 187 -480 267q-131 -141 -203.5 -320.5t-72.5 -378.5zM285 -51q34 102 93 207.5t126 192.5t140.5 169.5t140 144t120 108.5t85.5 71l32 24 q13 -10 36.5 -27.5t91.5 -75t132 -117t145.5 -147.5t145 -172.5t118 -185t76.5 -192.5q-142 -149 -334.5 -233.5t-406.5 -84.5q-216 0 -407.5 84t-333.5 234zM360 1434q136 117 306.5 181t359.5 64q188 0 358 -64t306 -181q-78 41 -166.5 41t-177 -29.5t-160 -65 t-116.5 -64.5l-44 -30h-2q-9 6 -24 16.5t-62.5 39t-95 51.5t-114 46t-126 32.5t-125 1.5t-117.5 -39zM1294 1087q304 187 482 267q131 -141 203.5 -320.5t72.5 -378.5q0 -179 -58.5 -342.5t-166.5 -296.5q28 82 15 178t-59 199.5t-106.5 202.5t-128 188t-124 156.5 t-93.5 107.5z" />
<glyph unicode="&#xe058;" horiz-adv-x="1533" d="M7.5 473q-2.5 -108 12.5 -168q10 -37 39.5 -52t59.5 -5l418 129q45 15 63 54.5t0 78.5t-65 53l-418 168q-28 6 -56 -15.5t-34 -62.5q-17 -72 -19.5 -180zM186 1450q-20 35 2.5 70t77.5 55q169 65 350 94q59 3 83.5 -18t27.5 -60q36 -467 51 -794q3 -49 -24 -76t-66 -19 t-65 50q-304 472 -437 698zM309 -169.5q-3 30.5 19 52.5l284 340q34 39 74 43t65.5 -29t20.5 -90v-452q-5 -28 -35 -44t-69 -5q-73 12 -173.5 52t-150.5 77q-32 25 -35 55.5zM920 629q4 32 28 59l266 367q19 20 54.5 14.5t60.5 -37.5q51 -53 108 -144t76 -151q11 -38 -4 -64 t-45 -32l-428 -109q-35 -9 -63.5 3.5t-42.5 37t-10 56.5zM939.5 264q1.5 -32 23.5 -61l233 -389q16 -22 55 -19.5t68 31.5q56 49 114 128t85 142q14 40 3 70t-40 39l-420 143q-36 14 -63.5 3t-43.5 -33t-14.5 -54z" />
<glyph unicode="&#xe059;" horiz-adv-x="1478" d="M0 94v27v168q1 13 5.5 24.5t8.5 16.5l4 6q133 157 399.5 471.5t399.5 472.5q-116 -23 -210.5 -29.5t-149 0t-95.5 21.5t-56.5 27.5t-24.5 25.5q-2 0 -2 2q-3 4 -5 6q-12 29 -6 80q9 96 101 225q4 5 12 14.5t12 14.5q20 20 56 2q36 -20 40 -20q114 -46 304 -54 q262 -12 544 47q19 4 28.5 -3.5t8.5 -16.5v-10q-3 -28 -9 -86.5t-9 -87.5q-1 -6 -8 -15t-14.5 -16.5t-18 -17t-12.5 -11.5q-70 -82 -330 -385q-145 -169 -389 -448q-62 -73 -123 -144q-23 -26 -27 -37.5t-4 -48.5q139 5 309 -100q92 -60 176 -104.5t122 -58.5l38 -15 q250 -67 395 47q12 -47 8.5 -100.5t-14 -106.5t-37.5 -100.5t-61.5 -82.5t-85.5 -52.5t-109 -10.5q-80 9 -160.5 45t-179.5 93q-46 27 -147.5 104.5t-161.5 108.5q-98 50 -193.5 63.5t-240.5 13.5h-63q-18 0 -25 35z" />
<glyph unicode="&#xe05a;" horiz-adv-x="2525" d="M-129 1122q190 36 288.5 69.5t137.5 76.5q6 7 16 20.5t35 61.5t41 104q24 -30 36.5 -62t13.5 -57t-1 -45.5t-6 -31.5l-4 -11q-12 -27 -21 -43.5t-15.5 -27.5t-20 -22.5t-21 -17.5t-32.5 -22.5t-42 -28.5q-72 -55 -69 -128t63 -111q53 -32 103.5 12.5t107.5 159.5 q22 45 34.5 106.5t19.5 105.5t36 97.5t84 95.5q31 24 67.5 33.5t70.5 8.5t71.5 -3.5t78 2t82.5 20.5t92 56.5t100 104.5q5 7 14.5 17.5t41 38t65.5 48t85.5 37.5t103.5 16.5t117 -25t128 -77.5q-72 31 -137.5 39.5t-112.5 -3t-87 -32.5t-65.5 -48.5t-44 -52t-25.5 -41.5 l-8 -16q-24 -57 -45 -99t-38 -71t-37 -48.5t-35.5 -31t-39.5 -20t-42.5 -13t-52 -12.5t-60.5 -16q-75 -23 -97 -90q-20 -60 13 -105q41 -54 124 -29q34 10 71 33q62 40 103 82t60.5 75.5t39 66.5t54 65t89.5 60q69 34 138 51t116 18.5t112 3.5t109 9q61 10 103 18.5 t110.5 27.5t117 42t97.5 61t76 84q11 18 27.5 48t44 101t28.5 117q34 -119 23 -213.5t-54 -164t-90 -116t-84 -68.5l-37 -23q-124 -64 -218.5 -98.5t-139 -40.5t-98 -20t-105.5 -42q-210 -113 -106 -318q43 -84 125.5 -91t158.5 61l11 11t25 30t32 48.5t26 64.5t13 79 q27 -63 32.5 -124.5t-5.5 -104.5t-26 -77t-29 -51l-13 -16q-55 -59 -94.5 -89.5t-90 -46.5t-108 -10t-143.5 30q-109 30 -182.5 38t-112 -0.5t-76.5 -31.5q-57 -33 -87.5 -87.5t-4.5 -99.5q20 -34 43 -47.5t62 -6t96 41.5q79 47 134 61t140 17q79 2 156 -18t129 -60 q75 -49 151 -117q-83 49 -162.5 62.5t-135 -1.5t-98.5 -37.5t-64 -43.5l-21 -21q-70 -80 -118.5 -122.5t-93.5 -56t-80 -8t-91 27.5q-25 10 -61 26t-61 26.5t-44 14.5v226q0 120 -49.5 219.5t-138.5 161.5q-121 81 -275 81q-156 0 -274 -79q-42 58 -105 67v154q0 63 -44 107 t-107 44h-37zM131 -33v332q0 134 39 203q38 67 109 106t165 39q142 0 227 -89t85 -224v-277h-414v-137q0 -47 27 -72.5t75 -25.5q94 0 103 100h209q0 -128 -84 -215q-88 -88 -228 -88q-94 0 -165 39t-109 106q-39 69 -39 203zM342 215h205v115q0 56 -27.5 85t-75.5 29 t-75 -29t-27 -85v-115zM879 -193q22 74 22 146q0 23 -6 41q50 -8 70 -10q125 -11 174 -95q2 -2 4 -6.5t8.5 -19t9 -28.5t4.5 -35.5t-4 -39.5q-39 32 -83 48.5t-78 17t-62 -3t-44 -9.5z" />
<glyph unicode="&#xe05b;" horiz-adv-x="1742" d="M4 -115q6 63 25 135q23 93 148 719t145 715q13 55 22 85.5t25 59.5t34 42t51 24t72.5 13.5t102.5 2.5h493q121 0 276 -2t238 -2q73 0 97 -72q25 -73 -12 -158q-41 -93 -129 -122q-31 -10 -66 -10h-821l-103 -477l721 -7q77 0 104 -71q26 -69 -9 -146q-38 -85 -121 -108 q-24 -6 -50 -6h-737l-98 -508h884q130 0 157 -92q21 -73 -28 -161q-44 -78 -109 -100q-19 -6 -36 -6h-1133q-62 0 -98 35t-43.5 94.5t-1.5 122.5z" />
<glyph unicode="&#xe05c;" horiz-adv-x="1777" d="M0 221q0 91 64.5 156t156.5 65q91 0 156 -65t65 -156q0 -94 -69 -160q133 -151 334 -151q141 0 237 84q96 87 96 217q0 81 -51 158q-48 78 -123 131q-65 44 -235 116q-154 61 -250 121q-87 57 -150 131q-62 73 -94 152q-28 79 -28 162q0 209 165 352q168 145 404 145 q159 0 317 -67q135 -59 232 -164q76 -66 76 -164q0 -92 -65.5 -156.5t-156.5 -64.5q-92 0 -156.5 64.5t-64.5 156.5q0 39 21 90q-65 33 -181 33q-141 0 -229 -64t-88 -163q0 -87 78 -156q81 -68 262 -145q183 -79 289 -150q103 -68 168 -151q68 -83 98 -175q1 -2 2 -6t2 -6 q-72 -60 -113 -143.5t-41 -179.5q0 -109 58 -213q-25 -31 -49 -56q-177 -168 -422 -168q-243 0 -426 131q-181 130 -285 398l6 2q-10 30 -10 59zM1255 68q0 -106 74 -181t180 -75t180 75t74 181q0 105 -74.5 179.5t-179.5 74.5t-179.5 -74.5t-74.5 -179.5z" />
<glyph unicode="&#xe05d;" horiz-adv-x="1359" d="M0 1649h205l16 -248h21l10 6q79 133 208 202.5t296 69.5q176 0 313.5 -91.5t214 -255.5t76.5 -374q0 -154 -36.5 -282t-98.5 -216t-145.5 -149t-176.5 -89.5t-192 -28.5q-142 0 -258 58.5t-177 162.5l-4 6h-22l-6 -14v-775h-232v1559q0 181 -12 459zM244 803 q0 -63 12 -113q38 -143 151 -231t259 -88q207 0 331.5 157t124.5 418q0 244 -124.5 397.5t-321.5 153.5q-147 0 -262 -93t-152 -241q-18 -67 -18 -108v-252z" />
<glyph unicode="&#xe05e;" horiz-adv-x="1773" d="M0 -401v1015v1016l1026 -1016zM0 -401l1026 1015l274 -272l-413 -240l-887 -512v9zM0 1630v8l887 -512l413 -239l-274 -273zM1026 614l274 273l474 -273l-474 -272z" />
<glyph unicode="&#xe05f;" horiz-adv-x="2650" d="M27 643v105q0 51 81.5 94.5t222 68.5t306.5 25t306.5 -25t222 -68.5t81.5 -94.5v-105q-2 -77 -180.5 -131.5t-429.5 -54.5t-429.5 54.5t-180.5 131.5zM614 1141v55q3 38 131 66t306 28q177 0 304 -28t130 -66h2v-55h-2q-3 -40 -129.5 -68.5t-304.5 -28.5t-305 28.5 t-132 68.5zM1047 129v121v10q5 75 112.5 137.5t288 98.5t393.5 36t393.5 -36t287.5 -98.5t112 -137.5v-8v-115v-8q-4 -75 -111 -137.5t-288 -98.5t-394 -36t-393.5 36t-288 98.5t-112.5 137.5zM1513 827v2v78v6q5 59 155 100.5t360 41.5t359.5 -41.5t154.5 -100.5v-4v-80v-2 q0 -60 -150 -102.5t-364 -42.5t-364.5 42.5t-150.5 102.5z" />
<glyph unicode="&#xe06a;" horiz-adv-x="2312" d="M0 1319q0 40 27 67t67 27h481q33 0 59.5 -20t33.5 -52l49 -200h1380l14 43q9 30 34 47.5t55 17.5q18 0 26 -4q30 -7 49 -32.5t19 -55.5q0 -11 -4 -27l-242 -804q-7 -30 -32 -49t-56 -19h-1145q-32 0 -58 20.5t-34 51.5l-221 897h-408q-40 0 -67 27t-27 65zM762 954 l127 -508h1001l4 19l148 489h-1280zM848 -37q0 78 56 133.5t134 55.5t133.5 -55.5t55.5 -133.5t-55.5 -133t-133.5 -55t-134 55t-56 133zM981 795h340v-189h-299zM1483 606l2 189h332l-50 -189h-284zM1559 -37q0 78 56 133.5t134 55.5t133 -55.5t55 -133.5t-55 -133 t-133 -55t-134 55t-56 133z" />
<glyph unicode="&#xe06b;" horiz-adv-x="1918" d="M10 -184v682l238 264h174v-131h-115l-164 -182v-502h1629v502l-164 182h-115v131h174l236 -264v-682h-1893zM422 469v162v131v383l309 309h762v-692v-131v-162h-1071zM545 594h825v737h-516v-309h-309v-428z" />
<glyph unicode="&#xe06c;" horiz-adv-x="1425" d="M-20 176q0 129 57 201q49 64 151 92q-32 83 -32 121q0 69 71 143q75 72 142 72q31 0 82 -19q-103 297 -142 426q-49 165 -49 236q0 99 49 154q51 57 137 57q149 0 355 -604l35 -101q11 28 24 68q101 295 192.5 446.5t176.5 151.5q82 0 129 -55q49 -55 49 -144 q0 -59 -49 -231q-45 -153 -135 -410q115 -27 168 -110q55 -86 55 -273q0 -363 -217 -596q-221 -231 -561 -231q-135 0 -260 49q-123 44 -220 135q-102 95 -155 203q-53 109 -53 219zM113 186q0 -50 28 -120q26 -66 80 -138q82 -108 199 -166q117 -57 264 -57q263 0 444 199 q179 197 179 497q0 89 -13 146q-10 44 -39 69q-55 45 -217 82q-161 37 -344 37q-45 0 -57 -12q-14 -7 -14 -41q0 -83 94 -119q101 -43 338 -43h57q33 0 47 -22q17 -17 23 -64q-34 -34 -113 -63q-64 -24 -108 -54q-83 -61 -132 -141q-47 -78 -47 -151q0 -41 21 -105 q22 -68 22 -88v-12l-8 -31q-49 3 -79.5 23.5t-51.5 60.5q-36 65 -39 168q-6 -2 -24 -2h-23q2 -10 2 -25q0 -60 -47 -104q-46 -43 -111 -43q-96 0 -194 92q-98 95 -98 184q0 18 6 33q3 16 32 45q51 -62 70 -88q86 -121 156 -121q19 0 35 13q12 12 12 20q0 13 -25 59 q-17 30 -76 109q-60 77 -92 102q-32 29 -45 29q-38 0 -75 -45q-37 -46 -37 -113zM297 578q0 -28 33 -93q31 -59 92 -139q64 -82 115 -127q49 -41 73 -41q14 0 29 15q14 17 14 32q0 26 -37 119q-48 113 -94 188q-49 76 -80 101q-34 31 -63 31q-22 0 -51 -31q-31 -31 -31 -55z M389 1462q0 -71 49 -219q45 -144 135 -389q19 12 58 12q2 0 14 -1t21 -1q7 0 53 -4l-141 410q-56 158 -91 213q-26 43 -53 43q-17 0 -31 -17t-14 -47zM729 418q26 -64 39 -103q41 48 82 78q-10 2 -31 5t-31 5q-35 7 -59 15zM944 836l137 -25q102 284 142 408q51 165 51 196 q0 33 -14 51q-9 15 -31 15q-29 0 -64 -56q-36 -55 -88 -206z" />
<glyph unicode="&#xe06d;" d="M0 -205v1639q0 85 60 144.5t145 59.5h1638q85 0 145 -59.5t60 -144.5v-1639q0 -85 -60 -145t-145 -60h-1638q-85 0 -145 60t-60 145zM410 614q0 -167 82 -308.5t223.5 -223.5t308.5 -82t308.5 82t223.5 223.5t82 308.5q0 125 -48.5 239t-130.5 196.5t-196 131t-239 48.5 t-239 -48.5t-196 -131t-130.5 -196.5t-48.5 -239zM614 614q0 169 120.5 289.5t289.5 120.5t289.5 -120.5t120.5 -289.5t-120.5 -289t-289.5 -120t-289.5 120t-120.5 289z" />
<glyph unicode="&#xe06e;" horiz-adv-x="1425" />
<glyph horiz-adv-x="1359" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -0,0 +1,420 @@
@charset "UTF-8";
/*!
Code written by Sam Collins (@smcllns) of www.eventasaur.us
You are free to use this work commercially
You are free to extend this work without permissions from the author (just do so tastefully eh?)
Enjoy
*/
/* Reference icons from font-files */
@font-face {
font-family: 'zocial';
font-style: normal;
font-weight: normal;
src: url('zocial-regular-webfont.eot');
src: url('zocial-regular-webfont.eot?#iefix') format('embedded-opentype'),
url('zocial-regular-webfont.woff') format('woff'),
url('zocial-regular-webfont.ttf') format('truetype'),
url('zocial-regular-webfont.svg#ZocialRegular') format('svg');
unicode-range: U+0-U+10FFFF;
}
/* Button structure */
.zocial,
a.zocial {
border: 1px solid #777;
border-color: rgba(0,0,0,0.2);
border-bottom-color: #333;
border-bottom-color: rgba(0,0,0,0.4);
color: #fff;
-moz-box-shadow: inset 0 0.08em 0 rgba(255,255,255,0.4), inset 0 0 0.1em rgba(255,255,255,0.9);
-webkit-box-shadow: inset 0 0.08em 0 rgba(255,255,255,0.4), inset 0 0 0.1em rgba(255,255,255,0.9);
box-shadow: inset 0 0.08em 0 rgba(255,255,255,0.4), inset 0 0 0.1em rgba(255,255,255,0.9);
cursor: pointer;
display: inline-block;
font: bold 100%/2.1 "Lucida Grande", Tahoma, sans-serif;
padding: 0 .95em 0 0;
text-align: center;
text-decoration: none;
text-shadow: 0 1px 0 rgba(0,0,0,0.5);
white-space: nowrap;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
position: relative;
-moz-border-radius: .3em;
-webkit-border-radius: .3em;
border-radius: .3em;
}
.zocial:before {
content: "";
border-right: 0.075em solid rgba(0,0,0,0.1);
float: left;
font: 120%/1.65 zocial;
font-style: normal;
font-weight: normal;
margin: 0 0.5em 0 0;
padding: 0 0.5em;
text-align: center;
text-decoration: none;
text-transform: none;
-moz-box-shadow: 0.075em 0 0 rgba(255,255,255,0.25);
-webkit-box-shadow: 0.075em 0 0 rgba(255,255,255,0.25);
box-shadow: 0.075em 0 0 rgba(255,255,255,0.25);
-webkit-font-smoothing: antialiased;
}
.zocial:active {
outline: none; /* outline is visible on :focus */
}
/* Buttons can be displayed as standalone icons by adding a class of "icon" */
.zocial.icon {
overflow: hidden;
max-width: 2.4em;
padding-left: 0;
padding-right: 0;
max-height: 2.15em;
white-space: nowrap;
}
.zocial.icon:before {
padding: 0;
width: 2em;
height: 2em;
box-shadow: none;
border: none;
}
/* Gradients */
.zocial {
background-image: -moz-linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.05) 49%, rgba(0,0,0,.05) 51%, rgba(0,0,0,.1));
background-image: -ms-linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.05) 49%, rgba(0,0,0,.05) 51%, rgba(0,0,0,.1));
background-image: -o-linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.05) 49%, rgba(0,0,0,.05) 51%, rgba(0,0,0,.1));
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,.1)), color-stop(49%, rgba(255,255,255,.05)), color-stop(51%, rgba(0,0,0,.05)), to(rgba(0,0,0,.1)));
background-image: -webkit-linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.05) 49%, rgba(0,0,0,.05) 51%, rgba(0,0,0,.1));
background-image: linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.05) 49%, rgba(0,0,0,.05) 51%, rgba(0,0,0,.1));
}
.zocial:hover, .zocial:focus {
background-image: -moz-linear-gradient(rgba(255,255,255,.15) 49%, rgba(0,0,0,.1) 51%, rgba(0,0,0,.15));
background-image: -ms-linear-gradient(rgba(255,255,255,.15) 49%, rgba(0,0,0,.1) 51%, rgba(0,0,0,.15));
background-image: -o-linear-gradient(rgba(255,255,255,.15) 49%, rgba(0,0,0,.1) 51%, rgba(0,0,0,.15));
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,.15)), color-stop(49%, rgba(255,255,255,.15)), color-stop(51%, rgba(0,0,0,.1)), to(rgba(0,0,0,.15)));
background-image: -webkit-linear-gradient(rgba(255,255,255,.15) 49%, rgba(0,0,0,.1) 51%, rgba(0,0,0,.15));
background-image: linear-gradient(rgba(255,255,255,.15) 49%, rgba(0,0,0,.1) 51%, rgba(0,0,0,.15));
}
.zocial:active {
background-image: -moz-linear-gradient(bottom, rgba(255,255,255,.1), rgba(255,255,255,0) 30%, transparent 50%, rgba(0,0,0,.1));
background-image: -ms-linear-gradient(bottom, rgba(255,255,255,.1), rgba(255,255,255,0) 30%, transparent 50%, rgba(0,0,0,.1));
background-image: -o-linear-gradient(bottom, rgba(255,255,255,.1), rgba(255,255,255,0) 30%, transparent 50%, rgba(0,0,0,.1));
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,.1)), color-stop(30%, rgba(255,255,255,0)), color-stop(50%, transparent), to(rgba(0,0,0,.1)));
background-image: -webkit-linear-gradient(bottom, rgba(255,255,255,.1), rgba(255,255,255,0) 30%, transparent 50%, rgba(0,0,0,.1));
background-image: linear-gradient(bottom, rgba(255,255,255,.1), rgba(255,255,255,0) 30%, transparent 50%, rgba(0,0,0,.1));
}
/* Adjustments for light background buttons */
.zocial.bitcoin,
.zocial.cloudapp,
.zocial.dropbox,
.zocial.email,
.zocial.eventful,
.zocial.github,
.zocial.gmail,
.zocial.instapaper,
.zocial.itunes,
.zocial.ninetyninedesigns,
.zocial.openid,
.zocial.plancast,
.zocial.posterous,
.zocial.reddit,
.zocial.secondary,
.zocial.viadeo,
.zocial.weibo,
.zocial.wikipedia {
border: 1px solid #aaa;
border-color: rgba(0,0,0,0.3);
border-bottom-color: #777;
border-bottom-color: rgba(0,0,0,0.5);
-moz-box-shadow: inset 0 0.08em 0 rgba(255,255,255,0.7), inset 0 0 0.08em rgba(255,255,255,0.5);
-webkit-box-shadow: inset 0 0.08em 0 rgba(255,255,255,0.7), inset 0 0 0.08em rgba(255,255,255,0.5);
box-shadow: inset 0 0.08em 0 rgba(255,255,255,0.7), inset 0 0 0.08em rgba(255,255,255,0.5);
text-shadow: 0 1px 0 rgba(255,255,255,0.8);
}
/* :hover adjustments for light background buttons */
.zocial.bitcoin:focus,
.zocial.bitcoin:hover,
.zocial.dropbox:focus,
.zocial.dropbox:hover,
.zocial.email:focus,
.zocial.email:hover,
.zocial.eventful:focus,
.zocial.eventful:hover,
.zocial.github:focus,
.zocial.github:hover,
.zocial.gmail:focus,
.zocial.gmail:hover,
.zocial.instapaper:focus,
.zocial.instapaper:hover,
.zocial.itunes:focus,
.zocial.itunes:hover,
.zocial.ninetyninedesigns:focus,
.zocial.ninetyninedesigns:hover,
.zocial.openid:focus,
.zocial.openid:hover,
.zocial.plancast:focus,
.zocial.plancast:hover,
.zocial.posterous:focus,
.zocial.posterous:hover,
.zocial.reddit:focus,
.zocial.reddit:hover,
.zocial.secondary:focus,
.zocial.secondary:hover,
.zocial.twitter:focus,
.zocial.viadeo:focus,
.zocial.viadeo:hover,
.zocial.weibo:focus,
.zocial.weibo:hover,
.zocial.wikipedia:focus,
.zocial.wikipedia:hover {
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.5)), color-stop(49%, rgba(255,255,255,0.2)), color-stop(51%, rgba(0,0,0,0.05)), to(rgba(0,0,0,0.15)));
background-image: -moz-linear-gradient(top, rgba(255,255,255,0.5), rgba(255,255,255,0.2) 49%, rgba(0,0,0,0.05) 51%, rgba(0,0,0,0.15));
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.5), rgba(255,255,255,0.2) 49%, rgba(0,0,0,0.05) 51%, rgba(0,0,0,0.15));
background-image: -o-linear-gradient(top, rgba(255,255,255,0.5), rgba(255,255,255,0.2) 49%, rgba(0,0,0,0.05) 51%, rgba(0,0,0,0.15));
background-image: -ms-linear-gradient(top, rgba(255,255,255,0.5), rgba(255,255,255,0.2) 49%, rgba(0,0,0,0.05) 51%, rgba(0,0,0,0.15));
background-image: linear-gradient(top, rgba(255,255,255,0.5), rgba(255,255,255,0.2) 49%, rgba(0,0,0,0.05) 51%, rgba(0,0,0,0.15));
}
/* :active adjustments for light background buttons */
.zocial.bitcoin:active,
.zocial.dropbox:active,
.zocial.email:active,
.zocial.eventful:active,
.zocial.github:active,
.zocial.gmail:active,
.zocial.instapaper:active,
.zocial.itunes:active,
.zocial.ninetyninedesigns:active,
.zocial.openid:active,
.zocial.plancast:active,
.zocial.posterous:active,
.zocial.reddit:active,
.zocial.secondary:active,
.zocial.viadeo:active,
.zocial.weibo:active,
.zocial.wikipedia:active {
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0)), color-stop(30%, rgba(255,255,255,0)), color-stop(50%, rgba(0,0,0,0)), to(rgba(0,0,0,0.1)));
background-image: -moz-linear-gradient(bottom, rgba(255,255,255,0), rgba(255,255,255,0) 30%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.1));
background-image: -webkit-linear-gradient(bottom, rgba(255,255,255,0), rgba(255,255,255,0) 30%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.1));
background-image: -o-linear-gradient(bottom, rgba(255,255,255,0), rgba(255,255,255,0) 30%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.1));
background-image: -ms-linear-gradient(bottom, rgba(255,255,255,0), rgba(255,255,255,0) 30%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.1));
background-image: linear-gradient(bottom, rgba(255,255,255,0), rgba(255,255,255,0) 30%, rgba(0,0,0,0) 50%, rgba(0,0,0,0.1));
}
/* Button icon and color */
/* Icon characters are stored in unicode private area */
.zocial.amazon:before {content: "\E040";}
.zocial.android:before {content: "\E005";}
.zocial.angellist:before {content: "\E06C";}
.zocial.aol:before {content: "\E001";}
.zocial.appstore:before {content: "\E020";}
.zocial.bitcoin:before {content: "\E011"; color: #f7931a;}
.zocial.blogger:before {content: "\E021";}
.zocial.call:before {content: "\E016";}
.zocial.cal:before {content: "\E00D";}
.zocial.cart:before {content: "\E06A";}
.zocial.chrome:before {content: "\E03A";}
.zocial.cloudapp:before {content: "\E042";}
.zocial.creativecommons:before {content: "\E022";}
.zocial.delicious:before {content: "\E002";}
.zocial.digg:before {content: "\E01A";}
.zocial.disqus:before {content: "\E030";}
.zocial.dribbble:before {content: "\E023";}
.zocial.dropbox:before {content: "\E043"; color: #1f75cc;}
.zocial.email:before {content: "\E03C"; color: #312c2a;}
.zocial.eventasaurus:before {content: "\E055"; color: #9de428;}
.zocial.eventbrite:before {content: "\E05B";}
.zocial.eventful:before {content: "\E006"; color: #0066CC;}
.zocial.evernote:before {content: "\E024";}
.zocial.facebook:before {content: "\E045";}
.zocial.fivehundredpx:before {content: "\E00F"; color: #29b6ff;}
.zocial.flattr:before {content: "\E004";}
.zocial.flickr:before {content: "\E025";}
.zocial.forrst:before {content: "\E019"; color: #50894f;}
.zocial.foursquare:before {content: "\E013";}
.zocial.github:before {content: "\E046";}
.zocial.gmail:before {content: "\E04C"; color: #f00;}
.zocial.google:before {content: "\E026";}
.zocial.googleplay:before {content: "\E05E";}
.zocial.googleplus:before {content: "\E00A";}
.zocial.gowalla:before {content: "\E01F";}
.zocial.grooveshark:before {content: "\E017";}
.zocial.guest:before {content: "\E01E";}
.zocial.html5:before {content: "\E014";}
.zocial.ie:before {content: "\E015";}
.zocial.instagram:before {content: "\E06D";}
.zocial.instapaper:before {content: "\E028";}
.zocial.intensedebate:before {content: "\E05A";}
.zocial.itunes:before {content: "\E048"; color: #1a6dd2;}
.zocial.klout:before {content: "\E02A"; }
.zocial.lanyrd:before {content: "\E00C";}
.zocial.lastfm:before {content: "\E04B";}
.zocial.linkedin:before {content: "\E02B";}
.zocial.macstore:before {content: "\E03D";}
.zocial.meetup:before {content: "\E02C";}
.zocial.myspace:before {content: "\E03E";}
.zocial.ninetyninedesigns:before {content: "\E018"; color: #f50;}
.zocial.openid:before {content: "\E04E"; color: #ff921d;}
.zocial.opentable:before {content: "\E05F";}
.zocial.paypal:before {content: "\E003";}
.zocial.pinboard:before {content: "\E04D";}
.zocial.pinterest:before {content: "\E010";}
.zocial.plancast:before {content: "\E02F";}
.zocial.plurk:before {content: "\E049";}
.zocial.podcast:before {content: "\E03F";}
.zocial.posterous:before {content: "\E05D";}
.zocial.print:before {content: "\E06B";}
.zocial.quora:before {content: "\E050";}
.zocial.reddit:before {content: "\E01D"; color: red;}
.zocial.rss:before {content: "\E031";}
.zocial.scribd:before {content: "\E05C"; color: #00d5ea;}
.zocial.skype:before {content: "\E032";}
.zocial.smashing:before {content: "\E009";}
.zocial.songkick:before {content: "\E04A";}
.zocial.soundcloud:before {content: "\E052";}
.zocial.spotify:before {content: "\E01C";}
.zocial.stumbleupon:before {content: "\E00E";}
.zocial.tumblr:before {content: "\E053";}
.zocial.twitter:before {content: "\E033";}
.zocial.viadeo:before {content: "\E027"; color: #f59b20;}
.zocial.vimeo:before {content: "\E035";}
.zocial.weibo:before {content: "\E029"; color: #e6162d;}
.zocial.wikipedia:before {content: "\E00B";}
.zocial.windows:before {content: "\E036";}
.zocial.xing:before {content: "\E037"}
.zocial.wordpress:before {content: "\E056";}
.zocial.yahoo:before {content: "\E038";}
.zocial.yelp:before {content: "\E058";}
.zocial.youtube:before {content: "\E034";}
/* Button background and text color */
.zocial.amazon {background-color: #ffad1d; color: #030037; text-shadow: 0 1px 0 rgba(255,255,255,0.5);}
.zocial.android {background-color: #a4c639;}
.zocial.angellist {background-color: #000;}
.zocial.aol {background-color: #f00;}
.zocial.appstore {background-color: #000;}
.zocial.bitcoin {background-color: #efefef; color: #4d4d4d;}
.zocial.blogger {background-color: #ee5a22;}
.zocial.call {background-color: #008000;}
.zocial.cal {background-color: #d63538;}
.zocial.cart {background-color: #333;}
.zocial.chrome {background-color: #006cd4;}
.zocial.cloudapp {background-color: #fff; color: #312c2a;}
.zocial.creativecommons {background-color: #000;}
.zocial.delicious {background-color: #3271cb;}
.zocial.digg {background-color: #164673;}
.zocial.disqus {background-color: #5d8aad;}
.zocial.dribbble {background-color: #ea4c89;}
.zocial.dropbox {background-color: #fff; color: #312c2a;}
.zocial.email {background-color: #f0f0eb; color: #312c2a;}
.zocial.eventasaurus {background-color: #192931; color: #fff;}
.zocial.eventbrite {background-color: #ff5616;}
.zocial.eventful {background-color: #fff; color: #47ab15;}
.zocial.evernote {background-color: #6bb130; color: #fff;}
.zocial.facebook {background-color: #4863ae;}
.zocial.fivehundredpx {background-color: #333;}
.zocial.flattr {background-color: #8aba42;}
.zocial.flickr {background-color: #ff0084;}
.zocial.forrst {background-color: #1e360d;}
.zocial.foursquare {background-color: #44a8e0;}
.zocial.github {background-color: #fbfbfb; color: #050505;}
.zocial.gmail {background-color: #efefef; color: #222;}
.zocial.google {background-color: #4e6cf7;}
.zocial.googleplay {background-color: #000;}
.zocial.googleplus {background-color: #dd4b39;}
.zocial.gowalla {background-color: #ff720a;}
.zocial.grooveshark {background-color: #111; color:#eee;}
.zocial.guest {background-color: #1b4d6d;}
.zocial.html5 {background-color: #ff3617;}
.zocial.ie {background-color: #00a1d9;}
.zocial.instapaper {background-color: #eee; color: #222;}
.zocial.instagram {background-color: #3f729b;}
.zocial.intensedebate {background-color: #0099e1;}
.zocial.klout {background-color: #e34a25;}
.zocial.itunes {background-color: #efefeb; color: #312c2a;}
.zocial.lanyrd {background-color: #2e6ac2;}
.zocial.lastfm {background-color: #dc1a23;}
.zocial.linkedin {background-color: #0083a8;}
.zocial.macstore {background-color: #007dcb}
.zocial.meetup {background-color: #ff0026;}
.zocial.myspace {background-color: #000;}
.zocial.ninetyninedesigns {background-color: #fff; color: #072243;}
.zocial.openid {background-color: #f5f5f5; color: #333;}
.zocial.opentable {background-color: #990000;}
.zocial.paypal {background-color: #fff; color: #32689a; text-shadow: 0 1px 0 rgba(255,255,255,0.5);}
.zocial.pinboard {background-color: blue;}
.zocial.pinterest {background-color: #c91618;}
.zocial.plancast {background-color: #e7ebed; color: #333;}
.zocial.plurk {background-color: #cf682f;}
.zocial.podcast {background-color: #9365ce;}
.zocial.posterous {background-color: #ffd959; color: #bc7134;}
.zocial.print {background-color: #f0f0eb; color: #222; text-shadow: 0 1px 0 rgba(255,255,255,0.8);}
.zocial.quora {background-color: #a82400;}
.zocial.reddit {background-color: #fff; color: #222;}
.zocial.rss {background-color: #ff7f25;}
.zocial.scribd {background-color: #231c1a;}
.zocial.skype {background-color: #00a2ed;}
.zocial.smashing {background-color: #ff4f27;}
.zocial.songkick {background-color: #ff0050;}
.zocial.soundcloud {background-color: #ff4500;}
.zocial.spotify {background-color: #60af00;}
.zocial.stumbleupon {background-color: #eb4924;}
.zocial.tumblr {background-color: #374a61;}
.zocial.twitter {background-color: #46c0fb;}
.zocial.viadeo {background-color: #fff; color: #000;}
.zocial.vimeo {background-color: #00a2cd;}
.zocial.weibo {background-color: #faf6f1; color: #000;}
.zocial.wikipedia {background-color: #fff; color: #000;}
.zocial.windows {background-color: #0052a4; color: #fff;}
.zocial.wordpress {background-color: #464646;}
.zocial.xing {background-color: #0A5D5E;}
.zocial.yahoo {background-color: #a200c2;}
.zocial.yelp {background-color: #e60010;}
.zocial.youtube {background-color: #f00;}
/*
The Miscellaneous Buttons
These button have no icons and can be general purpose buttons while ensuring consistent button style
Credit to @guillermovs for suggesting
*/
.zocial.primary, .zocial.secondary {margin: 0.1em 0; padding: 0 1em;}
.zocial.primary:before, .zocial.secondary:before {display: none;}
.zocial.primary {background-color: #333;}
.zocial.secondary {background-color: #f0f0eb; color: #222; text-shadow: 0 1px 0 rgba(255,255,255,0.8);}
/* Any browser-specific adjustments */
button:-moz-focus-inner {
border: 0;
padding: 0;
}

View File

@ -0,0 +1,265 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Zocial CSS3 Buttons</title>
<link rel="stylesheet" type="text/css" href="css/zocial.css" />
<link href='http://fonts.googleapis.com/css?family=Pompiere' rel='stylesheet' type='text/css'>
<style>
html {
background: #f0f0eb;
font-family: "Helvetica Neue", sans-serif;
}
h2, p {
font-family: "Pompiere","Helvetica Neue", sans-serif;
}
body {
background: #FFF;
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.5);
-moz-box-shadow: 0 1px 1px rgba(0,0,0,0.5);
box-shadow: 0 1px 1px rgba(0,0,0,0.5);
-webkit-border-radius: 0 0 2px 2px;
-moz-border-radius: 0 0 2px 2px;
border-radius: 0 0 2px 2px;
margin: 0 auto 2em;
padding: 2em 1em;
width: 600px;
}
body .zocial {
margin: 8px 4px;
font-size: 13px;
}
h2 {
font-size: 17px;
font-weight: normal;
padding: 1em 0 0.75em;
border-bottom: 1px solid #eee;
}
</style>
<!--[if IE]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
</head>
<body>
<h2 id="popular-kids">The Popular Kids</h2>
<a href="#" class="zocial facebook">Sign in with Facebook</a>
<a href="#" class="zocial googleplus">Sign in with Google+</a>
<a href="#" class="zocial twitter">Sign in with Twitter</a>
<a href="#" class="zocial linkedin">Sign in with LinkedIn</a>
<h2 id="cool-kids">The Cool Kids</h2>
<a href="#" class="zocial dropbox">Sync with Dropbox</a>
<a href="#" class="zocial evernote">Clip to Evernote</a>
<a href="#" class="zocial forrst">Follow me on Forrst</a>
<a href="#" class="zocial dribbble">Sign in with Dribbble</a>
<a href="#" class="zocial cloudapp">Sign in to CloudApp</a>
<a href="#" class="zocial github">Fork me on Github</a>
<a href="#" class="zocial spotify">Play on Spotify</a>
<a href="#" class="zocial instapaper">Read It Later</a>
<a href="#" class="zocial soundcloud">Follow me on Soundcloud</a>
<a href="#" class="zocial tumblr">Follow me on Tumblr</a>
<a href="#" class="zocial smashing">Read on Smashing Magazine</a>
<a href="#" class="zocial itunes">Available on iTunes</a>
<a href="#" class="zocial appstore">Available on the App Store</a>
<a href="#" class="zocial macstore">Available on the Mac App Store</a>
<a href="#" class="zocial android">Available on Android Market</a>
<a href="#" class="zocial pinterest">Follow me on Pinterest</a>
<a href="#" class="zocial quora">Follow me on Quora</a>
<a href="#" class="zocial lanyrd">Attend on Lanyrd</a>
<h2 id="not-so-cool-kids">The Not-So-Cool But Have-To-Be-Invited Kids</h2>
<a href="#" class="zocial paypal">Pay with Paypal</a>
<a href="#" class="zocial amazon">Sign in with Amazon</a>
<a href="#" class="zocial skype">Call me on Skype</a>
<a href="#" class="zocial lastfm">Sign in with Last.fm</a>
<a href="#" class="zocial yelp">Write a review on Yelp</a>
<a href="#" class="zocial foursquare">Check in with foursquare</a>
<a href="#" class="zocial klout">Influence with Klout</a>
<h2 id="older-kids">The Older (but Solid) Kids</h2>
<a href="#" class="zocial wikipedia">View on Wikipedia</a>
<a href="#" class="zocial disqus">Sign in with Disqus</a>
<a href="#" class="zocial intensedebate">Sign in with IntenseDebate</a>
<a href="#" class="zocial google">Sign in with Google</a>
<a href="#" class="zocial gmail">Sign in with Gmail</a>
<a href="#" class="zocial vimeo">Upload to Vimeo</a>
<a href="#" class="zocial scribd">Read more on Scribd</a>
<a href="#" class="zocial youtube">Subscribe on YouTube</a>
<a href="#" class="zocial wordpress">Sign in with WordPress</a>
<a href="#" class="zocial songkick">Sign in with Songkick</a>
<a href="#" class="zocial posterous">Sign in with Posterous</a>
<a href="#" class="zocial eventbrite">Sign in with Eventbrite</a>
<a href="#" class="zocial flattr">Tip with Flattr</a>
<a href="#" class="zocial plancast">Follow me on Plancast</a>
<h2 id="smelly-kids">The Kids That Kinda Smell but Some People Don't Mind</h2>
<a href="#" class="zocial yahoo">Submit resume for CEO</a>
<a href="#" class="zocial ie">Download Internet Explorer 5</a>
<a href="#" class="zocial meetup">Report bugs with Meetup.com</a>
<a href="#" class="zocial openid">Learn how-to-use OpenID</a>
<a href="#" class="zocial html5">Register now for HTML6</a>
<a href="#" class="zocial aol">Chat with your parents</a>
<h2 id="quiet-kids">The Kids That Nobody Normally Notices</h2>
<a href="#" class="zocial guest">Sign in as guest</a>
<a href="#" class="zocial creativecommons">View Creative Commons Licence</a>
<a href="#" class="zocial rss">Subscribe to RSS</a>
<a href="#" class="zocial chrome">Add to Chrome</a>
<h2>The He's-My-Son-So-Of-Course-He's-Here Kid</h2>
<a href="#" class="zocial eventasaurus">Sign up for Eventasaurus</a>
<h2 id="kids-by-request">Kids By Request</h2>
<a href="#" class="zocial weibo">Join me on Weibo</a>
<a href="#" class="zocial plurk">Sign in with Plurk</a>
<a href="#" class="zocial grooveshark">Play on Grooveshark</a>
<a href="#" class="zocial blogger">Post on Blogger</a>
<a href="#" class="zocial viadeo">Sign in with Viadeo</a>
<a href="#" class="zocial podcast">Subscribe to this Podcast</a>
<a href="#" class="zocial fivehundredpx">View Portfolio on 500px</a>
<a href="#" class="zocial bitcoin">Bitcoin accepted here</a>
<a href="#" class="zocial ninetyninedesigns">View Portfolio on 99Designs</a>
<a href="#" class="zocial pinboard">Bookmark with Pinboard</a>
<a href="#" class="zocial stumbleupon">Stumble!</a>
<a href="#" class="zocial myspace">Find me on Myspace</a>
<a href="#" class="zocial windows">Sign in with Windows Live</a>
<a href="#" class="zocial eventful">Find Events with Eventful</a>
<a href="#" class="zocial xing">Sign in with Xing</a>
<a href="#" class="zocial flickr">Upload to Flickr</a>
<a href="#" class="zocial delicious">Sign in with Del.icio.us</a>
<a href="#" class="zocial googleplay">Download from Google Play</a>
<a href="#" class="zocial opentable">Reserve with OpenTable</a>
<a href="#" class="zocial digg">Digg this</a>
<a href="#" class="zocial reddit">Share on Reddit</a>
<a href="#" class="zocial angellist">Fund us on AngelList</a>
<a href="#" class="zocial instagram">Sign-in with Instagram</a>
<h2 id="multipurpose-kids">The Multi-Purpose Kids (Credit: <a href="http://pictos.drewwilson.com">Pictos Icons</a>)</h2>
<a href="#" class="zocial call">Call a phone</a>
<a href="#" class="zocial email">Send a message</a>
<a href="#" class="zocial cal">Add to calendar</a>
<a href="#" class="zocial cart">Add to cart</a>
<a href="#" class="zocial print">Print this page</a>
<a href="#" class="zocial primary" title="A primary button for general purposes to keep consistency with Zocial">Primary action</a>
<a href="#" class="zocial secondary" title="A secondary button for general purposes to keep consistency with Zocial">Secondary action</a>
<h2 id="icons">Icon versions of the above</h2>
<a href="#" class="zocial icon facebook">Sign in with Facebook</a>
<a href="#" class="zocial icon googleplus">Sign in with Google+</a>
<a href="#" class="zocial icon twitter">Sign in with Twitter</a>
<a href="#" class="zocial icon google">Sign in with Google</a>
<a href="#" class="zocial icon linkedin">Sign in with LinkedIn</a>
<a href="#" class="zocial icon paypal">Pay with Paypal</a>
<a href="#" class="zocial icon amazon">Sign in with Amazon</a>
<a href="#" class="zocial icon dropbox">Sync with Dropbox</a>
<a href="#" class="zocial icon evernote">Clip to Evernote</a>
<a href="#" class="zocial icon skype">Call me on Skype</a>
<a href="#" class="zocial icon guest">Sign in as guest</a>
<a href="#" class="zocial icon spotify">Play on Spotify</a>
<a href="#" class="zocial icon lastfm">Sign in with Last.fm</a>
<a href="#" class="zocial icon songkick">Sign in with Songkick</a>
<a href="#" class="zocial icon forrst">Follow me on Forrst</a>
<a href="#" class="zocial icon dribbble">Sign in with Dribbble</a>
<a href="#" class="zocial icon cloudapp">Sign in to CloudApp</a>
<a href="#" class="zocial icon github">Fork me on Github</a>
<a href="#" class="zocial pinterest icon">Follow me on Pinterest</a>
<a href="#" class="zocial quora icon">Follow me on Quora</a>
<a href="#" class="zocial pinboard icon">Bookmark with Pinboard</a>
<a href="#" class="zocial lanyrd icon">Attend on Lanyrd</a>
<a href="#" class="zocial icon itunes">Download on iTunes</a>
<a href="#" class="zocial icon android">Download on Android</a>
<a href="#" class="zocial icon disqus">Sign in with Disqus</a>
<a href="#" class="zocial icon yahoo">Sign in with Yahoo</a>
<a href="#" class="zocial icon vimeo">Upload to Vimeo</a>
<a href="#" class="zocial icon chrome">Add to Chrome</a>
<a href="#" class="zocial icon ie">Get a new browser</a>
<a href="#" class="zocial icon html5">Made from HTML5</a>
<a href="#" class="zocial icon instapaper">Read It Later</a>
<a href="#" class="zocial icon scribd">Read more on Scribd</a>
<a href="#" class="zocial icon wikipedia">View on Wikipedia</a>
<a href="#" class="zocial icon flattr">Tip with Flattr</a>
<a href="#" class="zocial icon tumblr">Follow me on Tumblr</a>
<a href="#" class="zocial icon posterous">Subscribe to my Posterous</a>
<a href="#" class="zocial icon gowalla">Check in with Gowalla</a>
<a href="#" class="zocial icon foursquare">Check in with foursquare</a>
<a href="#" class="zocial icon yelp">Write a review on Yelp</a>
<a href="#" class="zocial icon soundcloud">Follow me on Soundcloud</a>
<a href="#" class="zocial icon smashing">Read on Smashing Magazine</a>
<a href="#" class="zocial icon wordpress">Sign in with WordPress</a>
<a href="#" class="zocial icon intensedebate">Sign in with IntenseDebate</a>
<a href="#" class="zocial icon openid">Sign in with OpenID</a>
<a href="#" class="zocial icon gmail">Sign in with Gmail</a>
<a href="#" class="zocial icon eventbrite">Sign in with Eventbrite</a>
<a href="#" class="zocial icon eventasaurus">Sign in with Eventasaurus</a>
<a href="#" class="zocial icon meetup">Sign in with Meetup.com</a>
<a href="#" class="zocial icon aol">Sign in with AIM</a>
<a href="#" class="zocial icon plancast">Follow me on Plancast</a>
<a href="#" class="zocial icon youtube">Subscribe on YouTube</a>
<a href="#" class="zocial icon appstore">Available on the Mac App Store</a>
<a href="#" class="zocial icon creativecommons">View Creative Commons Licence</a>
<a href="#" class="zocial icon rss">Subscribe to RSS</a>
<a href="#" class="zocial weibo icon">Follow me on Weibo</a>
<a href="#" class="zocial plurk icon">Follow me on Plurk</a>
<a href="#" class="zocial grooveshark icon">Follow me on Grooveshark</a>
<a href="#" class="zocial blogger icon">Post on Blogger</a>
<a href="#" class="zocial viadeo icon">Sign in with Viadeo</a>
<a href="#" class="zocial podcast icon">Subscribe to this Podcast</a>
<a href="#" class="zocial fivehundredpx icon">View Portfolio on 500px</a>
<a href="#" class="zocial bitcoin icon">Bitcoin accepted here</a>
<a href="#" class="zocial ninetyninedesigns icon">View Portfolio on 99Designs</a>
<a href="#" class="zocial stumbleupon icon">Stumble!</a>
<a href="#" class="zocial itunes icon">Download on iTunes</a>
<a href="#" class="zocial myspace icon">Find me on Myspace</a>
<a href="#" class="zocial windows icon">Sign in with Windows Live</a>
<a href="#" class="zocial eventful icon">Find Events with Eventful</a>
<a href="#" class="zocial klout icon">Influence with Klout</a>
<a href="#" class="zocial xing icon">Sign in with Xing</a>
<a href="#" class="zocial flickr icon">Upload to Flickr</a>
<a href="#" class="zocial delicious icon">Sign in with Del.icio.us</a>
<a href="#" class="zocial googleplay icon">Download from Google Play</a>
<a href="#" class="zocial opentable icon">Reserve with OpenTable</a>
<a href="#" class="zocial digg icon">Digg this</a>
<a href="#" class="zocial reddit icon">Share on Reddit</a>
<a href="#" class="zocial angellist icon">Fund us on AngelList</a>
<a href="#" class="zocial instagram icon">Sign-in with Instagram</a>
<a href="#" class="zocial call icon">Call a phone</a>
<a href="#" class="zocial email icon">Send a message</a>
<a href="#" class="zocial cal icon">Add to calendar</a>
<a href="#" class="zocial print icon">Print this page</a>
<a href="#" class="zocial cart icon">Add to cart</a>
<p>Thanks to <a href="http://twitter.com/guillermovs" target="_blank">@guillermovs</a>, <a href="http://twitter.com/kamens" target="_blank">@kamens</a>, <a href="http://twitter.com/vizualover" target="_blank">@vizualover</a>, and <a href="http://twitter.com/leaverou" target="_blank">@leaverou</a> for code refinements and suggestions.</p>
<p>Massive thanks to <a href="http://twitter.com/drewwilson" target="_blank">@drewwilson</a> for making the multi-purpose buttons possible with his incredible <a href="http://pictos.drewwilson.com">Pictos icons</a>.</p>
<p>See code samples at <a href="http://zocial.smcllns.com">zocial.smcllns.com</a> &mdash; ask questions to <a href="http://twitter.com/smcllns">@smcllns</a></p>
<a href="https://twitter.com/share" class="twitter-share-button" data-url="http://zocial.smcllns.com" data-via="smcllns">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="http://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25061452-1']);
_gaq.push(['_setDomainName', 'smcllns.com']);
_gaq.push(['_setAllowHash', 'false']);
_gaq.push(['_trackPageview']);
_gaq.push(['_trackPageLoadTime']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>

View File

@ -0,0 +1,64 @@
openerp.auth_oauth = function(instance) {
var QWeb = instance.web.qweb;
instance.web.Login = instance.web.Login.extend({
start: function(parent, params) {
var d = this._super.apply(this, arguments);
this.$element.on('click', 'a.zocial', this.on_oauth_sign_in);
this.oauth_providers = [];
if(this.params.oauth_error === 1) {
this.do_warn("Sign up error.","Sign up is not allowed on this database.");
} else if(this.params.oauth_error === 2) {
this.do_warn("Authentication error","");
}
return d.then(this.do_oauth_load);
},
do_oauth_load: function() {
var db = this.$("form [name=db]").val();
if (db) {
this.rpc("/auth_oauth/list_providers", { dbname: db }).then(this.on_oauth_loaded);
}
},
on_oauth_loaded: function(result) {
this.oauth_providers = result;
console.log(result);
var buttons = QWeb.render("auth_oauth.Login.button",{"widget":this});
console.log(buttons);
this.$(".oe_login_pane form ul").after(buttons);
},
on_oauth_sign_in: function(ev) {
ev.preventDefault();
var index = $(ev.target).data('index');
var p = this.oauth_providers[index];
var ret = location.protocol+"//"+location.host+"/";
var dbname = self.$("form [name=db]").val();
var state_object = {
d: dbname,
p: p.id
}
var state = JSON.stringify(state_object);
var params = {
response_type: 'token',
client_id: p.client_id,
redirect_uri: ret,
scope: p.scope,
state: state,
};
var url = p.auth_endpoint + '?' + $.param(params);
window.location = url;
},
});
instance.web.WebClient = instance.web.WebClient.extend({
start: function() {
this._super.apply(this, arguments);
var params = $.deparam(window.location.hash.substring(1));
// alert(JSON.stringify(params));
if (params.hasOwnProperty('access_token')) {
var url = "/auth_oauth/signin" + '?' + $.param(params);
window.location = url;
}
},
});
};

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="auth_oauth.Login.button">
<t t-foreach="widget.oauth_providers" t-as="p">
<a href="#" t-att-class="p.css_class" t-att-data-index="p_index"><t t-esc="p.body"/></a>
<br/>
</t>
</t>
</templates>

View File

@ -28,6 +28,7 @@ import werkzeug.urls
import werkzeug.exceptions
from openerp.modules.registry import RegistryManager
from openerp.addons.web.controllers.main import login_and_redirect, set_cookie_and_redirect
try:
import openerp.addons.web.common.http as openerpweb
except ImportError:
@ -159,7 +160,7 @@ class OpenIDController(openerpweb.Controller):
def process(self, req, **kw):
session = getattr(req.session, 'openid_session', None)
if not session:
return werkzeug.utils.redirect('/')
return set_cookie_and_redirect(req, '/')
oidconsumer = consumer.Consumer(session, self._store, consumer_class=GoogleAppsAwareConsumer)
@ -168,12 +169,11 @@ class OpenIDController(openerpweb.Controller):
display_identifier = info.getDisplayIdentifier()
session['status'] = info.status
user_id = None
if info.status == consumer.SUCCESS:
dbname = session['dbname']
with utils.cursor(dbname) as cr:
registry = RegistryManager.get(dbname)
registry = RegistryManager.get(dbname)
with registry.cursor() as cr:
Modules = registry.get('ir.module.module')
installed = Modules.search_count(cr, 1, ['&', ('name', '=', 'auth_openid'), ('state', '=', 'installed')]) == 1
@ -206,10 +206,9 @@ class OpenIDController(openerpweb.Controller):
# TODO fill empty fields with the ones from sreg/ax
cr.commit()
req.session.authenticate(dbname, login, key, {})
return login_and_redirect(req, dbname, login, key)
if not user_id:
session['message'] = 'This OpenID identifier is not associated to any active users'
session['message'] = 'This OpenID identifier is not associated to any active users'
elif info.status == consumer.SETUP_NEEDED:
session['message'] = info.setup_url
@ -223,8 +222,7 @@ class OpenIDController(openerpweb.Controller):
# information in a log.
session['message'] = 'Verification failed.'
fragment = '#loginerror' if not user_id else ''
return werkzeug.utils.redirect('/' + fragment)
return set_cookie_and_redirect(req, '/#action=login&loginerror=1')
@openerpweb.jsonrequest
def status(self, req):

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -18,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.modules.registry import RegistryManager
from openerp.osv import osv, fields
import openerp.exceptions
import tools
@ -64,23 +65,22 @@ class res_users(osv.osv):
if result:
return result
else:
with utils.cursor(db) as cr:
with RegistryManager.get(db).cursor() as cr:
cr.execute("""UPDATE res_users
SET date=now() AT TIME ZONE 'UTC'
SET login_date=now() AT TIME ZONE 'UTC'
WHERE login=%s AND openid_key=%s AND active=%s RETURNING id""",
(tools.ustr(login), tools.ustr(password), True))
res = cr.fetchone()
cr.commit()
return res[0] if res else False
def check(self, db, uid, passwd):
try:
return super(res_users, self).check(db, uid, passwd)
except openerp.exceptions.AccessDenied:
if not passwd:
raise
with utils.cursor(db) as cr:
with RegistryManager.get(db).cursor() as cr:
cr.execute('''SELECT COUNT(1)
FROM res_users
WHERE id=%s

View File

@ -8,7 +8,7 @@ instance.web.Login = instance.web.Login.extend({
this._super.apply(this, arguments);
var self = this;
this._default_error_message = this.$element.find('.login_error_message').text();
this._default_error_message = this.$element.find('.oe_login_error_message').text();
this.$openid_selected_button = $();
this.$openid_selected_input = $();
@ -40,8 +40,7 @@ instance.web.Login = instance.web.Login.extend({
}
});
this._check_fragment();
this._check_error();
},
@ -67,11 +66,9 @@ instance.web.Login = instance.web.Login.extend({
},
_check_fragment: function() {
_check_error: function() {
var self = this;
var fragment = jQuery.deparam.fragment();
console.log(fragment);
if (fragment.loginerror !== undefined) {
if (this.params.loginerror !== undefined) {
this.rpc('/auth_openid/login/status', {}, function(result) {
if (_.contains(['success', 'failure'], result.status) && result.message) {
self.do_warn('Invalid OpenID Login', result.message);
@ -132,11 +129,12 @@ instance.web.Login = instance.web.Login.extend({
do_warn: function(title, msg) {
//console.warn(title, msg);
this.$element.find('.login_error_message').text(msg).show();
this.$element.find('.oe_login_error_message').text(msg).show();
this._super(title, msg);
},
reset_error_message: function() {
this.$element.find('.login_error_message').text(this._default_error_message);
this.$element.find('.oe_login_error_message').text(this._default_error_message);
}
});

View File

@ -3,7 +3,7 @@
<templates id="template" xml:space="preserve">
<t t-extend="Login">
<t t-jquery=".login .pane div#logo" t-operation="after">
<t t-jquery=".oe_login .oe_login_logo" t-operation="after">
<ul class="openid_providers">
<li><a href="#login,password" title="Password" data-url="" id="btn_password">Password</a></li>
<li><a href="#google" title="Google" data-url="https://www.google.com/accounts/o8/id">Google</a></li>
@ -13,25 +13,8 @@
</ul>
</t>
</t>
<t t-extend="Login">
<t t-jquery=".oe_box2 tr:first" t-operation="after">
<tr>
<td><label for="googleapps">Google Apps Domain:</label></td>
<td><input type="text" name="googleapps" /></td>
</tr>
<tr>
<td><label for="launchpad">Username:</label></td>
<td><input type="text" name="launchpad" /></td>
</tr>
<tr>
<td><label for="openid_url">OpenID URL:</label></td>
<td><input type="text" name="openid_url" /></td>
</tr>
</t>
</t>
<t t-extend="Login">
<t t-jquery=".login .pane form ul li:nth-child(4)" t-operation="after">
<t t-jquery=".oe_login .oe_login_pane form ul li:nth-child(4)" t-operation="after">
<li>
Google Apps Domain
</li>
@ -54,7 +37,7 @@
</t>
<t t-extend="Login">
<t t-jquery=".login .pane form ul li:has(input)">
<t t-jquery=".oe_login .oe_login_pane form ul li:has(input)">
this.each(function() {
var $i = $(this),
dp = $i.find('input').attr('name');

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -15,11 +15,9 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from contextlib import contextmanager
from openerp.modules.registry import RegistryManager
KEY_LENGTH = 16
@ -35,14 +33,4 @@ SREG2AX = { # from http://www.axschema.org/types/#sreg
'timezone': 'http://axschema.org/pref/timezone',
}
@contextmanager
def cursor(db):
cr = RegistryManager.get(db).db.cursor()
try:
yield cr
finally:
cr.close()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -31,7 +31,7 @@ openerp.auth_reset_password = function(instance) {
},
do_reset_password: function(db, email) {
var self = this;
instance.connection.session_authenticate(db, 'anonymous', 'anonymous', true).pipe(function () {
instance.session.session_authenticate(db, 'anonymous', 'anonymous', true).pipe(function () {
var func = new instance.web.Model("res.users").get_func("send_reset_password_request");
return func(email).then(function(res) {
// show message

View File

@ -1,2 +1,24 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2012-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# 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 for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
import controllers
import res_config
import auth_signup
import res_users

View File

@ -1,22 +1,40 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2009-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# 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 for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
{
'name': 'Signup',
'description': 'Allow users to sign up',
'author': 'OpenERP SA',
'version': '1.0',
'category': 'Authentication',
'website': 'http://www.openerp.com',
'installable': True,
'depends': ['auth_anonymous', 'base_setup'],
'data': [
'res_config.xml',
],
'js': [
'static/src/js/auth_signup.js',
],
'css': [
'static/src/css/auth_signup.css',
],
'qweb': [
'static/src/xml/auth_signup.xml',
],
'name': 'Signup',
'description': 'Allow users to sign up',
'author': 'OpenERP SA',
'version': '1.0',
'category': 'Authentication',
'website': 'http://www.openerp.com',
'installable': True,
'depends': ['base_setup'],
'data': [
'res_config.xml',
],
'js': [
'static/src/js/auth_signup.js',
],
'qweb': [
'static/src/xml/auth_signup.xml',
],
}

View File

@ -1,39 +0,0 @@
from openerp.osv import osv, fields
class res_users(osv.Model):
_inherit = 'res.users'
_sql_constraints = [
('email_uniq', 'UNIQUE (user_email)', 'You can not have two users with the same email!')
]
class signup_signup(osv.TransientModel):
_name = 'auth.signup'
# TODO add captcha
_columns = {
'name': fields.char('Name', size=64),
'email': fields.char('Email', size=64),
'password': fields.char('Password', size=64),
}
def create(self, cr, uid, values, context=None):
# NOTE here, invalid values raises exceptions to avoid storing
# sensitive data into the database (which then are available to anyone)
new_user = {
'name': values['name'],
'login': values['email'],
'user_email': values['email'],
'password': values['password'],
'active': True,
}
user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth.signup_template_user_id', 0)
if user_template_id:
self.pool.get('res.users').copy(cr, 1, user_template_id, new_user, context=context)
else:
self.pool.get('res.users').create(cr, 1, new_user, context=context)
# Dont store anything
return 0

View File

@ -0,0 +1,3 @@
import main
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,34 @@
import logging
import werkzeug.urls
from openerp.modules.registry import RegistryManager
from openerp.addons.web.controllers.main import login_and_redirect
import openerp.addons.web.common.http as openerpweb
_logger = logging.getLogger(__name__)
class OpenIDController(openerpweb.Controller):
_cp_path = '/auth_signup'
@openerpweb.httprequest
def signup(self, req, dbname, name, login, password):
url = '/'
registry = RegistryManager.get(dbname)
with registry.cursor() as cr:
try:
Users = registry.get('res.users')
credentials = Users.auth_signup(cr, 1, name, login, password)
cr.commit()
return login_and_redirect(req, *credentials)
except AttributeError:
# auth_signup is not installed
_logger.exception('attribute error when signup')
url = "/#action=auth_signup&error=NA" # Not Available
except Exception:
# signup error
_logger.exception('error when signup')
url = "/#action=auth_signup&error=UE" # Unexcpected Error
return werkzeug.utils.redirect(url)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,3 +1,24 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2012-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# 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 for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
from openerp.osv import osv, fields
class base_config_settings(osv.TransientModel):

View File

@ -0,0 +1,46 @@
import openerp
from openerp.osv import osv
class res_users(osv.Model):
_inherit = 'res.users'
def auth_signup_create(self, cr, uid, new_user, context=None):
# new_user:
# login
# email
# name (optional)
# partner_id (optional)
# groups (optional)
# sign (for partner_id and groups)
#
user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth.signup_template_user_id', 0)
if user_template_id:
self.pool.get('res.users').copy(cr, 1, user_template_id, new_user, context=context)
else:
self.pool.get('res.users').create(cr, 1, new_user, context=context)
def auth_signup(self, cr, uid, name, login, password, context=None):
r = (cr.dbname, login, password)
res = self.search(cr, uid, [("login", "=", login)])
if res:
# Existing user
user_id = res[0]
try:
self.check(cr.dbname, user_id, password)
# Same password
except openerp.exceptions.AccessDenied:
# Different password
raise
else:
# New user
new_user = {
'name': name,
'login': login,
'user_email': login,
'password': password,
'active': True,
}
self.auth_signup_create(cr, uid, new_user)
return r
#

View File

@ -1,9 +0,0 @@
.openerp .oe_login .oe_login_pane ul.oe_signup a {
color: #eeeeee;
margin: 0 8px;
}
.openerp .oe_login .oe_login_pane ul.oe_signup a:hover {
text-decoration: underline;
}

View File

@ -1,65 +1,38 @@
openerp.auth_signup = function(instance) {
instance.auth_signup = instance.auth_signup || {};
var _t = instance.web._t;
instance.web.Login.include({
start: function() {
var self = this;
this.$('a.oe_signup').click(function() {
var db = self.$("form [name=db]").val();
if (!db) {
self.do_warn(_t("Login"), _t("No database selected!"));
return false;
}
var cnx = instance.connection;
if (cnx.session_is_valid()) {
self._signup();
} else {
cnx.session_authenticate(db, 'anonymous', 'anonymous', true).then(function() {
self._signup();
}).fail(function(error, event) {
console.log(error);
// cannot log as anonymous or auth_signup not installed
self.do_warn(_t('Sign Up'), _.str.sprintf(_t('Signup functionnality is not available for database %s'), db), true);
});
}
var dbname = self.$("form [name=db]").val();
self.do_action({
type: 'ir.actions.client',
tag: 'auth_signup.signup',
params: {'dbname': dbname},
target: 'new',
name: 'Sign up'
});
return true;
});
return this._super();
},
_signup: function() {
this.do_action({
type: 'ir.actions.client',
tag: 'auth_signup.signup',
target: 'new',
name: 'Sign up'
});
}
});
instance.auth_signup = instance.auth_signup || {};
instance.auth_signup.Signup = instance.web.Widget.extend({
template: 'auth_signup.signup',
init: function() {
this._super.apply(this, arguments);
this.dataset = new instance.web.DataSet(this, 'auth.signup');
init: function(parent, params) {
this.params = params;
return this._super();
},
start: function() {
var self = this;
this.$('input[type=password]').change(function() {
this.$('input[name=password_confirmation]').keyup(function() {
var v = $(this).val();
var e = !_.isEmpty(v);
if (e) {
e =_.all(self.$('input[type=password]'), function(i) {
return $(i).val() === v;
});
}
var $b = self.$('button');
if (e) {
if (_.isEmpty(v) || self.$('input[name=password]').val() === v) {
$b.removeAttr('disabled');
} else {
$b.attr('disabled', 'disabled');
@ -70,36 +43,18 @@ openerp.auth_signup = function(instance) {
if(ev) {
ev.preventDefault();
}
var name = self.$('input[name=name]').val();
var email = self.$('input[name=email]').val();
var password = self.$('input[name=password]').val();
self.dataset.create({
name: name,
email: email,
password: password
}, function() {
self.do_action({
type: 'ir.actions.client',
tag: 'login',
params: {
db: instance.connection.db,
login: email,
password: password,
login_successful: function() {
self.do_action('home');
}
}
});
});
return false;
var params = {
dbname : self.params.dbname,
name: self.$('input[name=name]').val(),
login: self.$('input[name=email]').val(),
password: self.$('input[name=password]').val(),
};
var url = "/auth_signup/signup?" + $.param(params);
window.location = url;
});
return $.when(this._super());
return this._super();
}
});
instance.web.client_actions.add("auth_signup.signup", "instance.auth_signup.Signup");
};

View File

@ -14,10 +14,10 @@
<t t-name="auth_signup.signup">
<div>
<form>
Name = <input type="text" name="name"/>
Email = <input type="email" name="email"/>
Password = <input type="password" name="password"/>
Confirmation = <input type="password" name="password_confirmation"/>
Name = <input type="text" name="name"/><br/>
Email = <input type="email" name="email"/><br/>
Password = <input type="password" name="password"/><br/>
Confirmation = <input type="password" name="password_confirmation"/><br/>
<button type="submit" disabled="disabled">Signup</button>
</form>
</div>

View File

@ -292,7 +292,7 @@ the rule to mark CC(mail to any other person defined in actions)."),
'object_description': hasattr(obj, 'description') and obj.description or False,
'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/',
'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and \
obj.user_id.user_email) or '/',
obj.user_id.email) or '/',
'object_user_phone': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and \
obj.partner_address_id.phone) or '/',
'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/',
@ -319,8 +319,8 @@ the rule to mark CC(mail to any other person defined in actions)."),
mail_message = self.pool.get('mail.message')
body = self.format_mail(obj, body)
if not emailfrom:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.user_email:
emailfrom = obj.user_id.user_email
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email:
emailfrom = obj.user_id.email
name = '[%d] %s' % (obj.id, tools.ustr(obj.name))
emailfrom = tools.ustr(emailfrom)
@ -419,7 +419,7 @@ the rule to mark CC(mail to any other person defined in actions)."),
emails = []
if hasattr(obj, 'user_id') and action.act_mail_to_user:
if obj.user_id:
emails.append(obj.user_id.user_email)
emails.append(obj.user_id.email)
if action.act_mail_to_watchers:
emails += (action.act_email_cc or '').split(',')

View File

@ -245,7 +245,7 @@ class calendar_attendee(osv.osv):
continue
else:
result[id][name] = self._get_address(attdata.sent_by_uid.name, \
attdata.sent_by_uid.user_email)
attdata.sent_by_uid.email)
if name == 'cn':
if attdata.user_id:
@ -289,7 +289,7 @@ class calendar_attendee(osv.osv):
if name == 'language':
user_obj = self.pool.get('res.users')
lang = user_obj.read(cr, uid, uid, ['context_lang'], context=context)['context_lang']
lang = user_obj.read(cr, uid, uid, ['lang'], context=context)['lang']
result[id][name] = lang.replace('_', '-')
return result
@ -434,7 +434,7 @@ property or property parameter."),
if not organizer:
organizer = event_obj.user_id
event_org.params['CN'] = [organizer.name]
event_org.value = 'MAILTO:' + (organizer.user_email or organizer.name)
event_org.value = 'MAILTO:' + (organizer.email or organizer.name)
if event_obj.alarm_id:
# computes alarm data
@ -535,7 +535,7 @@ property or property parameter."),
return {'value': {'email': ''}}
usr_obj = self.pool.get('res.users')
user = usr_obj.browse(cr, uid, user_id, *args)
return {'value': {'email': user.user_email, 'availability':user.availability}}
return {'value': {'email': user.email, 'availability':user.availability}}
def do_tentative(self, cr, uid, ids, context=None, *args):
""" Makes event invitation as Tentative
@ -891,9 +891,9 @@ From:
""" % (alarm.name, alarm.trigger_date, alarm.description, \
alarm.user_id.name, alarm.user_id.signature)
mail_to = [alarm.user_id.user_email]
mail_to = [alarm.user_id.email]
for att in alarm.attendee_ids:
mail_to.append(att.user_id.user_email)
mail_to.append(att.user_id.email)
if mail_to:
mail_message.schedule_with_attach(cr, uid,
tools.config.get('email_from', False),
@ -947,7 +947,7 @@ class calendar_event(osv.osv):
value['duration'] = duration
# change start_date's time to 00:00:00 in the user's timezone
user = self.pool.get('res.users').browse(cr, uid, uid)
tz = pytz.timezone(user.context_tz) if user.context_tz else pytz.utc
tz = pytz.timezone(user.tz) if user.tz else pytz.utc
start = pytz.utc.localize(start).astimezone(tz) # convert start in user's timezone
start = start.replace(hour=0, minute=0, second=0) # change start's time to 00:00:00
start = start.astimezone(pytz.utc) # convert start back to utc
@ -1097,8 +1097,8 @@ rule or repeating pattern of time to exclude from the recurring rule."),
user_pool = self.pool.get('res.users')
user = user_pool.browse(cr, uid, uid, context=context)
res = user.name
if user.user_email:
res += " <%s>" %(user.user_email)
if user.email:
res += " <%s>" %(user.email)
return res
_defaults = {

View File

@ -233,6 +233,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -99,12 +99,12 @@ send an Email to Invited Person')
user = user_obj.browse(cr, uid, user_id)
res = {
'user_id': user_id,
'email': user.user_email
'email': user.email
}
res.update(ref)
vals.append(res)
if user.user_email:
mail_to.append(user.user_email)
if user.email:
mail_to.append(user.email)
elif type == 'external' and datas.get('email'):
res = {'email': datas['email']}
@ -143,7 +143,7 @@ send an Email to Invited Person')
self._columns['type'].selection))
raise osv.except_osv(_('Error!'), _("%s must have an email address to send mail.") %(name[0]))
att_obj._send_mail(cr, uid, attendees, mail_to, \
email_from = current_user.user_email or tools.config.get('email_from', False))
email_from = current_user.email or tools.config.get('email_from', False))
return {'type': 'ir.actions.act_window_close'}

View File

@ -213,7 +213,7 @@ class users(osv.osv):
# Check if the encrypted password matches against the one in the db.
cr.execute("""UPDATE res_users
SET date=now() AT TIME ZONE 'UTC'
SET login_date=now() AT TIME ZONE 'UTC'
WHERE id=%s AND password=%s AND active
RETURNING id""",
(int(id), encrypted_pw.encode('utf-8')))

View File

@ -50,7 +50,7 @@ class specify_partner_terminology(osv.osv_memory):
def make_translations(self, cr, uid, ids, name, type, src, value, res_id=0, context=None):
trans_obj = self.pool.get('ir.translation')
user_obj = self.pool.get('res.users')
context_lang = user_obj.browse(cr, uid, uid, context=context).context_lang
context_lang = user_obj.browse(cr, uid, uid, context=context).lang
existing_trans_ids = trans_obj.search(cr, uid, [('name','=',name), ('lang','=',context_lang), ('type','=',type), ('src','=',src), ('res_id','=',res_id)])
if existing_trans_ids:
trans_obj.write(cr, uid, existing_trans_ids, {'value': value}, context=context)

View File

@ -57,7 +57,7 @@ class base_stage(object):
if not context or not context.get('portal'):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.user_email
return user.email
def _get_default_user(self, cr, uid, context=None):
""" Gives current user id
@ -301,15 +301,15 @@ class base_stage(object):
for case in self.browse(cr, uid, ids, context=context):
if not destination and not case.email_from:
return False
if not case.user_id.user_email:
if not case.user_id.email:
return False
if destination and case.section_id.user_id:
case_email = case.section_id.user_id.user_email
case_email = case.section_id.user_id.email
else:
case_email = case.user_id.user_email
case_email = case.user_id.email
src = case_email
dest = case.user_id.user_email or ""
dest = case.user_id.email or ""
body = case.description or ""
for message in case.message_ids:
if message.email_from and message.body_text:
@ -366,8 +366,8 @@ class base_stage(object):
l=[]
if case.email_cc:
l.append(case.email_cc)
if case.user_id and case.user_id.user_email:
l.append(case.user_id.user_email)
if case.user_id and case.user_id.email:
l.append(case.user_id.email)
res[case.id] = l
return res

View File

@ -55,7 +55,7 @@ class base_state(object):
if not context or not context.get('portal'):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.user_email
return user.email
def _get_default_user(self, cr, uid, context=None):
""" Gives current user id

View File

@ -49,8 +49,8 @@ class base_action_rule(osv.osv):
mail_message = self.pool.get('mail.message')
body = self.format_mail(obj, body)
if not emailfrom:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.user_email:
emailfrom = obj.user_id.user_email
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email:
emailfrom = obj.user_id.email
name = '[%d] %s' % (obj.id, tools.ustr(obj.name))
emailfrom = tools.ustr(emailfrom)

View File

@ -243,7 +243,7 @@ class crm_lead(base_stage, osv.osv):
'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True),
'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True),
'company_currency': fields.related('company_id', 'currency_id', 'symbol', type='char', string='Company Currency', readonly=True),
'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True),
# Fields for address, due to separation from crm and res.partner
@ -692,11 +692,11 @@ class crm_lead(base_stage, osv.osv):
"""
#TOFIX: mail template should be used here instead of fix subject, body text.
message = self.pool.get('mail.message')
email_to = lead.user_id and lead.user_id.user_email
email_to = lead.user_id and lead.user_id.email
if not email_to:
return False
email_from = lead.section_id and lead.section_id.user_id and lead.section_id.user_id.user_email or email_to
email_from = lead.section_id and lead.section_id.user_id and lead.section_id.user_id.email or email_to
partner = lead.partner_id and lead.partner_id.name or lead.partner_name
subject = "lead %s converted into opportunity" % lead.name
body = "Info \n Id : %s \n Subject: %s \n Partner: %s \n Description : %s " % (lead.id, lead.name, lead.partner_id.name, lead.description)
@ -859,13 +859,10 @@ class crm_lead(base_stage, osv.osv):
# OpenChatter methods and notifications
# ----------------------------------------
def message_get_subscribers(self, cr, uid, ids, context=None):
""" Override to add the salesman. """
user_ids = super(crm_lead, self).message_get_subscribers(cr, uid, ids, context=context)
for obj in self.browse(cr, uid, ids, context=context):
if obj.user_id and not obj.user_id.id in user_ids:
user_ids.append(obj.user_id.id)
return user_ids
def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
""" Add 'user_id' to the monitored fields """
res = super(crm_lead, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
return res + ['user_id']
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """

View File

@ -222,6 +222,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@ -532,6 +533,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -271,7 +271,6 @@ class crm_phonecall(base_state, osv.osv):
def case_open_send_note(self, cr, uid, ids, context=None):
lead_obj = self.pool.get('crm.lead')
for phonecall in self.browse(cr, uid, ids, context=context):
phonecall.message_subscribe([phonecall.user_id.id], context=context)
if phonecall.opportunity_id:
lead = phonecall.opportunity_id
# convert datetime field to a datetime, using server format, then

View File

@ -154,6 +154,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -182,6 +182,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -100,6 +100,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -43,16 +43,13 @@ class crm_lead_forward_to_partner(osv.osv_memory):
_defaults = {
'send_to' : 'email',
'history': 'latest',
'email_from': lambda self, cr, uid, *a: self.pool.get('res.users')._get_email_from(cr, uid, uid)[uid],
'email_from': lambda s, cr, uid, c: s.pool.get('res.users').browse(cr, uid, uid, c).email,
}
def on_change_email(self, cr, uid, ids, user):
def on_change_email(self, cr, uid, ids, user, context=None):
if not user:
return {'value': {'email_to': False}}
email = self.pool.get('res.users')._get_email_from(cr, uid, [user])[user]
return {'value': {'email_to': email}}
return {'value': {'email_to': self.pool.get('res.users').browse(cr, uid, uid, context=context).email}}
def on_change_history(self, cr, uid, ids, history_type, context=None):
"""Gives message body according to type of history selected
@ -80,7 +77,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
partner = partner_obj.browse(cr, uid, [partner_id])
user_id = partner and partner[0].user_id or False
data.update({'email_from': partner and partner[0].email or "",
'email_cc' : user_id and user_id.user_email or '',
'email_cc' : user_id and user_id.user or '',
'user_id': user_id and user_id.id or False})
return {'value' : data}
@ -185,7 +182,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
if partner_assigned_id:
assigned_partner = partner.browse(cr, uid, partner_assigned_id, context=context)
user_id = assigned_partner.user_id and assigned_partner.user_id.id or False
email_cc = assigned_partner.user_id and assigned_partner.user_id.user_email or ''
email_cc = assigned_partner.user_id and assigned_partner.user_id.email or ''
email = assigned_partner.email
res.update({

View File

@ -22,6 +22,7 @@
import time
from osv import fields,osv
from tools.translate import _
import decimal_precision as dp
class delivery_carrier(osv.osv):
_name = "delivery.carrier"
@ -237,8 +238,8 @@ class delivery_grid_line(osv.osv):
'max_value': fields.float('Maximum Value', required=True),
'price_type': fields.selection([('fixed','Fixed'),('variable','Variable')], 'Price Type', required=True),
'variable_factor': fields.selection([('weight','Weight'),('volume','Volume'),('wv','Weight * Volume'), ('price','Price')], 'Variable Factor', required=True),
'list_price': fields.float('Sale Price', required=True),
'standard_price': fields.float('Cost Price', required=True),
'list_price': fields.float('Sale Price', digits_compute= dp.get_precision('Product Price'), required=True),
'standard_price': fields.float('Cost Price', digits_compute= dp.get_precision('Product Price'), required=True),
}
_defaults = {
'type': lambda *args: 'weight',

View File

@ -6,7 +6,7 @@ openerp.edi.EdiView = openerp.web.Widget.extend({
this._super();
this.db = db;
this.token = token;
this.session = openerp.connection;
this.session = openerp.session;
this.template = "EdiEmpty";
this.content = "";
this.sidebar = "";
@ -108,7 +108,7 @@ openerp.edi.EdiView = openerp.web.Widget.extend({
});
openerp.edi.edi_view = function (db, token) {
openerp.connection.session_bind().then(function () {
openerp.session.session_bind().then(function () {
new openerp.edi.EdiView(null,db,token).appendTo($("body").addClass('openerp'));
});
}
@ -188,7 +188,7 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
});
openerp.edi.edi_import = function (url) {
openerp.connection.session_bind().then(function () {
openerp.session.session_bind().then(function () {
new openerp.edi.EdiImport(null,url).appendTo($("body").addClass('openerp'));
});
}

View File

@ -4,7 +4,7 @@
<record id="confirmation_event" model="email.template">
<field name="name">Confirmation of the Event</field>
<field name="model_id" ref="event.model_event_registration"/>
<field name="email_from" >${object.user_id.user_email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field>
<field name="email_from" >${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field>
<field name="email_to" >${object.email}</field>
<field name="subject">Your registration at ${object.event_id.name}</field>
<field name="body_text">
@ -24,7 +24,7 @@
<record id="confirmation_registration" model="email.template">
<field name="name">Confirmation of the Registration</field>
<field name="model_id" ref="event.model_event_registration"/>
<field name="email_from" >${object.user_id.user_email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field>
<field name="email_from" >${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field>
<field name="email_to" >${object.email}</field>
<field name="subject">Your registration at ${object.event_id.name}</field>
<field name="body_text">

View File

@ -230,7 +230,7 @@ class event_event(osv.osv):
curr_reg_ids = register_pool.search(cr, uid, [('user_id', '=', user.id), ('event_id', '=' , ids[0])])
#the subscription is done with SUPERUSER_ID because in case we share the kanban view, we want anyone to be able to subscribe
if not curr_reg_ids:
curr_reg_ids = [register_pool.create(cr, SUPERUSER_ID, {'event_id': ids[0] ,'email': user.user_email, 'name':user.name, 'user_id': user.id, 'nb_register': num_of_seats})]
curr_reg_ids = [register_pool.create(cr, SUPERUSER_ID, {'event_id': ids[0] ,'email': user.email, 'name':user.name, 'user_id': user.id, 'nb_register': num_of_seats})]
else:
register_pool.write(cr, uid, curr_reg_ids, {'nb_register': num_of_seats}, context=context)
return register_pool.confirm_registration(cr, SUPERUSER_ID, curr_reg_ids, context=context)

View File

@ -196,6 +196,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@ -477,6 +478,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -252,7 +252,7 @@ class hr_employee(osv.osv):
def onchange_user(self, cr, uid, ids, user_id, context=None):
work_email = False
if user_id:
work_email = self.pool.get('res.users').browse(cr, uid, user_id, context=context).user_email
work_email = self.pool.get('res.users').browse(cr, uid, user_id, context=context).email
return {'value': {'work_email' : work_email}}
def _get_default_image(self, cr, uid, context=None):

View File

@ -72,7 +72,7 @@ class hr_expense_expense(osv.osv):
'account_move_id': fields.many2one('account.move', 'Ledger Posting'),
'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
'note': fields.text('Note'),
'amount': fields.function(_amount, string='Total Amount'),
'amount': fields.function(_amount, string='Total Amount', digits_compute= dp.get_precision('Account')),
'invoice_id': fields.many2one('account.invoice', "Employee's Invoice"),
'currency_id': fields.many2one('res.currency', 'Currency', required=True),
'department_id':fields.many2one('hr.department','Department'),
@ -258,8 +258,8 @@ class hr_expense_line(osv.osv):
'date_value': fields.date('Date', required=True),
'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True),
'total_amount': fields.function(_amount, string='Total', digits_compute=dp.get_precision('Account')),
'unit_amount': fields.float('Unit Price', digits_compute=dp.get_precision('Account')),
'unit_quantity': fields.float('Quantities' ),
'unit_amount': fields.float('Unit Price', digits_compute=dp.get_precision('Product Price')),
'unit_quantity': fields.float('Quantities', digits_compute= dp.get_precision('Product Unit of Measure')),
'product_id': fields.many2one('product.product', 'Product', domain=[('hr_expense_ok','=',True)]),
'uom_id': fields.many2one('product.uom', 'Unit of Measure'),
'description': fields.text('Description'),

View File

@ -120,6 +120,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -366,15 +366,11 @@ class hr_holidays(osv.osv):
result[obj.id] = hr_manager_group['users']
return result
def message_get_subscribers(self, cr, uid, ids, context=None):
""" Override to add employee and its manager. """
user_ids = super(hr_holidays, self).message_get_subscribers(cr, uid, ids, context=context)
for obj in self.browse(cr, uid, ids, context=context):
if obj.user_id and not obj.user_id.id in user_ids:
user_ids.append(obj.user_id.id)
if obj.employee_id.parent_id and not obj.employee_id.parent_id.user_id.id in user_ids:
user_ids.append(obj.employee_id.parent_id.user_id.id)
return user_ids
def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
""" Add 'user_id' and 'manager' to the monitored fields """
res = super(hr_holidays, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
# TODO: add manager
return res + ['user_id']
def create_notificate(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):

View File

@ -137,6 +137,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>
@ -183,6 +184,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -228,7 +228,7 @@ class hr_applicant(base_stage, osv.Model):
multi='day_close', type="float", store=True),
'color': fields.integer('Color Index'),
'emp_id': fields.many2one('hr.employee', 'employee'),
'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
}
_defaults = {
@ -461,13 +461,10 @@ class hr_applicant(base_stage, osv.Model):
# OpenChatter methods and notifications
# -------------------------------------------------------
def message_get_subscribers(self, cr, uid, ids, context=None):
""" Override to add responsible user. """
user_ids = super(hr_applicant, self).message_get_subscribers(cr, uid, ids, context=context)
for obj in self.browse(cr, uid, ids, context=context):
if obj.user_id and not obj.user_id.id in user_ids:
user_ids.append(obj.user_id.id)
return user_ids
def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
""" Add 'user_id' to the monitored fields """
res = super(hr_applicant, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
return res + ['user_id']
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """

View File

@ -181,6 +181,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -72,7 +72,7 @@ class report_account_analytic_line_to_invoice(osv.osv):
'account_id':fields.many2one('account.analytic.account', 'Analytic account', readonly=True),
'product_uom_id':fields.many2one('product.uom', 'Unit of Measure', readonly=True),
'unit_amount': fields.float('Units', readonly=True),
'sale_price': fields.float('Sale price', readonly=True, digits_compute=dp.get_precision('Sale Price')),
'sale_price': fields.float('Sale price', readonly=True, digits_compute=dp.get_precision('Product Price')),
'amount': fields.float('Amount', readonly=True, digits_compute=dp.get_precision('Account')),
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'),
('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True),

View File

@ -78,6 +78,7 @@
</sheet>
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</form>
</field>

View File

@ -877,7 +877,7 @@ class sugar_import(import_framework):
else:
val['password'] = 'sugarcrm' #default password for all user #TODO needed in documentation
val['context_lang'] = self.context.get('lang','en_US')
val['lang'] = self.context.get('lang','en_US')
return val
def get_users_department(self, val):
@ -895,10 +895,10 @@ class sugar_import(import_framework):
'map' : {
'name': concat('first_name', 'last_name'),
'login': value('user_name', fallback='last_name'),
'context_lang' : 'context_lang',
'lang' : 'context_lang',
'password' : 'password',
'.id' : '.id',
'user_email' : 'email1',
'email' : 'email1',
}
}
@ -964,7 +964,7 @@ class import_sugarcrm(osv.osv):
}
def _get_email_id(self, cr, uid, context=None):
return self.pool.get('res.users').browse(cr, uid, uid, context=context).user_email
return self.pool.get('res.users').browse(cr, uid, uid, context=context).email
def _module_installed(self, cr, uid, model, context=None):
module_id = self.pool.get('ir.module.module').search(cr, uid, [('name', '=', model), ('state', "=", "installed")], context=context)

View File

@ -103,7 +103,7 @@
<td>
<para style="terp_default_8">[[ user.name ]] </para>
<para style="terp_default_8">[[ user.login ]]</para>
<para style="terp_default_8">[[ user.user_email ]]</para>
<para style="terp_default_8">[[ user.email ]]</para>
</td>
</tr>
</blockTable>

View File

@ -21,12 +21,12 @@
import mail_alias
import mail_message
import mail_followers
import mail_thread
import mail_group
import mail_subscription
import ir_needaction
import res_users
import res_partner
import res_users
import report
import wizard
import res_config

View File

@ -64,7 +64,7 @@ The main features of the module are:
'wizard/mail_compose_message_view.xml',
'res_config_view.xml',
'mail_message_view.xml',
'mail_subscription_view.xml',
'mail_followers_view.xml',
'mail_thread_view.xml',
'mail_group_view.xml',
'res_partner_view.xml',
@ -102,9 +102,11 @@ The main features of the module are:
'js': [
'static/lib/jquery.expander/jquery.expander.js',
'static/src/js/mail.js',
'static/src/js/mail_followers.js',
],
'qweb': [
'static/src/xml/mail.xml',
'static/src/xml/mail_followers.xml',
],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,7 +19,7 @@
<value>Welcome to OpenERP!</value>
<value>Your homepage is a summary of messages you received and key information about documents you follow.
The top menu bar contains all applications you installed. You can use this &lt;i&gt;Settings&lt;/i&gt; menu to intall more applications, activate others features or give access to new users.
The top menu bar contains all applications you installed. You can use this &lt;i&gt;Settings&lt;/i&gt; menu to install more applications, activate others features or give access to new users.
To setup your preferences (name, email signature, avatar), click on the top right corner.</value>
</function>

View File

@ -22,18 +22,21 @@
from osv import osv
from osv import fields
class mail_subscription(osv.osv):
"""
mail_subscription holds the data related to the follow mechanism inside OpenERP.
class mail_followers(osv.Model):
""" mail_followers holds the data related to the follow mechanism inside
OpenERP. Users can choose to follow documents (records) of any kind that
inherits from mail.thread. Following documents allow to receive
notifications for new messages.
A subscription is characterized by:
:param: res_model: model of the followed objects
:param: res_id: ID of resource (may be 0 for every objects)
:param: user_id: user_id of the follower
"""
_name = 'mail.subscription'
_name = 'mail.followers'
_rec_name = 'id'
_log_access = False
_order = 'res_model asc'
_description = 'Mail subscription'
_description = 'Mail Document Followers'
_columns = {
'res_model': fields.char('Related Document Model', size=128,
required=True, select=1,
@ -44,10 +47,8 @@ class mail_subscription(osv.osv):
ondelete='cascade', required=True, select=1),
}
class mail_notification(osv.osv):
"""
mail_notification is a relational table modeling messages pushed to users.
:param: read: not used currently
class mail_notification(osv.Model):
""" mail_notification is a relational table modeling messages pushed to users.
"""
_name = 'mail.notification'
_rec_name = 'id'
@ -59,9 +60,4 @@ class mail_notification(osv.osv):
ondelete='cascade', required=True, select=1),
'message_id': fields.many2one('mail.message', string='Message',
ondelete='cascade', required=True, select=1),
'read': fields.boolean('Read', help="Not used currently",),
# TODO: add a timestamp ? or use message date ?
}
_defaults = {
'read': False,
}

View File

@ -2,16 +2,14 @@
<openerp>
<data>
<!--
SUBSCRIPTION
!-->
<record model="ir.ui.view" id="view_subscription_tree">
<field name="name">mail.subscription.tree</field>
<field name="model">mail.subscription</field>
<!-- FOLLOWERS !-->
<record model="ir.ui.view" id="view_followers_tree">
<field name="name">mail.followers.tree</field>
<field name="model">mail.followers</field>
<field name="type">tree</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<tree string="Subscription">
<tree string="Followers">
<field name="res_model"/>
<field name="res_id"/>
<field name="user_id"/>
@ -19,26 +17,22 @@
</field>
</record>
<!--
NOTIFICATION
!-->
<!-- NOTIFICATION !-->
<record model="ir.ui.view" id="view_notification_tree">
<field name="name">mail.notification.tree</field>
<field name="model">mail.notification</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<tree string="Subscription">
<tree string="Notifications">
<field name="user_id"/>
<field name="message_id"/>
<field name="read"/>
</tree>
</field>
</record>
<record id="action_view_subscriptions" model="ir.actions.act_window">
<field name="name">Subscriptions</field>
<field name="res_model">mail.subscription</field>
<record id="action_view_followers" model="ir.actions.act_window">
<field name="name">Followers</field>
<field name="res_model">mail.followers</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
@ -50,9 +44,9 @@
<field name="view_mode">tree,form</field>
</record>
<!-- Add subscriptions related menu entries in Settings/Email -->
<menuitem name="Subscriptions" id="menu_email_subscriptions" parent="base.menu_email"
action="action_view_subscriptions" sequence="30" groups="base.group_no_one"/> -->
<!-- Add followers related menu entries in Settings/Email -->
<menuitem name="Followers" id="menu_email_followers" parent="base.menu_email"
action="action_view_followers" sequence="30" groups="base.group_no_one"/> -->
<!-- Add notifications related menu entry in Settings/Email -->
<menuitem name="Notifications" id="menu_email_notifications" parent="base.menu_email"

View File

@ -27,18 +27,12 @@ from osv import osv
from osv import fields
from tools.translate import _
class mail_group(osv.osv):
class mail_group(osv.Model):
"""
A mail_group is a collection of users sharing messages in a discussion
group. Group users are users that follow the mail group, using the
subscription/follow mechanism of OpenSocial. A mail group has nothing
in common with res.users.group.
Additional information on fields:
- ``member_ids``: user member of the groups are calculated with
``message_get_subscribers`` method from mail.thread
- ``member_count``: calculated with member_ids
- ``is_subscriber``: calculated with member_ids
"""
_description = 'Discussion group'
@ -55,26 +49,7 @@ class mail_group(osv.osv):
def _set_image(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
def get_member_ids(self, cr, uid, ids, field_names, args, context=None):
if context is None:
context = {}
result = dict.fromkeys(ids)
for id in ids:
result[id] = {}
result[id]['member_ids'] = self.message_get_subscribers(cr, uid, [id], context=context)
result[id]['member_count'] = len(result[id]['member_ids'])
result[id]['is_subscriber'] = uid in result[id]['member_ids']
return result
def search_member_ids(self, cr, uid, obj, name, args, context=None):
if context is None:
context = {}
sub_obj = self.pool.get('mail.subscription')
sub_ids = sub_obj.search(cr, uid, ['&', ('res_model', '=', obj._name), ('user_id', '=', args[0][2])], context=context)
subs = sub_obj.read(cr, uid, sub_ids, context=context)
return [('id', 'in', map(itemgetter('res_id'), subs))]
def get_last_month_msg_nbr(self, cr, uid, ids, name, args, context=None):
def _get_last_month_msg_nbr(self, cr, uid, ids, name, args, context=None):
result = {}
for id in ids:
lower_date = (DT.datetime.now() - DT.timedelta(days=30)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
@ -86,21 +61,21 @@ class mail_group(osv.osv):
return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
_columns = {
#'name': fields.char('Group Name', size=64, required=True),
'description': fields.text('Description'),
'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"),
'responsible_id': fields.many2one('res.users', string='Responsible',
ondelete='set null', required=True, select=1,
help="Responsible of the group that has all rights on the record."),
'public': fields.selection([('public','Public'),('private','Private'),('groups','Selected Group Only')], 'Privacy', required=True,
help='This group is visible by non members. \
Invisible groups can add members through the invite button.'),
'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')],
string='Privacy', required=True,
help='This group is visible by non members. '\
'Invisible groups can add members through the invite button.'),
'group_public_id': fields.many2one('res.groups', string='Authorized Group'),
'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel',
id1='mail_group_id', id2='groups_id', string='Auto Subscription',
help="Members of those groups will automatically added as followers. "\
"Note that they will be able to manage their subscription manually "\
"if necessary."),
"Note that they will be able to manage their subscription manually "\
"if necessary."),
'image': fields.binary("Photo",
help="This field holds the image used as photo for the "\
"user. The image is base64 encoded, and PIL-supported. "\
@ -121,15 +96,7 @@ class mail_group(osv.osv):
help="Small-sized photo of the group. It is automatically "\
"resized as a 50x50px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
'member_ids': fields.function(get_member_ids, fnct_search=search_member_ids,
type='many2many', relation='res.users', string='Group members', multi='get_member_ids',
deprecated='This field will be deleted in a few hours or days, so please do not use it.'),
'member_count': fields.function(get_member_ids, type='integer',
string='Member count', multi='get_member_ids',
deprecated='This field will be deleted in a few hours or days, so please do not use it.'),
'is_subscriber': fields.function(get_member_ids, type='boolean',
string='Joined', multi='get_member_ids'),
'last_month_msg_nbr': fields.function(get_last_month_msg_nbr, type='integer',
'last_month_msg_nbr': fields.function(_get_last_month_msg_nbr, type='integer',
string='Messages count for last month'),
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade",
help="The email address associated with this group. New emails received will automatically "
@ -218,3 +185,12 @@ class mail_group(osv.osv):
def action_group_leave(self, cr, uid, ids, context=None):
return self.message_unsubscribe(cr, uid, ids, context=context)
# ----------------------------------------
# OpenChatter methods and notifications
# ----------------------------------------
def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
""" Add 'responsible_id' to the monitored fields """
res = super(mail_group, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
return res + ['responsible_id']

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>)
# Copyright (C) 2012-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -24,21 +24,29 @@ from osv import fields
from tools.translate import _
class ir_ui_menu(osv.osv):
""" Override of ir.ui.menu class. When adding mail_thread module, each
new mail.group will create a menu entry. This overrides checks that
the current user is in the mail.group followers. If not, the menu
entry is taken off the list of menu ids. This way the user will see
menu entries for the mail.group he is following.
"""
_inherit = 'ir.ui.menu'
_columns = {
'mail_group_id': fields.many2one('mail.group', 'Mail Group')
}
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0, limit=None, order=order, context=context, count=False)
subs = self.pool.get('mail.subscription')
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
""" Override to take off menu entries (mail.group) the user is not
following. """
ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0, limit=None, order=order, context=context, count=False)
follower_obj = self.pool.get('mail.followers')
for menu in self.browse(cr, uid, ids, context=context):
if menu.mail_group_id:
sub_ids = subs.search(cr, uid, [
('user_id','=',uid),('res_model','=','mail.group'),
('res_id','=',menu.mail_group_id.id)
sub_ids = follower_obj.search(cr, uid, [
('user_id', '=', uid), ('res_model', '=', 'mail.group'),
('res_id', '=', menu.mail_group_id.id)
], context=context)
if not sub_ids:
ids.remove(menu.id)
return ids

View File

@ -14,6 +14,8 @@
<field name="priority" eval="10"/>
<field name="arch" type="xml">
<kanban>
<field name="message_follower_ids"/>
<field name="message_is_follower"/>
<templates>
<t t-name="kanban-description">
<div class="oe_group_description" t-if="record.description.raw_value">
@ -21,17 +23,19 @@
</div>
</t>
<t t-name="kanban-box">
<div t-attf-class="{record.is_subscriber.raw_value} oe_group_vignette">
<div t-attf-class="{record.message_is_follower.raw_value} oe_group_vignette">
<div class="oe_group_image">
<a type="edit"><img t-att-src="kanban_image('mail.group', 'image_medium', record.id.value)" class="oe_group_photo" tooltip="kanban-description"/></a>
</div>
<div class="oe_group_details">
<h4><a type="edit"><field name="name"/></a></h4>
<span style="display: none;"><field name="is_subscriber"/></span>
<ul>
<li><field name="member_count"/> members</li>
<li t-if="! record.is_subscriber.raw_value"><a name="action_group_join" string="Join" type="object" class="oe_group_join">Not following</a></li>
<li t-if="record.is_subscriber.raw_value"><a name="action_group_leave" string="Join" type="object" class="oe_group_leave">Following</a></li>
<!-- <li><field name="message_follower_count"/> member(s)</li> -->
<li>
<t t-raw="record.message_follower_ids.raw_value.length"/> member(s)
</li>
<li t-if="! record.message_is_follower.raw_value"><a name="action_group_join" string="Join" type="object" class="oe_group_join">Not following</a></li>
<li t-if="record.message_is_follower.raw_value"><a name="action_group_leave" string="Join" type="object" class="oe_group_leave">Following</a></li>
<li><field name="last_month_msg_nbr"/> messages last month</li>
</ul>
</div>
@ -84,6 +88,8 @@
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"
options='{"thread_level": 1}'/>
<field name="message_follower_ids" widget="mail_followers"
context="{'lapin': 'nouille'}" image="image_small"/>
</div>
</form>
</field>

View File

@ -93,6 +93,9 @@ class mail_message_common(osv.TransientModel):
return result
def name_get(self, cr, uid, ids, context=None):
# name_get may receive int id instead of an id list
if isinstance(ids, (int, long)):
ids = [ids]
res = []
for message in self.browse(cr, uid, ids, context=context):
name = ''
@ -208,12 +211,13 @@ class mail_message(osv.Model):
'partner_id': fields.many2one('res.partner', 'Related partner',
help="Deprecated field. Use partner_ids instead."),
'partner_ids': fields.many2many('res.partner',
'mail_message_destination_partner_rel',
'mail_message_res_partner_rel',
'message_id', 'partner_id', 'Destination partners',
help="When sending emails through the social network composition wizard"\
"you may choose to send a copy of the mail to partners."),
'user_id': fields.many2one('res.users', 'Related User', readonly=1),
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'),
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel',
'message_id', 'attachment_id', 'Attachments'),
'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1),
'state': fields.selection([
('outgoing', 'Outgoing'),
@ -371,7 +375,8 @@ class mail_message(osv.Model):
}
email_msg_id = self.create(cr, uid, msg_vals, context)
attachment_ids = []
for fname, fcontent in attachments.iteritems():
for attachment in attachments:
fname, fcontent = attachment
attachment_data = {
'name': fname,
'datas_fname': fname,

View File

@ -36,10 +36,61 @@ from tools.safe_eval import safe_eval as eval
_logger = logging.getLogger(__name__)
def decode_header(message, header, separator=' '):
return separator.join(map(decode,message.get_all(header, [])))
class many2many_reference(fields.many2many):
""" many2many_reference is an override of fields.many2many. It manages
many2many-like table where one id is given by two fields, res_model
and res_id.
"""
def _get_query_and_where_params(self, cr, model, ids, values, where_params):
""" Add in where:
- mail_followers.res_model = 'crm.lead'
"""
query = 'SELECT %(rel)s.%(id2)s, %(rel)s.%(id1)s \
FROM %(rel)s, %(from_c)s \
WHERE %(rel)s.%(id1)s IN %%s \
AND %(rel)s.%(id2)s = %(tbl)s.id \
AND %(rel)s.res_model = %%s \
%(where_c)s \
%(order_by)s \
%(limit)s \
OFFSET %(offset)d' \
% values
where_params = [model._name] + where_params
return query, where_params
def set(self, cr, model, id, name, values, user=None, context=None):
""" Override to add the res_model field in queries. """
if not values: return
rel, id1, id2 = self._sql_names(model)
obj = model.pool.get(self._obj)
for act in values:
if not (isinstance(act, list) or isinstance(act, tuple)) or not act:
continue
if act[0] == 0:
idnew = obj.create(cr, user, act[2], context=context)
cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, idnew, model._name))
elif act[0] == 3:
cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND res_model=%s', (id, act[1], model._name))
elif act[0] == 4:
# following queries are in the same transaction - so should be relatively safe
cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND res_model=%s', (id, act[1], model._name))
if not cr.fetchone():
cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, act[1], model._name))
elif act[0] == 6:
d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context)
if d1:
d1 = ' and ' + ' and '.join(d1)
else:
d1 = ''
cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND res_model=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, model._name, id]+d2)
for act_nbr in act[2]:
cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, act_nbr, model._name))
else:
return super(many2many_reference, self).set(cr, model, id, name, values, user, context)
class mail_thread(osv.Model):
'''Mixin model, meant to be inherited by any model that needs to
@ -62,22 +113,24 @@ class mail_thread(osv.Model):
default implementation will work for any model. However it is common
to override at least the ``message_new`` and ``message_update``
methods (calling ``super``) to add model-specific behavior at
creation and update of a thread; and ``message_get_subscribers``
to manage more precisely the social aspect of the thread through
the followers.
creation and update of a thread.
#TODO: UPDATE WITH SUBTYPE / NEW FOLLOW MECHANISM
'''
_name = 'mail.thread'
_description = 'Email Thread'
def _get_message_ids(self, cr, uid, ids, name, args, context=None):
res = {}
def _get_message_data(self, cr, uid, ids, field_names, args, context=None):
res = dict.fromkeys(ids)
for id in ids:
message_ids = self.message_search(cr, uid, [id], context=context)
subscriber_ids = self.message_get_subscribers(cr, uid, [id], context=context)
res[id] = {
'message_ids': message_ids,
'message_summary': "<span><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (len(message_ids), len(subscriber_ids)),
}
res[id] = {'message_ids': self.message_search(cr, uid, [id], context=context)}
for thread in self.browse(cr, uid, ids, context=context):
message_follower_ids = [follower.id for follower in thread.message_follower_ids]
res[thread.id].update({
'message_is_follower': uid in message_follower_ids,
'message_summary': "<span><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" %
(len(res[thread.id]['message_ids']), len(thread.message_follower_ids))
})
return res
def _search_message_ids(self, cr, uid, obj, name, args, context=None):
@ -86,20 +139,27 @@ class mail_thread(osv.Model):
return [('id', 'in', msg_ids)]
_columns = {
'message_ids': fields.function(_get_message_ids,
'message_ids': fields.function(_get_message_data,
fnct_search=_search_message_ids,
type='one2many', obj='mail.message', _fields_id = 'res_id',
string='Temp messages', multi="_get_message_ids",
help="Functional field holding messages related to the current document."),
string='Messages', multi="_get_message_data",
help="Field holding discussion about the current document."),
'message_follower_ids': many2many_reference('res.users',
rel='mail_followers', id1='res_id', id2='user_id', string="Followers",
help="Followers of the document. The followers have full access to " \
"the document details, as well as the conversation."),
'message_is_follower': fields.function(_get_message_data, method=True,
type='boolean', string='I am Follower', multi='_get_message_data',
help='True if the current user is following the current document.'),
'message_state': fields.boolean('Read',
help="When checked, new messages require your attention."),
'message_summary': fields.function(_get_message_ids, method=True,
type='text', string='Summary', multi="_get_message_ids",
'message_summary': fields.function(_get_message_data, method=True,
type='text', string='Summary', multi='_get_message_data',
help="Holds the Chatter summary (number of messages, ...). "\
"This summary is directly in html format in order to "\
"be inserted in kanban views."),
}
_defaults = {
'message_state': True,
}
@ -109,40 +169,93 @@ class mail_thread(osv.Model):
#------------------------------------------------------
def create(self, cr, uid, vals, context=None):
"""Automatically subscribe the creator """
""" Override of create to subscribe :
- the writer
- followers given by the monitored fields
"""
thread_id = super(mail_thread, self).create(cr, uid, vals, context=context)
if thread_id:
self.message_subscribe(cr, uid, [thread_id], [uid], context=context)
followers_command = self.message_get_automatic_followers(cr, uid, thread_id, vals, fetch_missing=False, context=context)
if followers_command:
self.write(cr, uid, [thread_id], {'message_follower_ids': followers_command}, context=context)
return thread_id
def write(self, cr, uid, ids, vals, context=None):
"""Automatically subscribe the writer"""
""" Override of write to subscribe :
- the writer
- followers given by the monitored fields
"""
if isinstance(ids, (int, long)):
ids = [ids]
write_res = super(mail_thread, self).write(cr, uid, ids, vals, context=context);
if write_res:
self.message_subscribe(cr, uid, ids, [uid], context=context)
return write_res;
for id in ids:
# copy original vals because we are going to modify it
specific_vals = dict(vals)
# we modify followers: do not subscribe the uid
if specific_vals.get('message_follower_ids'):
followers_command = self.message_get_automatic_followers(cr, uid, id, specific_vals, add_uid=False, context=context)
specific_vals['message_follower_ids'] += followers_command
else:
followers_command = self.message_get_automatic_followers(cr, uid, id, specific_vals, context=context)
specific_vals['message_follower_ids'] = followers_command
write_res = super(mail_thread, self).write(cr, uid, ids, specific_vals, context=context)
return True
def unlink(self, cr, uid, ids, context=None):
"""Override unlink, to automatically delete
- subscriptions
- messages
"""Override unlink, to automatically delete messages
that are linked with res_model and res_id, not through
a foreign key with a 'cascade' ondelete attribute.
Notifications will be deleted with messages
"""
subscr_obj = self.pool.get('mail.subscription')
msg_obj = self.pool.get('mail.message')
# delete subscriptions
subscr_to_del_ids = subscr_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
subscr_obj.unlink(cr, uid, subscr_to_del_ids, context=context)
# delete messages and notifications
msg_to_del_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context)
msg_obj.unlink(cr, uid, msg_to_del_ids, context=context)
return super(mail_thread, self).unlink(cr, uid, ids, context=context)
def message_get_automatic_followers(self, cr, uid, id, record_vals, add_uid=True, fetch_missing=False, context=None):
""" Return the command for the many2many follower_ids field to manage
subscribers. Behavior :
- get the monitored fields (ex: ['user_id', 'responsible_id']); those
fields should be relationships to res.users (#TODO: res.partner)
- if this field is in the record_vals: it means it has been modified
thus add its value to the followers
- if this fields is not in record_vals, but fetch_missing paramter
is set to True: fetch the value in the record (use: at creation
for default values, not present in record_vals)
- if add_uid: add the current user (for example: writer is subscriber)
- generate the command and return it
This method has to be used on 1 id, because otherwise it would imply
to track which user.id is used for which record.id.
:param record_vals: values given to the create method of the new
record, or values updated in a write.
:param monitored_fields: a list of fields that are monitored. Those
fields must be many2one fields to the res.users model.
:param fetch_missing: is set to True, the method will read the
record to find values that are not present in record_vals.
#TODO : UPDATE WHEN MERGING TO PARTNERS
"""
# get monitored fields
monitored_fields = self.message_get_monitored_follower_fields(cr, uid, [id], context=context)
modified_fields = [field for field in monitored_fields if field in record_vals.iterkeys()]
other_fields = [field for field in monitored_fields if field not in record_vals.iterkeys()] if fetch_missing else []
# for each monitored field: if in record_vals, it has been modified/added
follower_ids = []
for field in modified_fields:
# do not add 'False'
if record_vals.get(fields):
follower_ids.append(record_vals.get(field))
# for other fields: read in record if fetch_missing (otherwise list is void)
for field in other_fields:
record = self.browse(cr, uid, id, context=context)
value = getattr(record, field)
if value:
follower_ids.append(value)
# add uid if asked and not already present
if add_uid and uid not in follower_ids:
follower_ids.append(uid)
return self.message_subscribe_get_command(cr, uid, follower_ids, context=context)
#------------------------------------------------------
# mail.message wrappers and tools
#------------------------------------------------------
@ -151,21 +264,21 @@ class mail_thread(osv.Model):
""" OpenChatter: wrapper of mail.message create method
- creates the mail.message
- automatically subscribe the message writer
- push the message to subscribed users
- push the message to followers
"""
if context is None:
context = {}
message_obj = self.pool.get('mail.message')
notification_obj = self.pool.get('mail.notification')
# create message
msg_id = self.pool.get('mail.message').create(cr, uid, vals, context=context)
# automatically subscribe the writer of the message
if vals.get('user_id'):
self.message_subscribe(cr, uid, [thread_id], [vals['user_id']], context=context)
# create message
msg_id = message_obj.create(cr, uid, vals, context=context)
record = self.browse(cr, uid, thread_id, context=context)
follower_ids = [follower.id for follower in record.message_follower_ids]
if vals.get('user_id') not in follower_ids:
self.message_subscribe(cr, uid, [thread_id], [vals.get('user_id')], context=context)
# Set as unread if writer is not the document responsible
self.message_create_set_unread(cr, uid, [thread_id], context=context)
@ -174,6 +287,7 @@ class mail_thread(osv.Model):
return msg_id
# get users that will get a notification pushed
notification_obj = self.pool.get('mail.notification')
user_to_push_ids = self.message_get_user_ids_to_notify(cr, uid, [thread_id], vals, context=context)
for id in user_to_push_ids:
notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context)
@ -188,11 +302,10 @@ class mail_thread(osv.Model):
body = new_msg_vals.get('body_html', '') if new_msg_vals.get('content_subtype') == 'html' else new_msg_vals.get('body_text', '')
# get subscribers
notif_user_ids = self.message_get_subscribers(cr, uid, thread_ids, context=context)
# add users requested via parsing message (@login)
notif_user_ids += self.message_parse_users(cr, uid, body, context=context)
subscr_obj = self.pool.get('mail.followers')
subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', thread_ids)], context=context)
notif_user_ids = [sub['user_id'][0] for sub in subscr_obj.read(cr, uid, subscr_ids, ['user_id'], context=context)]
# add users requested to perform an action (need_action mechanism)
if hasattr(self, 'get_needaction_user_ids') and self._columns.get('user_id'):
user_ids_dict = self.get_needaction_user_ids(cr, uid, thread_ids, context=context)
@ -210,17 +323,6 @@ class mail_thread(osv.Model):
notif_user_ids = list(set(notif_user_ids))
return notif_user_ids
def message_parse_users(self, cr, uid, string, context=None):
"""Parse message content
- if find @login -(^|\s)@((\w|@|\.)*)-: returns the related ids
this supports login that are emails (such as @raoul@grobedon.net)
"""
regex = re.compile('(^|\s)@((\w|@|\.)*)')
login_lst = [item[1] for item in regex.findall(string)]
if not login_lst: return []
user_ids = self.pool.get('res.users').search(cr, uid, [('login', 'in', login_lst)], context=context)
return user_ids
#------------------------------------------------------
# Generic message api
#------------------------------------------------------
@ -794,7 +896,7 @@ class mail_thread(osv.Model):
for thread in self.browse(cr, uid, ids, context=context):
l = set()
for message in thread.message_ids:
l.add((message.user_id and message.user_id.user_email) or '')
l.add((message.user_id and message.user_id.email) or '')
l.add(message.email_from or '')
l.add(message.email_cc or '')
res[thread.id] = filter(None, l)
@ -903,70 +1005,41 @@ class mail_thread(osv.Model):
# Subscription mechanism
#------------------------------------------------------
def message_get_subscribers(self, cr, uid, ids, context=None):
""" Returns the current document followers. Basically this method
checks in mail.subscription for entries with matching res_model,
res_id.
This method can be overriden to add implicit subscribers, such
as project managers, by adding their user_id to the list of
ids returned by this method.
def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
""" Returns a list of fields containing a res.user.id. Those fields
will be checked to automatically subscribe those users.
"""
subscr_obj = self.pool.get('mail.subscription')
subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
return [sub['user_id'][0] for sub in subscr_obj.read(cr, uid, subscr_ids, ['user_id'], context=context)]
def message_read_subscribers(self, cr, uid, ids, fields=['id', 'name', 'image_small'], context=None):
""" Returns the current document followers as a read result. Used
mainly for Chatter having only one method to call to have
details about users.
"""
user_ids = self.message_get_subscribers(cr, uid, ids, context=context)
return self.pool.get('res.users').read(cr, uid, user_ids, fields=fields, context=context)
def message_is_subscriber(self, cr, uid, ids, user_id = None, context=None):
""" Check if uid or user_id (if set) is a subscriber to the current
document.
:param user_id: if set, check is done on user_id; if not set
check is done on uid
"""
sub_user_id = uid if user_id is None else user_id
if sub_user_id in self.message_get_subscribers(cr, uid, ids, context=context):
return True
return False
return []
def message_subscribe(self, cr, uid, ids, user_ids = None, context=None):
""" Subscribe the user (or user_ids) to the current document.
:param user_ids: a list of user_ids; if not set, subscribe
uid instead
:param return: new value of followers, for Chatter
"""
subscription_obj = self.pool.get('mail.subscription')
to_subscribe_uids = [uid] if user_ids is None else user_ids
create_ids = []
for id in ids:
already_subscribed_user_ids = self.message_get_subscribers(cr, uid, [id], context=context)
for user_id in to_subscribe_uids:
if user_id in already_subscribed_user_ids: continue
create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context))
return create_ids
write_res = self.write(cr, uid, ids, {'message_follower_ids': self.message_subscribe_get_command(cr, uid, to_subscribe_uids, context)}, context=context)
return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids]
def message_subscribe_get_command(self, cr, uid, follower_ids, context=None):
""" Generate the many2many command to add followers. """
return [(4, id) for id in follower_ids]
def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None):
""" Unsubscribe the user (or user_ids) from the current document.
:param user_ids: a list of user_ids; if not set, subscribe
uid instead
:param return: new value of followers, for Chatter
"""
# Trying to unsubscribe somebody not in subscribers: returns False
# if special management is needed; allows to know that an automatically
# subscribed user tries to unsubscribe and allows to warn him
to_unsubscribe_uids = [uid] if user_ids is None else user_ids
subscription_obj = self.pool.get('mail.subscription')
to_delete_sub_ids = subscription_obj.search(cr, uid,
['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', 'in', to_unsubscribe_uids)], context=context)
if not to_delete_sub_ids:
return False
return subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
write_res = self.write(cr, uid, ids, {'message_follower_ids': self.message_unsubscribe_get_command(cr, uid, to_unsubscribe_uids, context)}, context=context)
return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids]
def message_unsubscribe_get_command(self, cr, uid, follower_ids, context=None):
""" Generate the many2many command to remove followers. """
return [(3, id) for id in follower_ids]
#------------------------------------------------------
# Notification API
@ -997,19 +1070,16 @@ class mail_thread(osv.Model):
# remove message writer
if user_to_notify_ids.count(new_msg_values.get('user_id')) > 0:
user_to_notify_ids.remove(new_msg_values.get('user_id'))
# get user_ids directly asked
user_to_push_from_parse_ids = self.message_parse_users(cr, uid, body, context=context)
# try to find an email_to
email_to = ''
for user in res_users_obj.browse(cr, uid, user_to_notify_ids, context=context):
if not user.notification_email_pref == 'all' and \
not (user.notification_email_pref == 'to_me' and user.id in user_to_push_from_parse_ids):
# TO BE REFACTORED BY FP, JUSTE REMOVED TO_ME, NOT SURE WHAT S NEW BEHAVIOR
if not user.notification_email_pref == 'all':
continue
if not user.user_email:
if not user.email:
continue
email_to = '%s, %s' % (email_to, user.user_email)
email_to = '%s, %s' % (email_to, user.email)
email_to = email_to.lstrip(', ')
# did not find any email address: not necessary to create an email
@ -1020,7 +1090,7 @@ class mail_thread(osv.Model):
current_user = res_users_obj.browse(cr, uid, [uid], context=context)[0]
email_from = new_msg_values.get('email_from')
if not email_from:
email_from = current_user.user_email
email_from = current_user.email
# get email content, create it (with mail_message.create)
email_values = self.message_create_notify_get_email_dict(cr, uid, new_msg_values, email_from, email_to, context)

View File

@ -20,20 +20,28 @@
##############################################################################
from osv import osv
from osv import fields
class res_partner_mail(osv.osv):
class res_partner_mail(osv.Model):
""" Inherits partner and adds CRM information in the partner form """
_name = "res.partner"
_inherit = ['res.partner', 'mail.thread']
def message_search_get_domain(self, cr, uid, ids, context=None):
""" Override of message_search_get_domain for partner discussion page.
The purpose is to add messages directly sent to the partner.
The purpose is to add messages directly sent to the partner. It also
adds messages pushed to the related user, if any, using @login.
"""
initial_domain = super(res_partner_mail, self).message_search_get_domain(cr, uid, ids, context=context)
if self._name == 'res.partner': # to avoid models inheriting from res.partner
search_domain = ['|'] + initial_domain + ['|', ('partner_id', 'in', ids), ('partner_ids', 'in', ids)]
# to avoid models inheriting from res.partner
if self._name != 'res.partner':
return initial_domain
# add message linked to the partner
search_domain = ['|'] + initial_domain + ['|', ('partner_id', 'in', ids), ('partner_ids', 'in', ids)]
# if partner is linked to a user: find @login
res_users_obj = self.pool.get('res.users')
user_ids = res_users_obj.search(cr, uid, [('partner_id', 'in', ids)], context=context)
for user in res_users_obj.browse(cr, uid, user_ids, context=context):
search_domain = ['|'] + search_domain + ['|', ('body_text', 'like', '@%s' % (user.login)), ('body_html', 'like', '@%s' % (user.login))]
return search_domain
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -11,6 +11,7 @@
<div class="oe_chatter">
<field name="message_ids" widget="mail_thread"
options='{"thread_level": 1}'/>
<field name="message_follower_ids" widget="mail_followers"/>
</div>
</xpath>
</field>

View File

@ -28,28 +28,27 @@ from tools.translate import _
_logger = logging.getLogger(__name__)
class res_users(osv.osv):
class res_users(osv.Model):
""" Update of res.users class
- add a preference about sending emails about notifications
- make a new user follow itself
- add a welcome message
"""
_name = 'res.users'
_inherit = ['res.users', 'mail.thread']
_inherit = ['res.users']
_inherits = {'mail.alias': 'alias_id'}
_columns = {
'notification_email_pref': fields.selection([
('all', 'All feeds'),
('comments', 'Only comments'),
('to_me', 'Only when sent directly to me'),
('all', 'All Feeds'),
('to_me', 'Only send directly to me'),
('none', 'Never')
], 'Receive Feeds by Email', required=True,
help="Choose in which case you want to receive an email when you "\
"receive new feeds."),
"receive new feeds."),
'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True,
help="Email address internally associated with this user. Incoming emails will appear "
"in the user's notifications."),
help="Email address internally associated with this user. Incoming "\
"emails will appear in the user's notifications."),
}
_defaults = {
@ -70,13 +69,13 @@ class res_users(osv.osv):
def _auto_init(self, cr, context=None):
"""Installation hook to create aliases for all users and avoid constraint errors."""
# disable the unique alias_id not null constraint, to avoid spurious warning during
# super.auto_init. We'll reinstall it afterwards.
self._columns['alias_id'].required = False
super(res_users,self)._auto_init(cr, context=context)
registry = RegistryManager.get(cr.dbname)
mail_alias = registry.get('mail.alias')
res_users_model = registry.get('res.users')
@ -100,25 +99,30 @@ class res_users(osv.osv):
self._columns['alias_id'].required = True
def create(self, cr, uid, data, context=None):
# create default alias same as the login
mail_alias = self.pool.get('mail.alias')
alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': data['login']}, model_name=self._name, context=context)
data['alias_id'] = alias_id
data.pop('alias_name', None) # prevent errors during copy()
# create user that follows its related partner
user_id = super(res_users, self).create(cr, uid, data, context=context)
mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context)
user = self.browse(cr, uid, user_id, context=context)
# make user follow itself
self.message_subscribe(cr, uid, [user_id], [user_id], context=context)
self.pool.get('res.partner').message_subscribe(cr, uid, [user.partner_id.id], [user_id], context=context)
# alias
mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context)
# create a welcome message
company_name = user.company_id.name if user.company_id else _('the company')
message = _('%s joined the %s network! Take a moment to welcome %s.') % (user.name, company_name, user.name)
self.message_append_note(cr, uid, [user_id], body=message, type='comment', context=context)
self.create_welcome_message(cr, uid, user, context=context)
return user_id
def create_welcome_message(self, cr, uid, user, context=None):
company_name = user.company_id.name if user.company_id else _('the company')
subject = '''%s has joined %s.''' % (user.name, company_name)
body = '''Welcome to OpenERP !'''
# TODO change 1 into user.id but catch errors
return self.pool.get('res.partner').message_append_note(cr, 1, [user.partner_id.id],
subject=subject, body=body, type='comment', content_subtype='html', context=context)
def write(self, cr, uid, ids, vals, context=None):
# User alias is sync'ed with login
if vals.get('login'): vals['alias_name'] = vals['login']
@ -131,28 +135,52 @@ class res_users(osv.osv):
res = super(res_users, self).unlink(cr, uid, ids, context=context)
alias_pool.unlink(cr, uid, alias_ids, context=context)
return res
def message_search_get_domain(self, cr, uid, ids, context=None):
""" Override of message_search_get_domain for partner discussion page.
The purpose is to add messages directly sent to user using
@user_login.
"""
initial_domain = super(res_users, self).message_search_get_domain(cr, uid, ids, context=context)
custom_domain = []
for user in self.browse(cr, uid, ids, context=context):
if custom_domain:
custom_domain += ['|']
custom_domain += ['|', ('body_text', 'like', '@%s' % (user.login)), ('body_html', 'like', '@%s' % (user.login))]
return ['|'] + initial_domain + custom_domain
class res_users_mail_group(osv.osv):
# --------------------------------------------------
# Wrappers on partner methods for Chatter
# #FIXME: another branch holds a refactoring of mail.thread
# that should help cleaning those wrappers
# --------------------------------------------------
def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None,
type='email', email_date=None, parent_id=False,
content_subtype='plain', state=None,
partner_ids=None, email_from=False, email_to=False,
email_cc=None, email_bcc=None, reply_to=None,
headers=None, message_id=False, references=None,
attachments=None, original=None, context=None):
for user in self.browse(cr, uid, threads, context=context):
user.partner_id.message_append(subject, body_text, body_html, type, email_date, parent_id,
content_subtype, state, partner_ids, email_from, email_to, email_cc, email_bcc, reply_to,
headers, message_id, references, attachments, original)
def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None,
limit=100, offset=0, domain=None, context=None):
for user in self.browse(cr, uid, ids, context=context):
return user.partner_id.message_read(fetch_ancestors, ancestor_ids, limit, offset, domain)
def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None,
limit=100, offset=0, domain=None, count=False, context=None):
for user in self.browse(cr, uid, ids, context=context):
return user.partner_id.message_search(fetch_ancestors, ancestor_ids, limit, offset, domain, count)
def message_subscribe(self, cr, uid, ids, user_ids = None, context=None):
for user in self.browse(cr, uid, ids, context=context):
return user.partner_id.message_subscribe(user_ids)
def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None):
for user in self.browse(cr, uid, ids, context=context):
return user.partner_id.message_unsubscribe(user_ids)
class res_users_mail_group(osv.Model):
""" Update of res.groups class
- if adding/removing users from a group, check mail.groups linked to
this user group, and subscribe / unsubscribe them from the discussion
group. This is done by overriding the write method.
"""
_name = 'res.users'
_inherit = ['res.users', 'mail.thread']
_inherit = ['res.users']
def write(self, cr, uid, ids, vals, context=None):
write_res = super(res_users_mail_group, self).write(cr, uid, ids, vals, context=context)
@ -164,9 +192,8 @@ class res_users_mail_group(osv.osv):
mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', user_group_ids)], context=context)
mail_group_obj.message_subscribe(cr, uid, mail_group_ids, ids, context=context)
return write_res
class res_groups_mail_group(osv.osv):
class res_groups_mail_group(osv.Model):
""" Update of res.groups class
- if adding/removing users from a group, check mail.groups linked to
this user group, and subscribe / unsubscribe them from the discussion
@ -184,3 +211,5 @@ class res_groups_mail_group(osv.osv):
mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', ids)], context=context)
mail_group_obj.message_subscribe(cr, uid, mail_group_ids, user_ids, context=context)
return super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context)
# vim:et:

View File

@ -9,7 +9,7 @@
<field name="inherit_id" ref="base.view_users_form_simple_modif"/>
<field name="arch" type="xml">
<data>
<field name="user_email" position="before">
<field name="email" position="before">
<field name="notification_email_pref" readonly="0"/>
</field>
</data>
@ -23,15 +23,10 @@
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<data>
<field name="user_email" position="before">
<field name="email" position="before">
<field name="notification_email_pref"/>
</field>
<xpath expr="/form/sheet" position="after">
<div class="oe_chatter">
<field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/>
</div>
</xpath>
<field name="user_email" position="after">
<field name="email" position="after">
<field name="alias_domain" invisible="1"/>
<field name="alias_id" readonly="1" required="0" attrs="{'invisible': [('alias_domain', '=', False)]}"/>
</field>

View File

@ -2,7 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_message_all,mail.message.all,model_mail_message,,1,0,0,0
access_mail_message_group_user,mail.message.group.user,model_mail_message,base.group_user,1,1,1,1
access_mail_thread,mail.thread,model_mail_thread,base.group_user,1,1,1,0
access_mail_subscription_all,mail.subscription.all,model_mail_subscription,,1,1,1,1
access_mail_followers_all,mail.followers.all,model_mail_followers,,1,1,1,1
access_mail_notification_all,mail.notification.all,model_mail_notification,,1,1,1,1
access_mail_group,mail.group,model_mail_group,base.group_user,1,1,1,1
access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mail_message_all mail.message.all model_mail_message 1 0 0 0
3 access_mail_message_group_user mail.message.group.user model_mail_message base.group_user 1 1 1 1
4 access_mail_thread mail.thread model_mail_thread base.group_user 1 1 1 0
5 access_mail_subscription_all access_mail_followers_all mail.subscription.all mail.followers.all model_mail_subscription model_mail_followers 1 1 1 1
6 access_mail_notification_all mail.notification.all model_mail_notification 1 1 1 1
7 access_mail_group mail.group model_mail_group base.group_user 1 1 1 1
8 access_mail_alias_user mail.alias model_mail_alias base.group_user 1 1 1 0

View File

@ -21,7 +21,7 @@
<field name="name">Mail.group: access only public and joined groups</field>
<field name="model_id" ref="model_mail_group"/>
<!-- This rule has to be improved for employee only groups -->
<field name="domain_force">['|', '|', ('public', '=', 'public'), ('member_ids', 'in', [user.id]), '&amp;', ('public','=','groups'), ('group_public_id','in', [x.id for x in user.groups_id])]</field>
<field name="domain_force">['|', '|', ('public', '=', 'public'), ('message_follower_ids', 'in', [user.id]), '&amp;', ('public','=','groups'), ('group_public_id','in', [x.id for x in user.groups_id])]</field>
</record>
</data>

View File

@ -89,10 +89,14 @@
/* RecordThread
/* ------------------------------------------------------------ */
.openerp div.oe_mail_recthread {
.openerp .oe_form div.oe_chatter {
overflow: auto;
}
.openerp div.oe_mail_recthread {
/*overflow: auto;*/
}
.openerp div.oe_mail_recthread_main {
float: left;
width: 560px;

View File

@ -4,6 +4,8 @@ openerp.mail = function(session) {
var mail = session.mail = {};
openerp_mail_followers(session, mail); // import mail_followers.js
/**
* ------------------------------------------------------------
* FormView
@ -838,32 +840,17 @@ openerp.mail = function(session) {
this._super.apply(this, arguments);
this.params = this.get_definition_options();
this.params.thread_level = this.params.thread_level || 0;
this.params.see_subscribers = true;
this.params.see_subscribers_options = this.params.see_subscribers_options || false;
this.thread = null;
this.ds = new session.web.DataSet(this, this.view.model);
this.ds_users = new session.web.DataSet(this, 'res.users');
},
start: function() {
var self = this;
// NB: all the widget should be modified to check the actual_mode property on view, not use
// any other method to know if the view is in create mode anymore
this.view.on("change:actual_mode", this, this._check_visibility);
this._check_visibility();
mail.ChatterUtils.bind_events(this);
this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); });
if (! this.params.see_subscribers_options) {
this.$element.find('button.oe_mail_button_followers').hide(); }
this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); })
.mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.$element.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); })
.mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.reinit();
},
_check_visibility: function() {
@ -874,72 +861,20 @@ openerp.mail = function(session) {
this._super.apply(this, arguments);
},
reinit: function() {
this.params.see_subscribers = true;
this.params.see_subscribers_options = this.params.see_subscribers_options || false;
this.$element.find('button.oe_mail_button_followers').html('Hide followers')
this.$element.find('button.oe_mail_button_follow').hide();
this.$element.find('button.oe_mail_button_unfollow').hide();
},
set_value: function() {
this._super.apply(this, arguments);
var self = this;
this.reinit();
if (! this.view.datarecord.id ||
session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$element.find('.oe_mail_thread').hide();
return;
}
// fetch followers
var fetch_sub_done = this.fetch_subscribers();
// create and render Thread widget
this.$element.find('div.oe_mail_recthread_main').empty();
if (this.thread) this.thread.destroy();
this.thread = new mail.Thread(this, {'res_model': this.view.model, 'res_id': this.view.datarecord.id, 'uid': this.session.uid,
'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15});
var thread_done = this.thread.appendTo(this.$element.find('div.oe_mail_recthread_main'));
return fetch_sub_done && thread_done;
},
fetch_subscribers: function () {
return this.ds.call('message_read_subscribers', [[this.view.datarecord.id]]).then(this.proxy('display_subscribers'));
},
display_subscribers: function (records) {
var self = this;
this.is_subscriber = false;
var user_list = this.$element.find('ul.oe_mail_followers_display').empty();
this.$element.find('div.oe_mail_recthread_followers h4').html('Followers (' + records.length + ')');
_(records).each(function (record) {
if (record.id == self.session.uid) { self.is_subscriber = true; }
record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.users', 'image_small', record.id);
$(session.web.qweb.render('mail.record_thread.subscriber', {'record': record})).appendTo(user_list);
});
if (self.is_subscriber) {
self.$element.find('button.oe_mail_button_follow').hide();
self.$element.find('button.oe_mail_button_unfollow').show(); }
else {
self.$element.find('button.oe_mail_button_follow').show();
self.$element.find('button.oe_mail_button_unfollow').hide(); }
},
do_follow: function () {
return this.ds.call('message_subscribe', [[this.view.datarecord.id]]).pipe(this.proxy('fetch_subscribers'));
},
do_unfollow: function () {
var self = this;
return this.ds.call('message_unsubscribe', [[this.view.datarecord.id]]).then(function (record) {
if (record == false) self.do_notify("Impossible to unsubscribe", "You are automatically subscribed to this record. You cannot unsubscribe.");
}).pipe(this.proxy('fetch_subscribers'));
},
do_toggle_followers: function () {
this.params.see_subscribers = ! this.params.see_subscribers;
if (this.params.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); }
else { this.$element.find('button.oe_mail_button_followers').html('Show followers'); }
this.$element.find('div.oe_mail_recthread_followers').toggle();
return thread_done;
},
});
@ -976,7 +911,6 @@ openerp.mail = function(session) {
this.params.res_id = params.res_id || false;
this.params.search_view_id = params.search_view_id || false;
this.params.thread_level = params.thread_level || 1;
this.params.title = params.title || false;
this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}};
this.display_show_more = true;
this.thread_list = [];
@ -1021,7 +955,6 @@ openerp.mail = function(session) {
if (this.compose_message_widget) {
this.compose_message_widget.destroy();
}
debugger;
this.compose_message_widget = new mail.ComposeMessage(this, {
'extended_mode': false, 'uid': this.session.uid, 'res_model': this.params.res_model,
'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id });

View File

@ -0,0 +1,122 @@
openerp_mail_followers = function(session, mail) {
var _t = session.web._t,
_lt = session.web._lt;
var mail_followers = session.mail_followers = {};
/**
* ------------------------------------------------------------
* mail_followers Widget
* ------------------------------------------------------------
*
* This widget handles the display of a list of records as a vetical
* list, with an image on the left. The widget itself is a floatting
* right-sided box.
* This widget is mainly used to display the followers of records
* in OpenChatter.
*/
/* Add the widget to registry */
session.web.form.widgets.add('mail_followers', 'openerp.mail_followers.Followers');
mail_followers.Followers = session.web.form.AbstractField.extend({
template: 'mail.followers',
init: function() {
this._super.apply(this, arguments);
this.params = {};
this.params.image = this.node.attrs.image || 'image_small';
this.params.title = this.node.attrs.title || 'Followers';
this.params.display_followers = true;
this.params.display_control = this.node.attrs.display_control || false;
this.params.display_actions = this.node.attrs.display_actions || false;
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
},
start: function() {
var self = this;
// NB: all the widget should be modified to check the actual_mode property on view, not use
// any other method to know if the view is in create mode anymore
this.view.on("change:actual_mode", this, this._check_visibility);
this._check_visibility();
this.$element.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); });
if (! this.params.display_control) {
this.$element.find('button.oe_mail_button_followers').hide(); }
this.$element.find('button.oe_mail_button_follow').click(function () { self.do_follow(); })
.mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.$element.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); })
.mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); })
.mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); });
this.reinit();
},
_check_visibility: function() {
this.$element.toggle(this.view.get("actual_mode") !== "create");
},
destroy: function () {
this._super.apply(this, arguments);
},
reinit: function() {
this.params.display_followers = true;
this.params.display_control = this.node.attrs.display_control || false;
this.params.display_actions = this.node.attrs.display_actions || false;
this.$element.find('button.oe_mail_button_followers').html('Hide followers')
this.$element.find('button.oe_mail_button_follow').hide();
this.$element.find('button.oe_mail_button_unfollow').hide();
},
set_value: function(value_) {
this.reinit();
if (! this.view.datarecord.id ||
session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$element.find('div.oe_mail_recthread_aside').hide();
return;
}
return this.fetch_subscribers(value_);
},
fetch_subscribers: function (value_) {
return this.ds_follow.call('read', [value_ || this.get_value(), ['name', this.params.image]]).then(this.proxy('display_subscribers'));
},
/**
* Display the followers.
* TODO: replace the is_subscriber check by fields read */
display_subscribers: function (records) {
var self = this;
this.is_subscriber = false;
var user_list = this.$element.find('ul.oe_mail_followers_display').empty();
this.$element.find('div.oe_mail_recthread_followers h4').html(this.params.title + ' (' + records.length + ')');
_(records).each(function (record) {
if (record.id == self.session.uid) { self.is_subscriber = true; }
record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.users', 'image_small', record.id);
$(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(user_list);
});
if (this.is_subscriber) {
this.$element.find('button.oe_mail_button_follow').hide();
this.$element.find('button.oe_mail_button_unfollow').show(); }
else {
this.$element.find('button.oe_mail_button_follow').show();
this.$element.find('button.oe_mail_button_unfollow').hide(); }
},
do_follow: function () {
return this.ds_model.call('message_subscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value'));
},
do_unfollow: function () {
return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value'));
},
do_toggle_followers: function () {
this.params.see_subscribers = ! this.params.see_subscribers;
if (this.params.see_subscribers) { this.$element.find('button.oe_mail_button_followers').html('Hide followers'); }
else { this.$element.find('button.oe_mail_button_followers').html('Show followers'); }
this.$element.find('div.oe_mail_recthread_followers').toggle();
},
});
};

View File

@ -61,28 +61,8 @@
<div class="oe_mail_recthread_main">
<!-- contains the document thread -->
</div>
<div class="oe_mail_recthread_aside">
<div class="oe_mail_recthread_actions">
<button type="button" class="oe_mail_button_follow oe_mail_button_mouseout">Not following</button>
<button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button>
<button type="button" class="oe_mail_button_followers">Show followers</button>
</div>
<div class="oe_mail_recthread_followers">
<h4>Followers</h4>
<ul class="oe_mail_followers_display"></ul>
</div>
</div>
</div>
<!--
record_thread.subscriber template
Template used to display a subscriber.
-->
<li t-name="mail.record_thread.subscriber">
<img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/>
<a t-attf-href="#model=res.users&amp;id=#{record.id}"><t t-raw="record.name"/></a>
</li>
<!--
mail.compose_message template
This template holds the composition form to write a note or send

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<!--
followers main template
Template used to display the followers and the actions in a record.
-->
<div t-name="mail.followers" class="oe_mail_recthread_aside">
<div class="oe_mail_recthread_actions">
<button type="button" class="oe_mail_button_follow oe_mail_button_mouseout">Not following</button>
<button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button>
<button type="button" class="oe_mail_button_followers">Show followers</button>
</div>
<div class="oe_mail_recthread_followers">
<t t-if="widget.params.title">
<h4><t t-raw="widget.params.title"/></h4>
</t>
<ul class="oe_mail_followers_display"></ul>
</div>
</div>
<!--
followers.partner template
Template used to display a partner following the record
-->
<li t-name="mail.followers.partner">
<img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/>
<a t-attf-href="#model=res.users&amp;id=#{record.id}"><t t-raw="record.name"/></a>
</li>
</template>

View File

@ -100,7 +100,7 @@ class mail_compose_message(osv.TransientModel):
# Try to provide default email_from if not specified yet
if not result.get('email_from'):
current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
result['email_from'] = current_user.user_email or False
result['email_from'] = current_user.email or False
return result
_columns = {
@ -133,7 +133,7 @@ class mail_compose_message(osv.TransientModel):
result.update({
'model': model,
'res_id': res_id,
'email_from': user.user_email or tools.config.get('email_from', False),
'email_from': user.email or tools.config.get('email_from', False),
'body_html': False,
'body_text': False,
'subject': False,
@ -223,7 +223,7 @@ class mail_compose_message(osv.TransientModel):
'dest_partner_ids': dest_partner_ids,
'model': message_data.model or False,
'res_id': message_data.res_id or False,
'email_from': current_user.user_email or message_data.email_to or False,
'email_from': current_user.email or message_data.email_to or False,
'email_to': message_data.reply_to or message_data.email_from or False,
'email_cc': message_data.email_cc or False,
'user_id': uid,

Some files were not shown because too many files have changed in this diff Show More