<commit_before># coding: utf-8

"""
:mod:`boardgamegeek.hotitems` - BoardGameGeek "Hot Items"
=========================================================

.. module:: boardgamegeek.hotitems
   :platform: Unix, Windows
   :synopsis: BGG "Hot Items"

.. moduleauthor:: Cosmin Luță <q4break@gmail.com>
"""
from __future__ import unicode_literals

from copy import copy

from .things import Thing
from .utils import DictObject

class HotItem(Thing):
    """
    A hot item from a list. Can refer to either an item (``boardgame``, ``videogame``, etc.), a person (``rpgperson``, ``boardgameperson``)
    or even a company (``boardgamecompany``, ``videogamecompany``), depending on the type of hot list retrieved.
    """

    def __repr__(self):
        return "HotItem (id: {})".format(self.id)

    def _format(self, log):
        log.info("hot item id        : {}".format(self.id))
        log.info("hot item name      : {}".format(self.name))
        log.info("hot item rank      : {}".format(self.rank))
        log.info("hot item published : {}".format(self.year))
        log.info("hot item thumbnail : {}".format(self.thumbnail))

    @property
    def rank(self):
        """
        :return: Ranking of this hot item
        """
        return self._data["rank"]

    @property
    def year(self):
        """
        Year this item was published

        :return: integer representing the year
        :return: ``None`` if object doesn't refer to a game
        """
        return self._data.get("yearpublished")

    @property
    def thumbnail(self):
        """
        :return: url of the thumbnail corresponding to this item
        """
        return self._data.get("thumbnail")

class HotItems(DictObject):
    """
    A container for :class:`boardgamegeek.hotitems.HotItem`
    """
    def __init__(self, data):
        kw = copy(data)
        if "items" not in kw:
            kw["items"] = []
        self._items = []
        super(HotItems, self).__init__(kw)

    def add_hot_item(self, data):
        """
        Add a new hot item to the container

        :param data: dictionary containing the data

        """
        self._data["items"].append(data)
        self._items.append(HotItem(data))

    @property
    def items(self):
        """

        :return: the list of :class:`boardgamegeek.hotitems.HotItem`
        """
        return self._items

    def __len__(self):
        return len(self._items)

    def __iter__(self):
        for item in self._data["items"]:
            yield HotItem(item)<commit_msg>Adding any initial hotitems from kw['items']<commit_after># coding: utf-8

"""
:mod:`boardgamegeek.hotitems` - BoardGameGeek "Hot Items"
=========================================================

.. module:: boardgamegeek.hotitems
   :platform: Unix, Windows
   :synopsis: BGG "Hot Items"

.. moduleauthor:: Cosmin Luță <q4break@gmail.com>
"""
from __future__ import unicode_literals

from copy import copy

from .things import Thing
from .utils import DictObject

class HotItem(Thing):
    """
    A hot item from a list. Can refer to either an item (``boardgame``, ``videogame``, etc.), a person (``rpgperson``, ``boardgameperson``)
    or even a company (``boardgamecompany``, ``videogamecompany``), depending on the type of hot list retrieved.
    """

    def __repr__(self):
        return "HotItem (id: {})".format(self.id)

    def _format(self, log):
        log.info("hot item id        : {}".format(self.id))
        log.info("hot item name      : {}".format(self.name))
        log.info("hot item rank      : {}".format(self.rank))
        log.info("hot item published : {}".format(self.year))
        log.info("hot item thumbnail : {}".format(self.thumbnail))

    @property
    def rank(self):
        """
        :return: Ranking of this hot item
        """
        return self._data["rank"]

    @property
    def year(self):
        """
        Year this item was published

        :return: integer representing the year
        :return: ``None`` if object doesn't refer to a game
        """
        return self._data.get("yearpublished")

    @property
    def thumbnail(self):
        """
        :return: url of the thumbnail corresponding to this item
        """
        return self._data.get("thumbnail")

class HotItems(DictObject):
    """
    A container for :class:`boardgamegeek.hotitems.HotItem`
    """
    def __init__(self, data):
        kw = copy(data)
        if "items" not in kw:
            kw["items"] = []

        self._items = []
        for data in kw["items"]:
            self._items.append(HotItem(data))

        super(HotItems, self).__init__(kw)

    def add_hot_item(self, data):
        """
        Add a new hot item to the container

        :param data: dictionary containing the data

        """
        self._data["items"].append(data)
        self._items.append(HotItem(data))

    @property
    def items(self):
        """

        :return: the list of :class:`boardgamegeek.hotitems.HotItem`
        """
        return self._items

    def __len__(self):
        return len(self._items)

    def __iter__(self):
        for item in self._data["items"]:
            yield HotItem(item)<|endoftext|>


<commit_before>#==========================================================================
#
#   Copyright Insight Software Consortium
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#          http://www.apache.org/licenses/LICENSE-2.0.txt
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
#==========================================================================*/

from __future__ import print_function

import types
import inspect
import os
import warnings
import itkConfig
from itkTypes import itkCType

def itkFormatWarning(msg, *a):
    """"Format the warnings issued by itk to display only the message.

    This will ignore the filename and the linenumber where the warning was
    triggered. The message is returned to the warnings module.
    """

    return str(msg) + '\n'

# Redefine the format of the warnings
warnings.formatwarning = itkFormatWarning

def registerNoTpl(name, cl):
    """Register a class without template

    It can seem not useful to register classes without template (and it wasn't
    useful until the SmartPointer template was generated), but those classes
    can be used as template argument of classes with template.
    """
    itkTemplate.__templates__[normalizeName(name)] = cl

def normalizeName(name):
    """Normalize the class name to remove ambiguity

    This function removes the white spaces in the name, and also
    remove the pointer declaration "*" (it have no sense in python) """

    name = name.replace(" ", "")
    name = name.replace("*", "")

    return name

