Private function
    Insert a comment/review or remarkinto the database
    :param recID: record id
    :param uid: user id
    :param msg: comment body
    :param note: comment title
    :param score: review star score
    :param priority: remark priority #!FIXME
    :param editor_type: the kind of editor used to submit the comment: 'textarea', 'ckeditor'
    :param req: request object. If provided, email notification are sent after we reply to user request.
    :param reply_to: the id of the comment we are replying to with this inserted comment.
    :return: integer >0 representing id if successful, integer 0 if not

def query_add_comment_or_remark(reviews=0, recID=0, uid=-1, msg="",
                                note="", score=0, priority=0,
                                client_ip_address='', editor_type='textarea',
                                req=None, reply_to=None, attached_files=None):
    """
    Private function
    Insert a comment/review or remarkinto the database
    :param recID: record id
    :param uid: user id
    :param msg: comment body
    :param note: comment title
    :param score: review star score
    :param priority: remark priority #!FIXME
    :param editor_type: the kind of editor used to submit the comment: 'textarea', 'ckeditor'
    :param req: request object. If provided, email notification are sent after we reply to user request.
    :param reply_to: the id of the comment we are replying to with this inserted comment.
    :return: integer >0 representing id if successful, integer 0 if not
    """
    current_date = calculate_start_date('0d')
    # change utf-8 message into general unicode
    msg = msg.decode('utf-8')
    note = note.decode('utf-8')
    # change general unicode back to utf-8
    msg = msg.encode('utf-8')
    note = note.encode('utf-8')
    (restriction, round_name) = get_record_status(recID)
    if attached_files is None:
        attached_files = {}
    if reply_to and CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH >= 0:
        # Check that we have not reached max depth
        comment_ancestors = get_comment_ancestors(reply_to)
        if len(comment_ancestors) >= CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH:
            if CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH == 0:
                reply_to = None
            else:
                reply_to = comment_ancestors[
                    CFG_WEBCOMMENT_MAX_COMMENT_THREAD_DEPTH -
                    1]
        # Inherit restriction and group/round of 'parent'
        comment = query_get_comment(reply_to)
        if comment:
            (round_name, restriction) = comment[10:12]
    if editor_type == 'ckeditor':
        # Here we remove the line feeds introduced by CKEditor (they
        # have no meaning for the user) and replace the HTML line
        # breaks by linefeeds, so that we are close to an input that
        # would be done without the CKEditor. That's much better if a
        # reply to a comment is made with a browser that does not
        # support CKEditor.
        msg = msg.replace('\n', '').replace('\r', '')

        # We clean the quotes that could have been introduced by
        # CKEditor when clicking the 'quote' button, as well as those
        # that we have introduced when quoting the original message.
        # We can however not use directly '>>' chars to quote, as it
        # will be washed/fixed when calling tidy_html(): double-escape
        # all &gt; first, and use &gt;&gt;
        msg = msg.replace('&gt;', '&amp;gt;')
        msg = re.sub('^\s*<blockquote', '<br/> <blockquote', msg)
        msg = re.sub('<blockquote.*?>\s*<(p|div).*?>', '&gt;&gt;', msg)
        msg = re.sub('</(p|div)>\s*</blockquote>', '', msg)
        # Then definitely remove any blockquote, whatever it is
        msg = re.sub('<blockquote.*?>', '<div>', msg)
        msg = re.sub('</blockquote>', '</div>', msg)
        # Tidy up the HTML
        msg = tidy_html(msg)
        # We remove EOL that might have been introduced when tidying
        msg = msg.replace('\n', '').replace('\r', '')
        # Now that HTML has been cleaned, unescape &gt;
        msg = msg.replace('&gt;', '>')
        msg = msg.replace('&amp;gt;', '&gt;')
        msg = re.sub('<br .*?(/>)', '\n', msg)
        msg = msg.replace('&nbsp;', ' ')
        # In case additional <p> or <div> got inserted, interpret
        # these as new lines (with a sad trick to do it only once)
        # (note that it has been deactivated, as it is messing up
        # indentation with >>)
        #msg = msg.replace('</div><', '</div>\n<')
        #msg = msg.replace('</p><', '</p>\n<')

    query = """INSERT INTO "cmtRECORDCOMMENT" (id_bibrec,
                                           id_user,
                                           body,
                                           date_creation,
                                           star_score,
                                           nb_votes_total,
                                           title,
                                           round_name,
                                           restriction,
                                           "in_reply_to_id_cmtRECORDCOMMENT")
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
    params = (
        recID,
        uid,
        msg,
        current_date,
        score,
        0,
        note,
        round_name,
        restriction,
        reply_to or 0)
    res = run_sql(query, params)
    if res:
        new_comid = int(res)
        move_attached_files_to_storage(attached_files, recID, new_comid)
        parent_reply_order = run_sql(
            """SELECT reply_order_cached_data from "cmtRECORDCOMMENT" where id=%s""",
            (reply_to,
             ))
        if not parent_reply_order or parent_reply_order[0][0] is None:
            # This is not a reply, but a first 0-level comment
            parent_reply_order = ''
        else:
            parent_reply_order = parent_reply_order[0][0]
        run_sql(
            """UPDATE "cmtRECORDCOMMENT" SET reply_order_cached_data=%s WHERE id=%s""",
            (parent_reply_order +
             get_reply_order_cache_data(new_comid),
             new_comid))
        action_code = CFG_WEBCOMMENT_ACTION_CODE[
            reviews and 'ADD_REVIEW' or 'ADD_COMMENT']
        action_time = convert_datestruct_to_datetext(time.localtime())
        query2 = """INSERT INTO "cmtACTIONHISTORY"  ("id_cmtRECORDCOMMENT",
                     id_bibrec, id_user, client_host, action_time, action_code)
                    VALUES (%s, %s, %s, inet_aton(%s), %s, %s)"""
        params2 = (
            res,
            recID,
            uid,
            client_ip_address,
            action_time,
            action_code)
        run_sql(query2, params2)

        def notify_subscribers_callback(data):
            """
            Define a callback that retrieves subscribed users, and
            notify them by email.

            :param data: contains the necessary parameters in a tuple:
                         (recid, uid, comid, msg, note, score, editor_type, reviews)
            """
            recid, uid, comid, msg, note, score, editor_type, reviews = data
            # Email this comment to 'subscribers'
            (subscribers_emails1, subscribers_emails2) = \
                get_users_subscribed_to_discussion(recid)
            email_subscribers_about_new_comment(
                recid,
                reviews=reviews,
                emails1=subscribers_emails1,
                emails2=subscribers_emails2,
                comID=comid,
                msg=msg,
                note=note,
                score=score,
                editor_type=editor_type,
                uid=uid)

        # Register our callback to notify subscribed people after
        # having replied to our current user.
        data = (recID, uid, res, msg, note, score, editor_type, reviews)
        if req:
            req.register_cleanup(notify_subscribers_callback, data)
        else:
            notify_subscribers_callback(data)

    return int(res)