docs: 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)