class itkTemplate(object):

    """This class manages access to available template arguments of a C++ class.

    There are two ways to access types:

    1. With a dict interface. The user can manipulate template parameters
    similarly to C++, with the exception that the available parameters sets are
    chosen at compile time. It is also possible, with the dict interface, to
    explore the available parameters sets.
    2. With object attributes. The user can easily find the available parameters
    sets by pressing tab in interperter like ipython
    """
    __templates__ = {}
    __class_to_template__ = {}
    __named_templates__ = {}
    __doxygen_root__ = itkConfig.doxygen_root

    def __new__(cls, name):
        # Singleton pattern: we only make a single instance of any Template of
        # a given name. If we have already made the instance, just return it
        # as-is.
        if name not in cls.__named_templates__:
                new_instance = object.__new__(cls)
                new_instance.__name__ = name
                new_instance.__template__ = {}
                cls.__named_templates__[name] = new_instance
        return cls.__named_templates__[name]

    def __add__(self, paramSetString, cl):
        """Add a new argument set and the resulting class to the template.

        paramSetString is the C++ string which defines the parameters set.
        cl is the class which corresponds to the couple template-argument set.
        """
        # recreate the full name and normalize it to avoid ambiguity
        normFullName = normalizeName(
            self.__name__ + "<" + paramSetString + ">")

        # the full class should not be already registered. If it is, there is a
        # problem somewhere so warn the user so he can fix the problem
        if normFullName in itkTemplate.__templates__:
            message = (
                "Template %s\n already defined as %s\n is redefined "
                "as %s") % (normFullName, self.__templates__[normFullName], cl)
            warnings.warn(message)
        # register the class
        itkTemplate.__templates__[normFullName] = cl

        # __find_param__ will parse the paramSetString and produce a list of
        # the same parameters transformed in corresponding python classes.
        # we transform this list in tuple to make it usable as key of the dict
        param = tuple(self.__find_param__(paramSetString))

        # once again, warn the user if the tuple of parameter is already
        # defined so he can fix the problem
        if param in self.__template__:
            message = "Warning: template already defined '%s'" % normFullName
            warnings.warn(message)
        # and register the parameter tuple
        self.__template__[param] = cl

        # add in __class_to_template__ dictionary
        itkTemplate.__class_to_template__[cl] = (self, param)

        # now populate the template
        # 2 cases:
        # - the template is a SmartPointer. In that case, the attribute name
        # will be the full real name of the class without the itk prefix and
        # _Pointer suffix
        # - the template is not a SmartPointer. In that case, we keep only the
        # end of the real class name which is a short string discribing the
        # template arguments (for example IUC2)
        if cl.__name__.startswith("itk"):
            if cl.__name__.endswith("_Pointer"):
                # it's a SmartPointer
                attributeName = cl.__name__[len("itk"):-len("_Pointer")]
            else:
                # it's not a SmartPointer
                # we need to now the size of the name to keep only the suffix
                # short name does not contain :: and nested namespace
                # itk::Numerics::Sample -> itkSample
                import re
                shortNameSize = len(re.sub(r':.*:', '', self.__name__))
                attributeName = cl.__name__[shortNameSize:]
        elif cl.__name__.startswith("vcl_complex"):
            # C++ name is likely to be std::complex here, instead of the
            # expected vcl_complex
            attributeName = cl.__name__[len("vcl_complex"):]
        else:
            import re
            shortNameSize = len(re.sub(r'.*::', '', self.__name__))
            attributeName = cl.__name__[shortNameSize:]

        if attributeName.isdigit():
            # the attribute name can't be a number
            # add a single undescore before it to build a valid name
            attributeName = "_" + attributeName

        # add the attribute to this object
        self.__dict__[attributeName] = cl

    def __find_param__(self, paramSetString):
        """Find the parameters of the template.

        paramSetString is the C++ string which defines the parameters set.

        __find_param__ returns a list of itk classes, itkCType, and/or numbers
        which correspond to the parameters described in paramSetString.
        The parameters MUST have been registered before calling this method,
        or __find_param__ will return a string and not the wanted object, and
        will display a warning. Registration order is important.

        This method is not static only to be able to display the template name
        in the warning.
        """
        # split the string in a list of parameters
        paramStrings = []
        inner = 0
        part = paramSetString.split(",")
        for elt in part:
            if inner == 0:
                paramStrings.append(elt)
            else:
                paramStrings[-1] += "," + elt
            inner += elt.count("<") - elt.count(">")

        # convert all string parameters into classes (if possible)
        parameters = []
        for param in paramStrings:
            # the parameter need to be normalized several time below
            # do it once here
            param = param.strip()
            paramNorm = normalizeName(param)

            if paramNorm in itkTemplate.__templates__:
                # the parameter is registered.
                # just get the really class form the dictionary
                param = itkTemplate.__templates__[paramNorm]

            elif itkCType.GetCType(param):
                # the parameter is a c type
                # just get the itkCtype instance
                param = itkCType.GetCType(param)

            elif paramNorm.isdigit():
                # the parameter is a number
                # convert the string to a number !
                param = int(param)

            elif paramNorm == "true":
                param = True
            elif paramNorm == "false":
                param = False

            else:
                # unable to convert the parameter
                # use it without changes, but display a warning message, to
                # incite developer to fix the problem
                message = (
                    "Warning: Unknown parameter '%s' in "
                    "template '%s'" % (param, self.__name__))
                warnings.warn(message)

            parameters.append(param)

        return parameters

    def __getitem__(self, parameters):
        """Return the class which corresponds to the given template parameters.

        parameters can be:
            - a single parameter (Ex: itk.Index[2])
            - a list of elements (Ex: itk.Image[itk.UC, 2])
        """

        isin = isinstance(parameters, types.TupleType)
        if not isin and not isinstance(parameters, types.ListType):
            # parameters is a single element.
            # include it in a list to manage the 2 cases in the same way
            parameters = [parameters]

        cleanParameters = []
        for param in parameters:
            # In the case of itk class instance, get the class
            name = param.__class__.__name__
            isclass = inspect.isclass(param)
            if not isclass and name[:3] == 'itk' and name != "itkCType":
                param = param.__class__

            # append the parameter to the list. If it's not a supported type,
            # it is not in the dictionary and we will raise an exception below
            cleanParameters.append(param)

        try:
            return(self.__template__[tuple(cleanParameters)])
        except:
            raise KeyError(
                'itkTemplate : No template %s for the %s class' %
                (str(parameters), self.__name__))

    def __repr__(self):
        return '<itkTemplate %s>' % self.__name__

    def __getattribute__(self, attr):
        """Support for reading doxygen man pages to produce __doc__ strings."""
        root = itkTemplate.__doxygen_root__
        indoc = (attr == '__doc__')
        if indoc and root != "" and self.__name__.startswith('itk'):
            try:
                import commands
                doxyname = self.__name__.replace("::", "_")
                man_path = "%s/man3/%s.3" % (root, doxyname)
                bzman_path = "%s/man3/%s.3.bz2" % (root, doxyname)
                if os.path.exists(bzman_path):
                        return (
                            commands.getoutput(
                                "bunzip2 --stdout '" + bzman_path +
                                "' | groff -mandoc -Tascii -c"))
                elif os.path.exists(man_path):
                    # Use groff here instead of man because man dies when it is
                    # passed paths with spaces (!) groff does not.
                    return (
                        commands.getoutput(
                            "groff -mandoc -Tascii -c '" +
                            man_path + "'"))
                else:
                    return (
                        "Cannot find man page for %s: %s"
                        % (self.__name__, man_path + "[.bz2]"))
            except Exception as e:
                return (
                    "Cannot display man page for %s due to exception: %s."
                    % (self.__name__, e))
        else:
            return object.__getattribute__(self, attr)

    def New(self, *args, **kargs):
        """TODO: some doc! Don't call it __call__ as it break the __doc__
        attribute feature in ipython"""
        import itk
        keys = self.keys()
        if len(args) != 0:
            # try to find a type suitable for the input provided
            input_types = [output(f).__class__ for f in args]
            keys = [k for k in self.keys() if k[0] == input_types[0]]
        cur = itk.auto_pipeline.current
        if cur is not None and len(cur) != 0:
            # try to find a type suitable for the input provided
            input_type = output(cur).__class__
            keys = [k for k in self.keys() if k[0] == input_type]
        if len(keys) == 0:
            raise RuntimeError("No suitable template parameter can be found.")
        return self[keys[0]].New(*args, **kargs)

    def keys(self):
        return self.__template__.keys()

    # everything after this comment is for dict interface
    # and is a copy/paste from DictMixin
    # only methods to edit dictionary are not there
    def __iter__(self):
        for k in self.keys():
            yield k

    def __contains__(self, key):
        return key in self

    # third level takes advantage of second level definitions
    def iteritems(self):
        for k in self:
            yield (k, self[k])

    def iterkeys(self):
        return self.__iter__()

    # fourth level uses definitions from lower levels
    def itervalues(self):
        for _, v in self.iteritems():
            yield v

    def values(self):
        return [v for _, v in self.iteritems()]

    def items(self):
        return list(self.iteritems())

    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default

    def __len__(self):
        return len(self.keys())

    def GetTypes(self):
        """Helper method which prints out the available template parameters."""

        print("<itkTemplate %s>" % self.__name__)
        print("Options:")
        for tp in self.GetTypesAsList():
            print("  " + str(tp).replace("(", "[").replace(")", "]"))

    def GetTypesAsList(self):
        """Helper method which returns the available template parameters."""

        # Make a list of allowed types, and sort them
        ctypes = []
        classes = []
        for key_tuple in self.__template__:
            key = str(key_tuple)
            if "itkCType" in key:
                ctypes.append(key)
            elif "class" in key:
                classes.append(key)

        # Sort the lists
        ctypes = sorted(ctypes)
        classes = sorted(classes)

        return ctypes + classes

# create a new New function which accepts parameters
def New(self, *args, **kargs):
    import itk

    itk.set_inputs(self, args, kargs)

    # now, try to add observer to display progress
    if "auto_progress" in kargs.keys():
        if kargs["auto_progress"] in [True, 1]:
            callback = itk.terminal_progress_callback
        elif kargs["auto_progress"] == 2:
            callback = itk.simple_progress_callback
        else:
            callback = None
    elif itkConfig.ProgressCallback:
        callback = itkConfig.ProgressCallback
    else:
        callback = None

    if callback:
        try:
            name = self.__class__.__name__

            def progress():
                # self and callback are kept referenced with a closure
                callback(name, self.GetProgress())

            self.AddObserver(itk.ProgressEvent(), progress)
        except:
            # it seems that something goes wrong...
            # as this feature is designed for prototyping, it's not really a
            # problem if an object doesn't have progress reporter, so adding
            # reporter can silently fail
            pass

    if itkConfig.NotInPlace and "SetInPlace" in dir(self):
        self.SetInPlace(False)

    if itk.auto_pipeline.current is not None:
        itk.auto_pipeline.current.connect(self)

    return self

def output(input):
    try:
        img = input.GetOutput()
    except AttributeError:
        img = input
    return img

def image(input):
    import sys
    print(
        ("WrapITK warning: itk.image() is deprecated. "
            "Use itk.output() instead."), file=sys.stderr)
    return output(input)
<commit_msg>BUG: Update itkTemplate.py for Python 3.<commit_after>#==========================================================================
#
#   Copyright Insight Software Consortium
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#          http://www.apache.org/licenses/LICENSE-2.0.txt
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
#==========================================================================*/

from __future__ import print_function

import inspect
import os
import warnings
import itkConfig
from itkTypes import itkCType

def itkFormatWarning(msg, *a):
    """"Format the warnings issued by itk to display only the message.

    This will ignore the filename and the linenumber where the warning was
    triggered. The message is returned to the warnings module.
    """

    return str(msg) + '\n'

# Redefine the format of the warnings
warnings.formatwarning = itkFormatWarning

def registerNoTpl(name, cl):
    """Register a class without template

    It can seem not useful to register classes without template (and it wasn't
    useful until the SmartPointer template was generated), but those classes
    can be used as template argument of classes with template.
    """
    itkTemplate.__templates__[normalizeName(name)] = cl

def normalizeName(name):
    """Normalize the class name to remove ambiguity

    This function removes the white spaces in the name, and also
    remove the pointer declaration "*" (it have no sense in python) """

    name = name.replace(" ", "")
    name = name.replace("*", "")

    return name

class itkTemplate(object):

    """This class manages access to available template arguments of a C++ class.

    There are two ways to access types:

    1. With a dict interface. The user can manipulate template parameters
    similarly to C++, with the exception that the available parameters sets are
    chosen at compile time. It is also possible, with the dict interface, to
    explore the available parameters sets.
    2. With object attributes. The user can easily find the available parameters
    sets by pressing tab in interperter like ipython
    """
    __templates__ = {}
    __class_to_template__ = {}
    __named_templates__ = {}
    __doxygen_root__ = itkConfig.doxygen_root

    def __new__(cls, name):
        # Singleton pattern: we only make a single instance of any Template of
        # a given name. If we have already made the instance, just return it
        # as-is.
        if name not in cls.__named_templates__:
                new_instance = object.__new__(cls)
                new_instance.__name__ = name
                new_instance.__template__ = {}
                cls.__named_templates__[name] = new_instance
        return cls.__named_templates__[name]

    def __add__(self, paramSetString, cl):
        """Add a new argument set and the resulting class to the template.

        paramSetString is the C++ string which defines the parameters set.
        cl is the class which corresponds to the couple template-argument set.
        """
        # recreate the full name and normalize it to avoid ambiguity
        normFullName = normalizeName(
            self.__name__ + "<" + paramSetString + ">")

        # the full class should not be already registered. If it is, there is a
        # problem somewhere so warn the user so he can fix the problem
        if normFullName in itkTemplate.__templates__:
            message = (
                "Template %s\n already defined as %s\n is redefined "
                "as %s") % (normFullName, self.__templates__[normFullName], cl)
            warnings.warn(message)
        # register the class
        itkTemplate.__templates__[normFullName] = cl

        # __find_param__ will parse the paramSetString and produce a list of
        # the same parameters transformed in corresponding python classes.
        # we transform this list in tuple to make it usable as key of the dict
        param = tuple(self.__find_param__(paramSetString))

        # once again, warn the user if the tuple of parameter is already
        # defined so he can fix the problem
        if param in self.__template__:
            message = "Warning: template already defined '%s'" % normFullName
            warnings.warn(message)
        # and register the parameter tuple
        self.__template__[param] = cl

        # add in __class_to_template__ dictionary
        itkTemplate.__class_to_template__[cl] = (self, param)

        # now populate the template
        # 2 cases:
        # - the template is a SmartPointer. In that case, the attribute name
        # will be the full real name of the class without the itk prefix and
        # _Pointer suffix
        # - the template is not a SmartPointer. In that case, we keep only the
        # end of the real class name which is a short string discribing the
        # template arguments (for example IUC2)
        if cl.__name__.startswith("itk"):
            if cl.__name__.endswith("_Pointer"):
                # it's a SmartPointer
                attributeName = cl.__name__[len("itk"):-len("_Pointer")]
            else:
                # it's not a SmartPointer
                # we need to now the size of the name to keep only the suffix
                # short name does not contain :: and nested namespace
                # itk::Numerics::Sample -> itkSample
                import re
                shortNameSize = len(re.sub(r':.*:', '', self.__name__))
                attributeName = cl.__name__[shortNameSize:]
        elif cl.__name__.startswith("vcl_complex"):
            # C++ name is likely to be std::complex here, instead of the
            # expected vcl_complex
            attributeName = cl.__name__[len("vcl_complex"):]
        else:
            import re
            shortNameSize = len(re.sub(r'.*::', '', self.__name__))
            attributeName = cl.__name__[shortNameSize:]

        if attributeName.isdigit():
            # the attribute name can't be a number
            # add a single undescore before it to build a valid name
            attributeName = "_" + attributeName

        # add the attribute to this object
        self.__dict__[attributeName] = cl

    def __find_param__(self, paramSetString):
        """Find the parameters of the template.

        paramSetString is the C++ string which defines the parameters set.

        __find_param__ returns a list of itk classes, itkCType, and/or numbers
        which correspond to the parameters described in paramSetString.
        The parameters MUST have been registered before calling this method,
        or __find_param__ will return a string and not the wanted object, and
        will display a warning. Registration order is important.

        This method is not static only to be able to display the template name
        in the warning.
        """
        # split the string in a list of parameters
        paramStrings = []
        inner = 0
        part = paramSetString.split(",")
        for elt in part:
            if inner == 0:
                paramStrings.append(elt)
            else:
                paramStrings[-1] += "," + elt
            inner += elt.count("<") - elt.count(">")

        # convert all string parameters into classes (if possible)
        parameters = []
        for param in paramStrings:
            # the parameter need to be normalized several time below
            # do it once here
            param = param.strip()
            paramNorm = normalizeName(param)

            if paramNorm in itkTemplate.__templates__:
                # the parameter is registered.
                # just get the really class form the dictionary
                param = itkTemplate.__templates__[paramNorm]

            elif itkCType.GetCType(param):
                # the parameter is a c type
                # just get the itkCtype instance
                param = itkCType.GetCType(param)

            elif paramNorm.isdigit():
                # the parameter is a number
                # convert the string to a number !
                param = int(param)

            elif paramNorm == "true":
                param = True
            elif paramNorm == "false":
                param = False

            else:
                # unable to convert the parameter
                # use it without changes, but display a warning message, to
                # incite developer to fix the problem
                message = (
                    "Warning: Unknown parameter '%s' in "
                    "template '%s'" % (param, self.__name__))
                warnings.warn(message)

            parameters.append(param)

        return parameters

    def __getitem__(self, parameters):
        """Return the class which corresponds to the given template parameters.

        parameters can be:
            - a single parameter (Ex: itk.Index[2])
            - a list of elements (Ex: itk.Image[itk.UC, 2])
        """

        parameters_type = type(parameters)
        if not parameters_type is tuple and not parameters_type is list:
            # parameters is a single element.
            # include it in a list to manage the 2 cases in the same way
            parameters = [parameters]

        cleanParameters = []
        for param in parameters:
            # In the case of itk class instance, get the class
            name = param.__class__.__name__
            isclass = inspect.isclass(param)
            if not isclass and name[:3] == 'itk' and name != "itkCType":
                param = param.__class__

            # append the parameter to the list. If it's not a supported type,
            # it is not in the dictionary and we will raise an exception below
            cleanParameters.append(param)

        try:
            return(self.__template__[tuple(cleanParameters)])
        except:
            raise KeyError(
                'itkTemplate : No template %s for the %s class' %
                (str(parameters), self.__name__))

    def __repr__(self):
        return '<itkTemplate %s>' % self.__name__

    def __getattribute__(self, attr):
        """Support for reading doxygen man pages to produce __doc__ strings."""
        root = itkTemplate.__doxygen_root__
        indoc = (attr == '__doc__')
        if indoc and root != "" and self.__name__.startswith('itk'):
            try:
                import commands
                doxyname = self.__name__.replace("::", "_")
                man_path = "%s/man3/%s.3" % (root, doxyname)
                bzman_path = "%s/man3/%s.3.bz2" % (root, doxyname)
                if os.path.exists(bzman_path):
                        return (
                            commands.getoutput(
                                "bunzip2 --stdout '" + bzman_path +
                                "' | groff -mandoc -Tascii -c"))
                elif os.path.exists(man_path):
                    # Use groff here instead of man because man dies when it is
                    # passed paths with spaces (!) groff does not.
                    return (
                        commands.getoutput(
                            "groff -mandoc -Tascii -c '" +
                            man_path + "'"))
                else:
                    return (
                        "Cannot find man page for %s: %s"
                        % (self.__name__, man_path + "[.bz2]"))
            except Exception as e:
                return (
                    "Cannot display man page for %s due to exception: %s."
                    % (self.__name__, e))
        else:
            return object.__getattribute__(self, attr)

    def New(self, *args, **kargs):
        """TODO: some doc! Don't call it __call__ as it break the __doc__
        attribute feature in ipython"""
        import itk
        keys = self.keys()
        if len(args) != 0:
            # try to find a type suitable for the input provided
            input_types = [output(f).__class__ for f in args]
            keys = [k for k in self.keys() if k[0] == input_types[0]]
        cur = itk.auto_pipeline.current
        if cur is not None and len(cur) != 0:
            # try to find a type suitable for the input provided
            input_type = output(cur).__class__
            keys = [k for k in self.keys() if k[0] == input_type]
        if len(keys) == 0:
            raise RuntimeError("No suitable template parameter can be found.")
        return self[keys[0]].New(*args, **kargs)

    def keys(self):
        return self.__template__.keys()

    # everything after this comment is for dict interface
    # and is a copy/paste from DictMixin
    # only methods to edit dictionary are not there
    def __iter__(self):
        for k in self.keys():
            yield k

    def __contains__(self, key):
        return key in self

    # third level takes advantage of second level definitions
    def iteritems(self):
        for k in self:
            yield (k, self[k])

    def iterkeys(self):
        return self.__iter__()

    # fourth level uses definitions from lower levels
    def itervalues(self):
        for _, v in self.iteritems():
            yield v

    def values(self):
        return [v for _, v in self.iteritems()]

    def items(self):
        return list(self.iteritems())

    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default

    def __len__(self):
        return len(self.keys())

    def GetTypes(self):
        """Helper method which prints out the available template parameters."""

        print("<itkTemplate %s>" % self.__name__)
        print("Options:")
        for tp in self.GetTypesAsList():
            print("  " + str(tp).replace("(", "[").replace(")", "]"))

    def GetTypesAsList(self):
        """Helper method which returns the available template parameters."""

        # Make a list of allowed types, and sort them
        ctypes = []
        classes = []
        for key_tuple in self.__template__:
            key = str(key_tuple)
            if "itkCType" in key:
                ctypes.append(key)
            elif "class" in key:
                classes.append(key)

        # Sort the lists
        ctypes = sorted(ctypes)
        classes = sorted(classes)

        return ctypes + classes

# create a new New function which accepts parameters
def New(self, *args, **kargs):
    import itk

    itk.set_inputs(self, args, kargs)

    # now, try to add observer to display progress
    if "auto_progress" in kargs.keys():
        if kargs["auto_progress"] in [True, 1]:
            callback = itk.terminal_progress_callback
        elif kargs["auto_progress"] == 2:
            callback = itk.simple_progress_callback
        else:
            callback = None
    elif itkConfig.ProgressCallback:
        callback = itkConfig.ProgressCallback
    else:
        callback = None

    if callback:
        try:
            name = self.__class__.__name__

            def progress():
                # self and callback are kept referenced with a closure
                callback(name, self.GetProgress())

            self.AddObserver(itk.ProgressEvent(), progress)
        except:
            # it seems that something goes wrong...
            # as this feature is designed for prototyping, it's not really a
            # problem if an object doesn't have progress reporter, so adding
            # reporter can silently fail
            pass

    if itkConfig.NotInPlace and "SetInPlace" in dir(self):
        self.SetInPlace(False)

    if itk.auto_pipeline.current is not None:
        itk.auto_pipeline.current.connect(self)

    return self

def output(input):
    try:
        img = input.GetOutput()
    except AttributeError:
        img = input
    return img

def image(input):
    import sys
    print(
        ("WrapITK warning: itk.image() is deprecated. "
            "Use itk.output() instead."), file=sys.stderr)
    return output(input)
<|endoftext|>


<commit_before>#!/usr/bin/python2.5
#
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""User handlers and functions."""

__author__ = 'elsigh@google.com (Lindsey Simon)'

import hashlib
import logging
import random
import re

from google.appengine.api import users
from google.appengine.api import datastore_errors
from google.appengine.ext import deferred
from google.appengine.ext import db

import django
from django import http
from django import shortcuts
from django.template import loader, Context

import models.user_test
from base import decorators
from base import util

#from third_party.mirorrr import mirror

from django.template import add_to_builtins
add_to_builtins('base.custom_filters')

import settings

def TestHowto(request):
  params = {}
  return util.Render(request, 'user_test_howto.html', params)

@decorators.login_required
def Settings(request):
  logging.info('Settings start.')
  if request.POST:
    current_user = users.get_current_user()
    u = models.user_test.User.get_or_insert(current_user.user_id())
    u.email = request.POST.get('email', current_user.email())
    u.save()
    return http.HttpResponseRedirect('/user/settings')

  # Regular GET.
  current_user = users.get_current_user()
  u = models.user_test.User.get_or_insert(current_user.user_id(),
      email=current_user.email())
  tests = db.Query(models.user_test.Test)
  tests.filter('user', u)
  tests.order('created')
  if tests.count() == 0:
    tests = None

  params = {
    'api_key': current_user.user_id(),
    'tests': tests,
    'csrf_token': request.session.get('csrf_token')
  }
  return util.Render(request, 'user_settings.html', params)

@decorators.login_required
@decorators.provide_check_csrf
def TestCreate(request):
  return TestEdit(request, None)

_MAX_SANDBOXID = 18446744073709551616L
_MAX_SANDBOXID_LEN = 15
def _get_random_sandboxid():
  # Use the system (hardware-based) random number generator if it exists.
  if hasattr(random, 'SystemRandom'):
    randrange = random.SystemRandom().randrange
  else:
    randrange = random.randrange
  sanboxid_md5 = hashlib.md5('%s' % (randrange(0, _MAX_SANDBOXID)))
  sanboxid = sanboxid_md5.hexdigest()[0:_MAX_SANDBOXID_LEN]
  return sanboxid

@decorators.login_required
@decorators.provide_check_csrf
def TestEdit(request, key):
  test = None
  error_msg = None
  current_user = users.get_current_user()

  # Key means this is an edit.
  if key:
    test = models.user_test.Test.get_mem(key)
    if (test.user.key().name() != current_user.user_id() and not
        users.is_current_user_admin()):
      return http.HttpResponse('You cannot mess with this test duder.')

  if request.POST:
    user = models.user_test.User.get_by_key_name(current_user.user_id())

    try:
      if test:
        test.name = request.POST.get('name')
        test.url = request.POST.get('url')
        test.description = request.POST.get('description')
        test.sandboxid = request.POST.get('sandboxid')
      else:
        test = models.user_test.Test(user=user, name=request.POST.get('name'),
                                     url=request.POST.get('url'),
                                     description=request.POST.get('description'),
                                     sandboxid=request.POST.get('sandboxid'))
      test.save()
      test.add_memcache()
      return http.HttpResponseRedirect('/user/settings')

    # Note: that syntax totally didn't work in prod app engine.
    #except datastore_errors.BadValueError as strerror:
    except datastore_errors.BadValueError, e:
      error_msg = e
      test = {
        'name': request.POST.get('name'),
        'url': request.POST.get('url'),
        'description': request.POST.get('description'),
        'sandboxid': request.POST.get('sandboxid')
      }
    except:
      error_msg = 'Something did not quite work there, very sorry'
      test = {
        'name': request.POST.get('name'),
        'url': request.POST.get('url'),
        'description': request.POST.get('description'),
        'sandboxid': request.POST.get('sandboxid')
      }

  params = {
    'test': test,
    'sandboxid': _get_random_sandboxid(),
    'max_sandboxid_len': _MAX_SANDBOXID_LEN,
    'error_msg': error_msg,
    'csrf_token': request.session.get('csrf_token')
  }
  return util.Render(request, 'user_test_form.html', params)

@decorators.login_required
def RawTestData(request, key):
  current_user = users.get_current_user()

  if not key:
    return http.HttpResponse('No key.')

  test = models.user_test.Test.get_mem(key)
  if (test.user.key().name() != current_user.user_id() and not
      users.is_current_user_admin()):
    return http.HttpResponse('You cannot mess with this test duder.')

  fields = request.GET.get('f')
  if fields:
    test_keys = fields.split(',')
  else:
    test_keys = test.test_keys

  result_parents = db.Query(models.result.ResultParent)
  result_parents.filter('category', test.get_memcache_keyname())
  result_parents.order('created')
  lines = []
  for result_parent in result_parents:
    line = [
      '"%s"' % result_parent.created,
      '"%s"' % result_parent.user_agent.string,
      '"%s"' % result_parent.user_agent.family,
      '"%s"' % result_parent.user_agent.v1,
      '"%s"' % result_parent.user_agent.v2,
      '"%s"' % result_parent.user_agent.v3]

    result_times_dict = result_parent.GetResults()
    for test_key in test_keys:
      line.append('"%s"' % result_times_dict[test_key])

    lines.append(line)

  headers = ['"Created"', '"UA String"', '"UA Family"', '"v1"', '"v2"', '"v3"']
  for test_key in test_keys:
    headers.append('"%s"' % test_key)

  line_delim = '\n'
  out = ','.join(headers) + line_delim
  for line in lines:
    out += ','.join(line) + line_delim
  #return http.HttpResponse(out)
  return http.HttpResponse(out, mimetype='text/csv')

def TestStatsTable(request, key):
  test = models.user_test.Test.get_mem(key)
  if not test:
    msg = 'No test was found with test_key %s.' % key
    logging.info(msg)
    return http.HttpResponseServerError(msg)

  output = request.GET.get('o', 'html')
  if output not in ['html', 'pickle', 'xhr', 'csv',
                    'gviz', 'gviz_data', 'gviz_timeline_data']:
    return http.HttpResponse('Invalid output specified')

  fields = request.GET.get('f')
  if fields:
    test_keys = fields.split(',')
  else:
    test_keys = test.test_keys
  test_set = test.get_test_set_from_test_keys(test_keys)

  stats_table = util.GetStats(request, test_set, output)

  if output in ('xhr', 'pickle', 'csv',
                'gviz', 'gviz_data', 'gviz_timeline_data'):
    return http.HttpResponse(stats_table)

  simple_layout = request.GET.get('layout') == 'simple'
  params = {
    'hide_nav': simple_layout,
    'hide_footer': simple_layout,
    'test': test,
    'stats_table': stats_table
  }
  return util.Render(request, 'user_test_table.html', params)

@decorators.admin_required
def UpdateSchema(request):
  tests = db.Query(models.user_test.Test)
  for test in tests:
    if not hasattr(test, 'sandboxid') or test.sandboxid is None:
      setattr(test, 'sandboxid', str(_get_random_sandboxid()))
      test.save()
  return http.HttpResponse('All done')

@decorators.admin_required
def TestView(request, key):
  """Experimental hosted mode for user tests."""
  test = models.user_test.Test.get_mem(key)

  #TODO(elsigh): Remove this.
  #mirror.DEBUG = True
  #mirror.EXPIRATION_DELTA_SECONDS = 1
  #mirror.EXPIRATION_RECENT_URLS_SECONDS = 1

  mirrored_content = None
  if not request.GET.get('sc'):
    mirrored_content = test.get_mirrored_content()
    mirrored_content = None
    #logging.info('mc from memcache: %s' % mirrored_content)
  if mirrored_content is None:
    #logging.info('store w/ key: %s' % test.get_memcache_keyname())
    mirrored_content = mirror.MirroredContent.fetch_and_store(
        key_name=test.get_memcache_keyname(),
        base_url=test.get_base_url(),
        translated_address=request.get_full_path(),
        mirrored_url=test.url)
  params = {
    'mirrored_content': mirrored_content
  }
  return util.Render(request, 'user_test.html', params)

@decorators.admin_required
def Test(request, key):
  """Loads the test frameset for a category."""
  test = models.user_test.Test.get_mem(key)
  params = {
    'category': test.get_memcache_keyname(),
    'page_title': '%s' % test.name,
    'continue': request.GET.get('continue', ''),
    'autorun': '',
    'testurl': '',
    'test': test,
    'test_page': test.url
  }
  #return shortcuts.render_to_response('test_frameset.html', params)
  return util.Render(request, 'test_frameset.html', params)

@decorators.provide_csrf
def BeaconJs(request, key):
  test = models.user_test.Test.get_mem(key)
  if not test:
    return http.HttpResponseServerError('No test key sent or no tests match.')

  # TODO(elsigh): Is referer check enough prevent abuse? IP-based better?
  do_referer_check = False
  if do_referer_check:
    referer = request.META.get('HTTP_REFERER', None)
    if referer is None:
      msg = 'No referer, no beacon.'
      logging.info(msg)
      return http.HttpResponseServerError(msg)
    if not re.match(test.url, referer):
      msg = 'Referer check failed.'
      logging.info(msg)
      return http.HttpResponseServerError(msg)

  params = {
    'test_key': test.key(),
    'csrf_token': request.session.get('csrf_token'),
    'server': util.GetServer(request),
  }

  # Does the user want a callback?
  callback = request.GET.get('callback')
  if callback:
    params['callback'] = callback

  response = shortcuts.render_to_response('user_test_beacon.js', params,
                                          mimetype='text/javascript')

  # Add on a P3P header so that the provide_csrf will work as a third-party
  # cookie for IE security happiness.
  # Reference: http://www.w3.org/P3P/
  # To be fair, I copied this example string from a blog and it works in IE.
  # I did not spend the 6 hours it looks like it would take to read this
  # documentation nor did I spend $39 for the P3PEdit service ;0
  # So I'm not sure that this string honestly represents our cookie policy.
  response['P3P'] = ('CP="NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM '
                     'NAV INT STA"')

  return response
<commit_msg>pass sandboxid<commit_after>#!/usr/bin/python2.5
#
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""User handlers and functions."""

__author__ = 'elsigh@google.com (Lindsey Simon)'

import hashlib
import logging
import random
import re

from google.appengine.api import users
from google.appengine.api import datastore_errors
from google.appengine.ext import deferred
from google.appengine.ext import db

import django
from django import http
from django import shortcuts
from django.template import loader, Context

import models.user_test
from base import decorators
from base import util

#from third_party.mirorrr import mirror

from django.template import add_to_builtins
add_to_builtins('base.custom_filters')

import settings

def TestHowto(request):
  params = {}
  return util.Render(request, 'user_test_howto.html', params)

@decorators.login_required
def Settings(request):
  logging.info('Settings start.')
  if request.POST:
    current_user = users.get_current_user()
    u = models.user_test.User.get_or_insert(current_user.user_id())
    u.email = request.POST.get('email', current_user.email())
    u.save()
    return http.HttpResponseRedirect('/user/settings')

  # Regular GET.
  current_user = users.get_current_user()
  u = models.user_test.User.get_or_insert(current_user.user_id(),
      email=current_user.email())
  tests = db.Query(models.user_test.Test)
  tests.filter('user', u)
  tests.order('created')
  if tests.count() == 0:
    tests = None

  params = {
    'api_key': current_user.user_id(),
    'tests': tests,
    'csrf_token': request.session.get('csrf_token')
  }
  return util.Render(request, 'user_settings.html', params)

@decorators.login_required
@decorators.provide_check_csrf
def TestCreate(request):
  return TestEdit(request, None)

_MAX_SANDBOXID = 18446744073709551616L
_MAX_SANDBOXID_LEN = 15
def _get_random_sandboxid():
  # Use the system (hardware-based) random number generator if it exists.
  if hasattr(random, 'SystemRandom'):
    randrange = random.SystemRandom().randrange
  else:
    randrange = random.randrange
  sanboxid_md5 = hashlib.md5('%s' % (randrange(0, _MAX_SANDBOXID)))
  sanboxid = sanboxid_md5.hexdigest()[0:_MAX_SANDBOXID_LEN]
  return sanboxid

@decorators.login_required
@decorators.provide_check_csrf
def TestEdit(request, key):
  test = None
  error_msg = None
  current_user = users.get_current_user()

  # Key means this is an edit.
  if key:
    test = models.user_test.Test.get_mem(key)
    if (test.user.key().name() != current_user.user_id() and not
        users.is_current_user_admin()):
      return http.HttpResponse('You cannot mess with this test duder.')

  if request.POST:
    user = models.user_test.User.get_by_key_name(current_user.user_id())

    try:
      if test:
        test.name = request.POST.get('name')
        test.url = request.POST.get('url')
        test.description = request.POST.get('description')
        test.sandboxid = request.POST.get('sandboxid')
      else:
        test = models.user_test.Test(user=user, name=request.POST.get('name'),
                                     url=request.POST.get('url'),
                                     description=request.POST.get('description'),
                                     sandboxid=request.POST.get('sandboxid'))
      test.save()
      test.add_memcache()
      return http.HttpResponseRedirect('/user/settings')

    # Note: that syntax totally didn't work in prod app engine.
    #except datastore_errors.BadValueError as strerror:
    except datastore_errors.BadValueError, e:
      error_msg = e
      test = {
        'name': request.POST.get('name'),
        'url': request.POST.get('url'),
        'description': request.POST.get('description'),
        'sandboxid': request.POST.get('sandboxid')
      }
    except:
      error_msg = 'Something did not quite work there, very sorry'
      test = {
        'name': request.POST.get('name'),
        'url': request.POST.get('url'),
        'description': request.POST.get('description'),
        'sandboxid': request.POST.get('sandboxid')
      }

  params = {
    'test': test,
    'sandboxid': _get_random_sandboxid(),
    'max_sandboxid_len': _MAX_SANDBOXID_LEN,
    'error_msg': error_msg,
    'csrf_token': request.session.get('csrf_token')
  }
  return util.Render(request, 'user_test_form.html', params)

@decorators.login_required
def RawTestData(request, key):
  current_user = users.get_current_user()

  if not key:
    return http.HttpResponse('No key.')

  test = models.user_test.Test.get_mem(key)
  if (test.user.key().name() != current_user.user_id() and not
      users.is_current_user_admin()):
    return http.HttpResponse('You cannot mess with this test duder.')

  fields = request.GET.get('f')
  if fields:
    test_keys = fields.split(',')
  else:
    test_keys = test.test_keys

  result_parents = db.Query(models.result.ResultParent)
  result_parents.filter('category', test.get_memcache_keyname())
  result_parents.order('created')
  lines = []
  for result_parent in result_parents:
    line = [
      '"%s"' % result_parent.created,
      '"%s"' % result_parent.user_agent.string,
      '"%s"' % result_parent.user_agent.family,
      '"%s"' % result_parent.user_agent.v1,
      '"%s"' % result_parent.user_agent.v2,
      '"%s"' % result_parent.user_agent.v3]

    result_times_dict = result_parent.GetResults()
    for test_key in test_keys:
      line.append('"%s"' % result_times_dict[test_key])

    lines.append(line)

  headers = ['"Created"', '"UA String"', '"UA Family"', '"v1"', '"v2"', '"v3"']
  for test_key in test_keys:
    headers.append('"%s"' % test_key)

  line_delim = '\n'
  out = ','.join(headers) + line_delim
  for line in lines:
    out += ','.join(line) + line_delim
  #return http.HttpResponse(out)
  return http.HttpResponse(out, mimetype='text/csv')

def TestStatsTable(request, key):
  test = models.user_test.Test.get_mem(key)
  if not test:
    msg = 'No test was found with test_key %s.' % key
    logging.info(msg)
    return http.HttpResponseServerError(msg)

  output = request.GET.get('o', 'html')
  if output not in ['html', 'pickle', 'xhr', 'csv',
                    'gviz', 'gviz_data', 'gviz_timeline_data']:
    return http.HttpResponse('Invalid output specified')

  fields = request.GET.get('f')
  if fields:
    test_keys = fields.split(',')
  else:
    test_keys = test.test_keys
  test_set = test.get_test_set_from_test_keys(test_keys)

  stats_table = util.GetStats(request, test_set, output)

  if output in ('xhr', 'pickle', 'csv',
                'gviz', 'gviz_data', 'gviz_timeline_data'):
    return http.HttpResponse(stats_table)

  simple_layout = request.GET.get('layout') == 'simple'
  params = {
    'hide_nav': simple_layout,
    'hide_footer': simple_layout,
    'test': test,
    'stats_table': stats_table
  }
  return util.Render(request, 'user_test_table.html', params)

@decorators.admin_required
def UpdateSchema(request):
  tests = db.Query(models.user_test.Test)
  for test in tests:
    if not hasattr(test, 'sandboxid') or test.sandboxid is None:
      setattr(test, 'sandboxid', str(_get_random_sandboxid()))
      test.save()
  return http.HttpResponse('All done')

@decorators.admin_required
def TestView(request, key):
  """Experimental hosted mode for user tests."""
  test = models.user_test.Test.get_mem(key)

  #TODO(elsigh): Remove this.
  #mirror.DEBUG = True
  #mirror.EXPIRATION_DELTA_SECONDS = 1
  #mirror.EXPIRATION_RECENT_URLS_SECONDS = 1

  mirrored_content = None
  if not request.GET.get('sc'):
    mirrored_content = test.get_mirrored_content()
    mirrored_content = None
    #logging.info('mc from memcache: %s' % mirrored_content)
  if mirrored_content is None:
    #logging.info('store w/ key: %s' % test.get_memcache_keyname())
    mirrored_content = mirror.MirroredContent.fetch_and_store(
        key_name=test.get_memcache_keyname(),
        base_url=test.get_base_url(),
        translated_address=request.get_full_path(),
        mirrored_url=test.url)
  params = {
    'mirrored_content': mirrored_content
  }
  return util.Render(request, 'user_test.html', params)

@decorators.admin_required
def Test(request, key):
  """Loads the test frameset for a category."""
  test = models.user_test.Test.get_mem(key)
  params = {
    'category': test.get_memcache_keyname(),
    'page_title': '%s' % test.name,
    'continue': request.GET.get('continue', ''),
    'autorun': '',
    'testurl': '',
    'test': test,
    'test_page': test.url
  }
  #return shortcuts.render_to_response('test_frameset.html', params)
  return util.Render(request, 'test_frameset.html', params)

@decorators.provide_csrf
def BeaconJs(request, key):
  test = models.user_test.Test.get_mem(key)
  if not test:
    return http.HttpResponseServerError('No test key sent or no tests match.')

  # TODO(elsigh): Is referer check enough prevent abuse? IP-based better?
  do_referer_check = False
  if do_referer_check:
    referer = request.META.get('HTTP_REFERER', None)
    if referer is None:
      msg = 'No referer, no beacon.'
      logging.info(msg)
      return http.HttpResponseServerError(msg)
    if not re.match(test.url, referer):
      msg = 'Referer check failed.'
      logging.info(msg)
      return http.HttpResponseServerError(msg)

  params = {
    'test_key': test.key(),
    'csrf_token': request.session.get('csrf_token'),
    'server': util.GetServer(request),
    'sandboxid': request.GET.get('sandboxid', ''),
  }

  # Does the user want a callback?
  callback = request.GET.get('callback')
  if callback:
    params['callback'] = callback

  response = shortcuts.render_to_response('user_test_beacon.js', params,
                                          mimetype='text/javascript')

  # Add on a P3P header so that the provide_csrf will work as a third-party
  # cookie for IE security happiness.
  # Reference: http://www.w3.org/P3P/
  # To be fair, I copied this example string from a blog and it works in IE.
  # I did not spend the 6 hours it looks like it would take to read this
  # documentation nor did I spend $39 for the P3PEdit service ;0
  # So I'm not sure that this string honestly represents our cookie policy.
  response['P3P'] = ('CP="NOI DSP COR NID ADM DEV PSA OUR IND UNI PUR COM '
                     'NAV INT STA"')

  return response
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''A simple XML pretty printer. It formats XML with good indentiation.'''
# Copyright (c) 2013 by Pablo Martín <goinnn@gmail.com>
#
# This software is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this software.  If not, see <http://www.gnu.org/licenses/>.

# This plugin originally was in this repository:
# <https://github.com/goinnn/Kate-plugins/blob/master/kate_plugins/xhtml_plugins/xml_plugins.py>

import codecs
import kate
import os
import re

from PyKDE4.kdecore import i18n
from PyQt4 import uic
from PyQt4.QtGui import QWidget

from libkatepate.errors import showError

from xml.dom import minidom
from xml.parsers.expat import ExpatError

KATE_ACTIONS = {'togglePrettyXMLFormat': {'text': 'Pretty XML',
                                          'shortcut': 'Ctrl+Alt+X',
                                          'menu': 'XML', 'icon': None},
                }

KATE_CONFIG = {'name': 'xml_pretty',
               'fullName': 'XML Pretty',
               'icon': 'text-xml'}

_CONFIG_UI = 'xml_pretty.ui'
_INDENT_CFG = 'XMLPretty:indentXXX'
_NEWL_CFG = 'XMLPretty:newlXXX'

DEFAULT_INDENT = '\\t'
DEFAULT_NEWL = '\\n'

encoding_pattern = re.compile("<\?xml[\w =\"\.]*encoding=[\"']([\w-]+)[\"'][\w =\"\.]*\?>")

@kate.action(**KATE_ACTIONS['togglePrettyXMLFormat'])
#@check_constraints
#@has_selection(True)
def togglePrettyXMLFormat():
    """Pretty format of a XML code"""
    # TODO Use decorators to apply constraints
    document = kate.activeDocument()
    view = document.activeView()
    if not view.selection():
        showError(i18n('Please select XML text and press: %s', KATE_ACTIONS['togglePrettyXMLFormat']['shortcut']))
    else:
        try:
            encoding = 'utf-8'
            source = view.selectionText()
            m = encoding_pattern.match(source)
            if m:
                encoding = m.groups()[0]
            target = minidom.parseString(source)
            unicode_escape = codecs.getdecoder('unicode_escape')
            indent = unicode_escape(kate.configuration.get(_INDENT_CFG, DEFAULT_INDENT))[0]
            newl = unicode_escape(kate.configuration.get(_NEWL_CFG, DEFAULT_NEWL))[0]
            xml_pretty = target.toprettyxml(indent=indent, newl=newl, encoding=encoding).decode(encoding)
            xml_pretty = newl.join([line for line in xml_pretty.split(newl) if line.replace(' ', '').replace(indent, '')])
            document.replaceText(view.selectionRange(), xml_pretty)
        except (ExpatError, LookupError) as e:
            showError(i18n('The selected text is not valid XML: %s', e.message))

class ConfigWidget(QWidget):
    """Configuration widget for this plugin."""

    def __init__(self, parent=None, name=None):
        super(ConfigWidget, self).__init__(parent)

        # Set up the user interface from Designer.
        uic.loadUi(os.path.join(os.path.dirname(__file__), _CONFIG_UI), self)
        self.reset()

    def apply(self):
        kate.configuration[_NEWL_CFG] = self.newl.text()
        kate.configuration[_INDENT_CFG] = self.indent.text()
        kate.configuration.save()

    def reset(self):
        self.defaults()
        if _INDENT_CFG in kate.configuration:
            self.indent.setText(kate.configuration[_INDENT_CFG])
        if _NEWL_CFG in kate.configuration:
            self.newl.setText(kate.configuration[_NEWL_CFG])

    def defaults(self):
        self.indent.setText(DEFAULT_INDENT)
        self.newl.setText(DEFAULT_NEWL)

class ConfigPage(kate.Kate.PluginConfigPage, QWidget):
    """Kate configuration page for this plugin."""
    def __init__(self, parent=None, name=None):
        super(ConfigPage, self).__init__(parent, name)
        self.widget = ConfigWidget(parent)
        lo = parent.layout()
        lo.addWidget(self.widget)

    def apply(self):
        self.widget.apply()

    def reset(self):
        self.widget.reset()

    def defaults(self):
        self.widget.defaults()
        self.changed.emit()

@kate.configPage(**KATE_CONFIG)
def configPage(parent=None, name=None):
    return ConfigPage(parent, name)

# kate: space-indent on; indent-width 4;
<commit_msg>i18n: Must use KDE i18n's argument substitution.<commit_after># -*- coding: utf-8 -*-
'''A simple XML pretty printer. It formats XML with good indentiation.'''
# Copyright (c) 2013 by Pablo Martín <goinnn@gmail.com>
#
# This software is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this software.  If not, see <http://www.gnu.org/licenses/>.

# This plugin originally was in this repository:
# <https://github.com/goinnn/Kate-plugins/blob/master/kate_plugins/xhtml_plugins/xml_plugins.py>

import codecs
import kate
import os
import re

from PyKDE4.kdecore import i18n
from PyQt4 import uic
from PyQt4.QtGui import QWidget

from libkatepate.errors import showError

from xml.dom import minidom
from xml.parsers.expat import ExpatError

KATE_ACTIONS = {'togglePrettyXMLFormat': {'text': 'Pretty XML',
                                          'shortcut': 'Ctrl+Alt+X',
                                          'menu': 'XML', 'icon': None},
                }

KATE_CONFIG = {'name': 'xml_pretty',
               'fullName': 'XML Pretty',
               'icon': 'text-xml'}

_CONFIG_UI = 'xml_pretty.ui'
_INDENT_CFG = 'XMLPretty:indentXXX'
_NEWL_CFG = 'XMLPretty:newlXXX'

DEFAULT_INDENT = '\\t'
DEFAULT_NEWL = '\\n'

encoding_pattern = re.compile("<\?xml[\w =\"\.]*encoding=[\"']([\w-]+)[\"'][\w =\"\.]*\?>")

@kate.action(**KATE_ACTIONS['togglePrettyXMLFormat'])
#@check_constraints
#@has_selection(True)
def togglePrettyXMLFormat():
    """Pretty format of a XML code"""
    # TODO Use decorators to apply constraints
    document = kate.activeDocument()
    view = document.activeView()
    if not view.selection():
        showError(i18n('Please select XML text and press: %1', KATE_ACTIONS['togglePrettyXMLFormat']['shortcut']))
    else:
        try:
            encoding = 'utf-8'
            source = view.selectionText()
            m = encoding_pattern.match(source)
            if m:
                encoding = m.groups()[0]
            target = minidom.parseString(source)
            unicode_escape = codecs.getdecoder('unicode_escape')
            indent = unicode_escape(kate.configuration.get(_INDENT_CFG, DEFAULT_INDENT))[0]
            newl = unicode_escape(kate.configuration.get(_NEWL_CFG, DEFAULT_NEWL))[0]
            xml_pretty = target.toprettyxml(indent=indent, newl=newl, encoding=encoding).decode(encoding)
            xml_pretty = newl.join([line for line in xml_pretty.split(newl) if line.replace(' ', '').replace(indent, '')])
            document.replaceText(view.selectionRange(), xml_pretty)
        except (ExpatError, LookupError) as e:
            showError(i18n('The selected text is not valid XML: %1', e.message))

class ConfigWidget(QWidget):
    """Configuration widget for this plugin."""

    def __init__(self, parent=None, name=None):
        super(ConfigWidget, self).__init__(parent)

        # Set up the user interface from Designer.
        uic.loadUi(os.path.join(os.path.dirname(__file__), _CONFIG_UI), self)
        self.reset()

    def apply(self):
        kate.configuration[_NEWL_CFG] = self.newl.text()
        kate.configuration[_INDENT_CFG] = self.indent.text()
        kate.configuration.save()

    def reset(self):
        self.defaults()
        if _INDENT_CFG in kate.configuration:
            self.indent.setText(kate.configuration[_INDENT_CFG])
        if _NEWL_CFG in kate.configuration:
            self.newl.setText(kate.configuration[_NEWL_CFG])

    def defaults(self):
        self.indent.setText(DEFAULT_INDENT)
        self.newl.setText(DEFAULT_NEWL)

class ConfigPage(kate.Kate.PluginConfigPage, QWidget):
    """Kate configuration page for this plugin."""
    def __init__(self, parent=None, name=None):
        super(ConfigPage, self).__init__(parent, name)
        self.widget = ConfigWidget(parent)
        lo = parent.layout()
        lo.addWidget(self.widget)

    def apply(self):
        self.widget.apply()

    def reset(self):
        self.widget.reset()

    def defaults(self):
        self.widget.defaults()
        self.changed.emit()

@kate.configPage(**KATE_CONFIG)
def configPage(parent=None, name=None):
    return ConfigPage(parent, name)

# kate: space-indent on; indent-width 4;
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2010-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 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>
#
##############################################################################

import base64
import re
from openerp import tools

from openerp.osv import osv
from openerp.osv import fields
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _

# main mako-like expression pattern
EXPRESSION_PATTERN = re.compile('(\$\{.+?\})')

class mail_compose_message(osv.TransientModel):
    """ Generic message composition wizard. You may inherit from this wizard
        at model and view levels to provide specific features.

        The behavior of the wizard depends on the composition_mode field:
        - 'reply': reply to a previous message. The wizard is pre-populated
            via ``get_message_data``.
        - 'comment': new post on a record. The wizard is pre-populated via
            ``get_record_data``
        - 'mass_mail': wizard in mass mailing mode where the mail details can
            contain template placeholders that will be merged with actual data
            before being sent to each recipient.
    """
    _name = 'mail.compose.message'
    _inherit = 'mail.message'
    _description = 'Email composition wizard'
    _log_access = True

    def default_get(self, cr, uid, fields, context=None):
        """ Handle composition mode. Some details about context keys:
            - comment: default mode, model and ID of a record the user comments
                - default_model or active_model
                - default_res_id or active_id
            - reply: active_id of a message the user replies to
                - default_parent_id or message_id or active_id: ID of the
                    mail.message we reply to
                - message.res_model or default_model
                - message.res_id or default_res_id
            - mass_mail: model and IDs of records the user mass-mails
                - active_ids: record IDs
                - default_model or active_model
        """
        if context is None:
            context = {}
        result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)

        # get some important values from context
        composition_mode = context.get('default_composition_mode', context.get('mail.compose.message.mode'))
        model = context.get('default_model', context.get('active_model'))
        res_id = context.get('default_res_id', context.get('active_id'))
        message_id = context.get('default_parent_id', context.get('message_id', context.get('active_id')))
        active_ids = context.get('active_ids')

        # get default values according to the composition mode
        if composition_mode == 'reply':
            vals = self.get_message_data(cr, uid, message_id, context=context)
        elif composition_mode == 'comment' and model and res_id:
            vals = self.get_record_data(cr, uid, model, res_id, context=context)
        elif composition_mode == 'mass_mail' and model and active_ids:
            vals = {'model': model, 'res_id': res_id}
        else:
            vals = {'model': model, 'res_id': res_id}
        if composition_mode:
            vals['composition_mode'] = composition_mode

        for field in vals:
            if field in fields:
                result[field] = vals[field]

        # TDE HACK: as mailboxes used default_model='res.users' and default_res_id=uid
        # (because of lack of an accessible pid), creating a message on its own
        # profile may crash (res_users does not allow writing on it)
        # Posting on its own profile works (res_users redirect to res_partner)
        # but when creating the mail.message to create the mail.compose.message
        # access rights issues may rise
        # We therefore directly change the model and res_id
        if result.get('model') == 'res.users' and result.get('res_id') == uid:
            result['model'] = 'res.partner'
            result['res_id'] = self.pool.get('res.users').browse(cr, uid, uid).partner_id.id
        return result

    def _get_composition_mode_selection(self, cr, uid, context=None):
        return [('comment', 'Comment a document'), ('reply', 'Reply to a message'), ('mass_mail', 'Mass mailing')]

    _columns = {
        'composition_mode': fields.selection(
            lambda s, *a, **k: s._get_composition_mode_selection(*a, **k),
            string='Composition mode'),
        'partner_ids': fields.many2many('res.partner',
            'mail_compose_message_res_partner_rel',
            'wizard_id', 'partner_id', 'Additional contacts'),
        'notify': fields.boolean('Notify followers',
            help='Notify followers of the documents.'),
        'post': fields.boolean('Post',
            help='Post a copy of the message on the document communication history.'),
        'same_thread': fields.boolean('Reply in the document',
            help='Replies to the messages will go into the selected document.'),
        'attachment_ids': fields.many2many('ir.attachment',
            'mail_compose_message_ir_attachments_rel',
            'wizard_id', 'attachment_id', 'Attachments'),
        'filter_id': fields.many2one('ir.filters', 'Filters'),
    }

    _defaults = {
        'composition_mode': 'comment',
        'body': lambda self, cr, uid, ctx={}: '',
        'subject': lambda self, cr, uid, ctx={}: False,
        'partner_ids': lambda self, cr, uid, ctx={}: [],
        'notify': lambda self, cr, uid, ctx={}: False,
        'post': lambda self, cr, uid, ctx={}: True,
        'same_thread': lambda self, cr, uid, ctx={}: True,
    }

    def _notify(self, cr, uid, newid, context=None):
        """ Override specific notify method of mail.message, because we do
            not want that feature in the wizard. """
        return

    def get_record_data(self, cr, uid, model, res_id, context=None):
        """ Returns a defaults-like dict with initial values for the composition
            wizard when sending an email related to the document record
            identified by ``model`` and ``res_id``.

            :param str model: model name of the document record this mail is
                related to.
            :param int res_id: id of the document record this mail is related to
        """
        doc_name_get = self.pool.get(model).name_get(cr, uid, [res_id], context=context)
        if doc_name_get:
            record_name = doc_name_get[0][1]
        else:
            record_name = False
        return {'model': model, 'res_id': res_id, 'record_name': record_name}

    def get_message_data(self, cr, uid, message_id, context=None):
        """ Returns a defaults-like dict with initial values for the composition
            wizard when replying to the given message (e.g. including the quote
            of the initial message, and the correct recipients).

            :param int message_id: id of the mail.message to which the user
                is replying.
        """
        if not message_id:
            return {}
        if context is None:
            context = {}
        message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context)

        # create subject
        re_prefix = _('Re:')
        reply_subject = tools.ustr(message_data.subject or '')
        if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)) and message_data.subject:
            reply_subject = "%s %s" % (re_prefix, reply_subject)
        # get partner_ids from original message
        partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else []
        partner_ids += context.get('default_partner_ids', [])

        # update the result
        result = {
            'record_name': message_data.record_name,
            'model': message_data.model,
            'res_id': message_data.res_id,
            'parent_id': message_data.id,
            'subject': reply_subject,
            'partner_ids': partner_ids,
        }
        return result

    #------------------------------------------------------
    # Wizard validation and send
    #------------------------------------------------------

    def send_mail(self, cr, uid, ids, context=None):
        """ Process the wizard content and proceed with sending the related
            email(s), rendering any template patterns on the fly if needed. """
        if context is None:
            context = {}
        active_ids = context.get('active_ids')

        for wizard in self.browse(cr, uid, ids, context=context):
            mass_mail_mode = wizard.composition_mode == 'mass_mail'
            active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread')

            # wizard works in batch mode: [res_id] or active_ids
            res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id]
            for res_id in res_ids:
                # default values, according to the wizard options
                post_values = {
                    'subject': wizard.subject,
                    'body': wizard.body,
                    'parent_id': wizard.parent_id and wizard.parent_id.id,
                    'partner_ids': [(4, partner.id) for partner in wizard.partner_ids],
                    'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids],
                }
                # mass mailing: render and override default values
                if mass_mail_mode and wizard.model:
                    email_dict = self.render_message(cr, uid, wizard, res_id, context=context)
                    new_partner_ids = email_dict.pop('partner_ids', [])
                    post_values['partner_ids'] += [(4, partner_id) for partner_id in new_partner_ids]
                    new_attachments = email_dict.pop('attachments', [])
                    post_values['attachments'] += new_attachments
                    post_values.update(email_dict)
                    # email_from: mass mailing only can specify another email_from
                    if email_dict.get('email_from'):
                        post_values['email_from'] = email_dict.pop('email_from')
                    # replies redirection: mass mailing only
                    if not wizard.same_thread:
                        post_values['reply_to'] = email_dict.pop('reply_to')
                # automatically subscribe recipients if asked to
                if context.get('mail_post_autofollow') and wizard.model and post_values.get('partner_ids'):
                    active_model_pool.message_subscribe(cr, uid, [res_id], [item[1] for item in post_values.get('partner_ids')], context=context)
                # post the message
                if mass_mail_mode and not wizard.post:
                    post_values['recipient_ids'] = post_values.pop('partner_ids')
                    self.pool.get('mail.mail').create(cr, uid, post_values, context=context)
                else:
                    subtype = False
                    if wizard.notify:
                        subtype = 'mail.mt_comment'
                    msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
                    # mass_mailing, post without notify: notify specific partners
                    if mass_mail_mode and not wizard.notify and post_values['partner_ids']:
                        self.pool.get('mail.notification')._notify(cr, uid, msg_id, [item[1] for item in post_values['partner_ids']], context=context)

        return {'type': 'ir.actions.act_window_close'}

    def render_message(self, cr, uid, wizard, res_id, context=None):
        """ Generate an email from the template for given (wizard.model, res_id)
            pair. This method is meant to be inherited by email_template that
            will produce a more complete dictionary. """
        return {
            'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context),
            'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context),
            'email_from': self.render_template(cr, uid, wizard.email_from, wizard.model, res_id, context),
            'reply_to': self.render_template(cr, uid, wizard.reply_to, wizard.model, res_id, context),
        }

    def render_template(self, cr, uid, template, model, res_id, context=None):
        """ Render the given template text, replace mako-like expressions ``${expr}``
            with the result of evaluating these expressions with an evaluation context
            containing:

                * ``user``: browse_record of the current user
                * ``object``: browse_record of the document record this mail is
                              related to
                * ``context``: the context passed to the mail composition wizard

            :param str template: the template text to render
            :param str model: model name of the document record this mail is related to.
            :param int res_id: id of the document record this mail is related to.
        """
        if context is None:
            context = {}

        def merge(match):
            exp = str(match.group()[2:-1]).strip()
            result = eval(exp, {
                'user': self.pool.get('res.users').browse(cr, uid, uid, context=context),
                'object': self.pool.get(model).browse(cr, uid, res_id, context=context),
                'context': dict(context),  # copy context to prevent side-effects of eval
                })
            return result and tools.ustr(result) or ''
        return template and EXPRESSION_PATTERN.sub(merge, template)
<commit_msg>[FIX] mail: compose wizard: fixed subtype.<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2010-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 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>
#
##############################################################################

import base64
import re
from openerp import tools

from openerp.osv import osv
from openerp.osv import fields
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _

# main mako-like expression pattern
EXPRESSION_PATTERN = re.compile('(\$\{.+?\})')

class mail_compose_message(osv.TransientModel):
    """ Generic message composition wizard. You may inherit from this wizard
        at model and view levels to provide specific features.

        The behavior of the wizard depends on the composition_mode field:
        - 'reply': reply to a previous message. The wizard is pre-populated
            via ``get_message_data``.
        - 'comment': new post on a record. The wizard is pre-populated via
            ``get_record_data``
        - 'mass_mail': wizard in mass mailing mode where the mail details can
            contain template placeholders that will be merged with actual data
            before being sent to each recipient.
    """
    _name = 'mail.compose.message'
    _inherit = 'mail.message'
    _description = 'Email composition wizard'
    _log_access = True

    def default_get(self, cr, uid, fields, context=None):
        """ Handle composition mode. Some details about context keys:
            - comment: default mode, model and ID of a record the user comments
                - default_model or active_model
                - default_res_id or active_id
            - reply: active_id of a message the user replies to
                - default_parent_id or message_id or active_id: ID of the
                    mail.message we reply to
                - message.res_model or default_model
                - message.res_id or default_res_id
            - mass_mail: model and IDs of records the user mass-mails
                - active_ids: record IDs
                - default_model or active_model
        """
        if context is None:
            context = {}
        result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)

        # get some important values from context
        composition_mode = context.get('default_composition_mode', context.get('mail.compose.message.mode'))
        model = context.get('default_model', context.get('active_model'))
        res_id = context.get('default_res_id', context.get('active_id'))
        message_id = context.get('default_parent_id', context.get('message_id', context.get('active_id')))
        active_ids = context.get('active_ids')

        # get default values according to the composition mode
        if composition_mode == 'reply':
            vals = self.get_message_data(cr, uid, message_id, context=context)
        elif composition_mode == 'comment' and model and res_id:
            vals = self.get_record_data(cr, uid, model, res_id, context=context)
        elif composition_mode == 'mass_mail' and model and active_ids:
            vals = {'model': model, 'res_id': res_id}
        else:
            vals = {'model': model, 'res_id': res_id}
        if composition_mode:
            vals['composition_mode'] = composition_mode

        for field in vals:
            if field in fields:
                result[field] = vals[field]

        # TDE HACK: as mailboxes used default_model='res.users' and default_res_id=uid
        # (because of lack of an accessible pid), creating a message on its own
        # profile may crash (res_users does not allow writing on it)
        # Posting on its own profile works (res_users redirect to res_partner)
        # but when creating the mail.message to create the mail.compose.message
        # access rights issues may rise
        # We therefore directly change the model and res_id
        if result.get('model') == 'res.users' and result.get('res_id') == uid:
            result['model'] = 'res.partner'
            result['res_id'] = self.pool.get('res.users').browse(cr, uid, uid).partner_id.id
        return result

    def _get_composition_mode_selection(self, cr, uid, context=None):
        return [('comment', 'Comment a document'), ('reply', 'Reply to a message'), ('mass_mail', 'Mass mailing')]

    _columns = {
        'composition_mode': fields.selection(
            lambda s, *a, **k: s._get_composition_mode_selection(*a, **k),
            string='Composition mode'),
        'partner_ids': fields.many2many('res.partner',
            'mail_compose_message_res_partner_rel',
            'wizard_id', 'partner_id', 'Additional contacts'),
        'notify': fields.boolean('Notify followers',
            help='Notify followers of the documents.'),
        'post': fields.boolean('Post',
            help='Post a copy of the message on the document communication history.'),
        'same_thread': fields.boolean('Reply in the document',
            help='Replies to the messages will go into the selected document.'),
        'attachment_ids': fields.many2many('ir.attachment',
            'mail_compose_message_ir_attachments_rel',
            'wizard_id', 'attachment_id', 'Attachments'),
        'filter_id': fields.many2one('ir.filters', 'Filters'),
    }

    _defaults = {
        'composition_mode': 'comment',
        'body': lambda self, cr, uid, ctx={}: '',
        'subject': lambda self, cr, uid, ctx={}: False,
        'partner_ids': lambda self, cr, uid, ctx={}: [],
        'notify': lambda self, cr, uid, ctx={}: False,
        'post': lambda self, cr, uid, ctx={}: True,
        'same_thread': lambda self, cr, uid, ctx={}: True,
    }

    def _notify(self, cr, uid, newid, context=None):
        """ Override specific notify method of mail.message, because we do
            not want that feature in the wizard. """
        return

    def get_record_data(self, cr, uid, model, res_id, context=None):
        """ Returns a defaults-like dict with initial values for the composition
            wizard when sending an email related to the document record
            identified by ``model`` and ``res_id``.

            :param str model: model name of the document record this mail is
                related to.
            :param int res_id: id of the document record this mail is related to
        """
        doc_name_get = self.pool.get(model).name_get(cr, uid, [res_id], context=context)
        if doc_name_get:
            record_name = doc_name_get[0][1]
        else:
            record_name = False
        return {'model': model, 'res_id': res_id, 'record_name': record_name}

    def get_message_data(self, cr, uid, message_id, context=None):
        """ Returns a defaults-like dict with initial values for the composition
            wizard when replying to the given message (e.g. including the quote
            of the initial message, and the correct recipients).

            :param int message_id: id of the mail.message to which the user
                is replying.
        """
        if not message_id:
            return {}
        if context is None:
            context = {}
        message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context)

        # create subject
        re_prefix = _('Re:')
        reply_subject = tools.ustr(message_data.subject or '')
        if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)) and message_data.subject:
            reply_subject = "%s %s" % (re_prefix, reply_subject)
        # get partner_ids from original message
        partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else []
        partner_ids += context.get('default_partner_ids', [])

        # update the result
        result = {
            'record_name': message_data.record_name,
            'model': message_data.model,
            'res_id': message_data.res_id,
            'parent_id': message_data.id,
            'subject': reply_subject,
            'partner_ids': partner_ids,
        }
        return result

    #------------------------------------------------------
    # Wizard validation and send
    #------------------------------------------------------

    def send_mail(self, cr, uid, ids, context=None):
        """ Process the wizard content and proceed with sending the related
            email(s), rendering any template patterns on the fly if needed. """
        if context is None:
            context = {}
        active_ids = context.get('active_ids')

        for wizard in self.browse(cr, uid, ids, context=context):
            mass_mail_mode = wizard.composition_mode == 'mass_mail'
            active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread')

            # wizard works in batch mode: [res_id] or active_ids
            res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id]
            for res_id in res_ids:
                # default values, according to the wizard options
                post_values = {
                    'subject': wizard.subject,
                    'body': wizard.body,
                    'parent_id': wizard.parent_id and wizard.parent_id.id,
                    'partner_ids': [(4, partner.id) for partner in wizard.partner_ids],
                    'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids],
                }
                # mass mailing: render and override default values
                if mass_mail_mode and wizard.model:
                    email_dict = self.render_message(cr, uid, wizard, res_id, context=context)
                    new_partner_ids = email_dict.pop('partner_ids', [])
                    post_values['partner_ids'] += [(4, partner_id) for partner_id in new_partner_ids]
                    new_attachments = email_dict.pop('attachments', [])
                    post_values['attachments'] += new_attachments
                    post_values.update(email_dict)
                    # email_from: mass mailing only can specify another email_from
                    if email_dict.get('email_from'):
                        post_values['email_from'] = email_dict.pop('email_from')
                    # replies redirection: mass mailing only
                    if not wizard.same_thread:
                        post_values['reply_to'] = email_dict.pop('reply_to')
                # automatically subscribe recipients if asked to
                if context.get('mail_post_autofollow') and wizard.model and post_values.get('partner_ids'):
                    active_model_pool.message_subscribe(cr, uid, [res_id], [item[1] for item in post_values.get('partner_ids')], context=context)
                # post the message
                if mass_mail_mode and not wizard.post:
                    post_values['recipient_ids'] = post_values.pop('partner_ids')
                    self.pool.get('mail.mail').create(cr, uid, post_values, context=context)
                else:
                    subtype = 'mail.mt_comment'
                    if mass_mail_mode and not wizard.notify:
                        subtype = False
                    msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values)
                    # mass_mailing, post without notify: notify specific partners
                    if mass_mail_mode and not wizard.notify and post_values['partner_ids']:
                        self.pool.get('mail.notification')._notify(cr, uid, msg_id, [item[1] for item in post_values['partner_ids']], context=context)

        return {'type': 'ir.actions.act_window_close'}

    def render_message(self, cr, uid, wizard, res_id, context=None):
        """ Generate an email from the template for given (wizard.model, res_id)
            pair. This method is meant to be inherited by email_template that
            will produce a more complete dictionary. """
        return {
            'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context),
            'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context),
            'email_from': self.render_template(cr, uid, wizard.email_from, wizard.model, res_id, context),
            'reply_to': self.render_template(cr, uid, wizard.reply_to, wizard.model, res_id, context),
        }

    def render_template(self, cr, uid, template, model, res_id, context=None):
        """ Render the given template text, replace mako-like expressions ``${expr}``
            with the result of evaluating these expressions with an evaluation context
            containing:

                * ``user``: browse_record of the current user
                * ``object``: browse_record of the document record this mail is
                              related to
                * ``context``: the context passed to the mail composition wizard

            :param str template: the template text to render
            :param str model: model name of the document record this mail is related to.
            :param int res_id: id of the document record this mail is related to.
        """
        if context is None:
            context = {}

        def merge(match):
            exp = str(match.group()[2:-1]).strip()
            result = eval(exp, {
                'user': self.pool.get('res.users').browse(cr, uid, uid, context=context),
                'object': self.pool.get(model).browse(cr, uid, res_id, context=context),
                'context': dict(context),  # copy context to prevent side-effects of eval
                })
            return result and tools.ustr(result) or ''
        return template and EXPRESSION_PATTERN.sub(merge, template)
<|endoftext|>