Ejemplo n.º 1
0
def setup_confluence(use_oauth=False):
    """setup_confluence Set up the Confluence class instance

    Reads in the confluence.conf configuration file, which contains the URL,
    username, and password (and/or OAUTH info).

    NOTE: For Confluence install version >= 7.9, can use OAUTH for
          authentication instead of username/password.

    Parameters
    ----------
    use_oauth : `bool`, optional
        Use the OAUTH authentication scheme?  [Default: False]

    Returns
    -------
    confluence : `atlassian.Confluence`
        Confluence class, initialized with credentials
    """
    # Read the setup
    setup = utils.read_ligmos_conffiles("confluenceSetup")

    # If we are using OAUTH, instantiate a Confluence object with it
    if use_oauth:
        s = requests.Session()
        s.headers["Authorization"] = f"Bearer {setup.access_token}"
        return Confluence(url=setup.host, session=s)

    # Else, return a Confluence object instantiated with username/password
    return Confluence(url=setup.host,
                      username=setup.user,
                      password=setup.password)
Ejemplo n.º 2
0
def helperFunc1(confluenceUrl, jiraUrl):
    global confluence
    global jira
    if nameEnter.get() and not nameEnter.get().isspace():
        confluenceName = nameEnter.get()
        confluencePass = passwordEnter.get()
        confluence = Confluence(url=confluenceUrl,
                                username=confluenceName,
                                password=confluencePass)
        #try:
        jira = JIRA(jiraUrl, auth=(nameEnter.get(), passwordEnter.get()))
        textBox.insert(END, "logged in as " + nameEnter.get() + "\n")
        window.deiconify()
        top.destroy()
        jqlSearch = f'project=HR and status = open order by "cf[11200]" asc'
        issues = jira.search_issues(jqlSearch)
        for issue in issues:
            if (str(issue.fields.status) == 'Open'
                    and not issue.fields.customfield_11705):
                openBox.insert(END, issue.fields.summary)
        openBox.select_set(0)
        openBox.bind("<Double-Button-1>", OnDouble)
        #except Exception as e:
        #print e.status_code, e.text
        #    messageBox.showinfo("Login Failed","Login Failed. Please try again\n(You may have trigger captcha from too many failed login. Please login to jira directly and clear it)")
    else:
        messageBox.showinfo("Failure",
                            "Please enter both username and password")
Ejemplo n.º 3
0
    def __init__(
        self,
        url,
        username,
        apiToken,
        pageTitlePrefix,
        markdownDir,
        dbPath,
        space,
        parentPageId,
        forceUpdate=False,
        forceDelete=False,
        skipUpdate=False,
    ):
        self.api = Confluence(url=url, username=username, password=apiToken)
        self.pageTitlePrefix = pageTitlePrefix
        self.markdownDir = markdownDir
        self.kv = KeyValue(dbPath)
        self.space = space
        self.parentPageId = parentPageId
        self.forceUpdate = forceUpdate
        self.forceDelete = forceDelete
        self.skipUpdate = skipUpdate
        self.confluenceRenderer = ConfluenceRenderer(url)
        self.metadataPlugin = MetadataPlugin()
        self.renderer = mistune.create_markdown(
            renderer=self.confluenceRenderer,
            plugins=[
                'strikethrough', 'footnotes', 'table', 'url',
                self.metadataPlugin.plugin_metadata
            ])

        # Hack to allow metadata plugin to work (See mistune/block_parser.py)
        self.renderer.block.rules.remove('thematic_break')
Ejemplo n.º 4
0
def update_html_content(login, passw, html):
    c = Confluence(BASE_URL, login, passw)
    page = c.get_page_by_id(page_id=JENKINS_PAGE_ID, expand='body.storage')
    resp = c.update_existing_page(page_id=JENKINS_PAGE_ID,
                                  body=html,
                                  title=page['title'])
    return resp.ok
def atl_login(request, body, *args, **kwargs):
    """
    Update atlassian login info
    Method: Post
    Request: first_name,last_name,old_password,password
    """
    try:
        user = request.session.get('user', {})
        # if user['atl_login']:
        #     resp = init_http_response_my_enum(RespCode.success)
        #     return make_json_response(resp=resp)

        user['atl_username'] = body['atl_username']
        user['atl_password'] = body['atl_password']
        # user['atl_login'] = True
        request.session['user'] = user

        confluence = Confluence(
            url='https://confluence.cis.unimelb.edu.au:8443/',
            username=request.session['user']['atl_username'],
            password=request.session['user']['atl_password'])

        conf_resp = confluence.get_all_groups()

        # print("~~")
        # print(request.session['user']['atl_username'])
        resp = init_http_response_my_enum(RespCode.success)
        return make_json_response(resp=resp)
    except requests.exceptions.HTTPError as e:
        resp = init_http_response_my_enum(RespCode.server_error)
        return make_json_response(resp=resp)
Ejemplo n.º 6
0
    def __init__(
            self, url, username, api_token,
            page_title_prefix, markdown_dir, db_path, space, parent_pageid,
            force_update=False, force_delete=False, skip_update=False,
            verbose=False):

        self.api = Confluence(url=url, username=username, password=api_token)
        self.page_title_prefix = page_title_prefix
        self.markdown_dir = markdown_dir
        self.kv = KeyValue(db_path)
        self.space = space
        self.parent_pageid = parent_pageid
        self.force_update = force_update
        self.force_delete = force_delete
        self.skip_update = skip_update
        self.confluence_renderer = ConfluenceRenderer(verbose)
        self.renderer = mistune.create_markdown(
            renderer=self.confluence_renderer,
            plugins=[
                plugin_front_matter,
                DirectiveInclude(),
                HugoRefLinkPlugin(self.markdown_dir),
                'strikethrough',
                'footnotes',
                'table',
                'url',
                Admonition(),
                plugin_html_comment,
            ]
        )
Ejemplo n.º 7
0
 def __init__(self, url, username, password, space):
     self.confluence = Confluence(url, username=username, password=password)
     spaces = self.confluence.get_all_spaces(start=0, limit=500)
     if any(s for s in spaces if s["name"] == space):
         self.space = space
     else:
         raise ValueError("{} is not valid Confluence Space".format(space))
Ejemplo n.º 8
0
def login_confluence():
    # log in credentials for confluence api
    confluence = Confluence(
        # url='https://usmai-wwwdev.lib.umd.edu/portal',
        url='https://usmai-wwwdev.lib.umd.edu/portal',
        username='******',
        password='******')
    return confluence
Ejemplo n.º 9
0
def acquire_conf_connection(url, username=None, password=None):
    username = username if username is not None else getpass.getuser()
    password = password if password is not None else getpass.getpass(
        "Please insert your password >> ")
    return Confluence(url=url,
                      api_root='wiki/rest/api',
                      username=username,
                      password=password)
    def test_confluence_attach_file_2(self):
        credentials = None

        try:
            with open(self.secret_file) as json_file:
                credentials = json.load(json_file)
        except Exception as err:
            self.fail("[{0}]: {1}".format(self.secret_file, err))

        confluence = Confluence(
            url=credentials["host"],
            username=credentials["username"],
            password=credentials["password"],
        )

        # individual configuration
        space = "SAN"
        title = "atlassian-python-rest-api-wrapper"

        # TODO: check if page are exists

        fd, filename = tempfile.mkstemp("w")
        os.write(fd, b"Hello World - Version 1")

        name = os.path.basename(tempfile.mktemp()) + ".txt"

        # upload a new file
        result = confluence.attach_file(
            filename,
            name,
            content_type="text/plain",
            title=title,
            space=space,
            comment="upload from unittest",
        )

        # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue("results" in result)
        self.assertFalse("statusCode" in result)

        # upload a new version of an existing file
        os.lseek(fd, 0, 0)
        os.write(fd, b"Hello Universe - Version 2")
        result = confluence.attach_file(
            filename,
            name,
            content_type="text/plain",
            title=title,
            space=space,
            comment="upload from unittest",
        )

        # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue("id" in result)
        self.assertFalse("statusCode" in result)

        os.close(fd)
        os.remove(filename)
Ejemplo n.º 11
0
 def __init__(self):
     super().__init__()
     self.environment = Environment.Environment()
     self.logger = Logger.Logger()
     self.confluence = Confluence(
         self.environment.get_endpoint_confluence_host(),
         username=self.environment.get_endpoint_confluence_user(),
         password=self.environment.get_endpoint_confluence_password())
     self.card_transfer = CardTransfer.CardTransfer()
     self.regex = RegEx.RegEx()
 def _connect(self, host: str, login: str, password: str, verify_ssl: bool) -> Confluence:
     """Connect to Confluence server and test connection"""
     self.logger.debug(f'Trying to connect to confluence server at {host}')
     host = host.rstrip('/')
     self.con = Confluence(host, login, password, verify_ssl=verify_ssl)
     try:
         res = self.con.get('rest/api/space')
     except UnicodeEncodeError:
         raise RuntimeError('Sorry, non-ACSII passwords are not supported')
     if isinstance(res, str) or 'statusCode' in res:
         raise RuntimeError(f'Cannot connect to {host}:\n{res}')
Ejemplo n.º 13
0
def logIntoConfluence():
    """Temporary solution for Confluence login"""
    username = '******'
    password = '******'m not exposing my password. Gotta wait till I\
    figure out how to retrieve logged-in user details first!'
    confluence = Confluence(
        url='https://confluence.cis.unimelb.edu.au:8443/',
        username=username,
        password=password
    )
    return confluence
Ejemplo n.º 14
0
    def __init__(self, space="", url="", username="", token="", parent=None):
        self.parent = parent
        self.space = space

        # if not all([space, url, username, token]):
        #     raise ConfluenceAPIError("Must assigne a space, url, unername and token")

        # Define atlassian api connection
        self.conn = Confluence(url=url,
                               username=username,
                               password=token,
                               cloud=True)
Ejemplo n.º 15
0
def create_nbpm_collab_page(cir_title, creds):
    collab = Confluence(url='https://collab.test.net/',
                        username=creds['user'],
                        password=creds['password'])
    cir_date = datetime.now().strftime("%Y-%m-%d")
    cir_title = cir_title
    body_text = '''    '''

    page = collab.create_page(space="NBPM",
                              parent_id=143085345,
                              title=cir_title,
                              body=body_text)
Ejemplo n.º 16
0
def get_confluence_setup(config, feature_prefix):
    track_config = config.inicfg.config.sections["pytest_track"]
    api = Confluence(
        url=track_config["confluence_url"],
        username=track_config["confluence_username"],
        password=track_config["confluence_password"],
    )
    parent_id = track_config["confluence_{}_parent_page_id".format(
        feature_prefix)]
    page_title = track_config["confluence_{}_page_title".format(
        feature_prefix)]
    return api, parent_id, page_title
Ejemplo n.º 17
0
    def test_confluence_attach_file_2(self):
        credentials = None

        try:
            with open(self.secret_file) as json_file:
                credentials = json.load(json_file)
        except Exception as err:
            self.fail('[{0}]: {1}'.format(self.secret_file, err))

        confluence = Confluence(url=credentials['host'],
                                username=credentials['username'],
                                password=credentials['password'])

        # individual configuration
        space = 'SAN'
        title = 'atlassian-python-rest-api-wrapper'

        # TODO: check if page are exists

        fd, filename = tempfile.mkstemp('w')
        os.write(fd, b'Hello World - Version 1')

        name = os.path.basename(tempfile.mktemp()) + ".txt"

        # upload a new file
        result = confluence.attach_file(filename,
                                        name,
                                        content_type='text/plain',
                                        title=title,
                                        space=space,
                                        comment='upload from unittest')

        # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue('results' in result)
        self.assertFalse('statusCode' in result)

        # upload a new version of an existing file
        os.lseek(fd, 0, 0)
        os.write(fd, b'Hello Universe - Version 2')
        result = confluence.attach_file(filename,
                                        name,
                                        content_type='text/plain',
                                        title=title,
                                        space=space,
                                        comment='upload from unittest')

        # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue('id' in result)
        self.assertFalse('statusCode' in result)

        os.close(fd)
        os.remove(filename)
Ejemplo n.º 18
0
def client_from_settings(settings: Settings) -> Confluence:
    """Returns Confluence client from settings.

    Args:
        settings: a Confluence settings/

    Returns: Confluence client.
    """
    return Confluence(
        settings.url,
        settings.credentials.username,
        settings.credentials.api_key,
    )
Ejemplo n.º 19
0
 def _establish_confluence_connection(self):
     """
     Setup the confluence object to be interacted with later
     :return:
     """
     # URL needs to be in the settingsmeta.yaml
     # confluence username needs to be in the settingsmeta.yaml
     # confluence password needs to be in the settingsmeta.yaml
     self.username = self.settings.get('user_name')
     self.password = self.settings.get('password')
     self.confluence_url = self.settings.get('confluence_url')
     self.confluence = Confluence(url=self.confluence_url,
                                  username=self.username,
                                  password=self.password)
    def test_confluence_attach_content(self):
        credentials = None

        try:
            with open(self.secret_file) as json_file:
                credentials = json.load(json_file)
        except Exception as err:
            self.fail("[{0}]: {1}".format(self.secret_file, err))

        confluence = Confluence(
            url=credentials["host"],
            username=credentials["username"],
            password=credentials["password"],
        )

        # individual configuration
        space = "SAN"
        title = "atlassian-python-rest-api-wrapper"

        attachment_name = os.path.basename(tempfile.mktemp())

        # upload a new file
        content = b"Hello World - Version 1"
        result = confluence.attach_content(
            content,
            attachment_name,
            "text/plain",
            title=title,
            space=space,
            comment="upload from unittest",
        )

        # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue("results" in result)
        self.assertFalse("statusCode" in result)

        # upload a new version of an existing file
        content = b"Hello Universe - Version 2"
        result = confluence.attach_content(
            content,
            attachment_name,
            "text/plain",
            title=title,
            space=space,
            comment="upload from unittest",
        )

        # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue("id" in result)
        self.assertFalse("statusCode" in result)
def init():
    """
    Initialize global variables and connections to the Redmine and Jira servers.
    Parameters:
        None.
    Returns:
        None.
    """
    global yaml_vars, arg_vars, redmine, jira, confluence, wiki_pages_rel, wiki_pages_imported, current_page
    dir_path = os.path.dirname(os.path.realpath(__file__))
    arg_vars = get_args()

    # Check if the .yaml file provided in the command line arguments, if yes, use the provided file.
    # Otherwise, use 'vars.yaml'.
    yaml_vars = get_config_data(os.path.join(dir_path, arg_vars.yaml)) if arg_vars.yaml is not None\
        else get_config_data(os.path.join(dir_path, 'vars.yaml'))
    # Check if Redmine API key, Jira user and Jira project is provided in the command line.
    # If yes, overwrite variables which are initialized from the .yaml file.
    if arg_vars.redminekey:
        yaml_vars['redmine_apikey'] = arg_vars.redminekey
    if arg_vars.redmineproject:
        yaml_vars['redmine_wiki_project'] = arg_vars.redmineproject
    if arg_vars.jirauser:
        yaml_vars['jira_user'] = arg_vars.jirauser
    if arg_vars.jiraproject:
        yaml_vars['jira_project'] = arg_vars.jiraproject
    if arg_vars.confluencespace:
        yaml_vars['confluence_space'] = arg_vars.confluencespace
    # Initialize the redmine instance.
    redmine = Redmine(yaml_vars['redmine_server'],
                      key=yaml_vars['redmine_apikey'],
                      requests={'timeout': 10})
    redmine_project = redmine.project.get(yaml_vars['redmine_wiki_project'])
    yaml_vars['redmine_project_id'] = redmine_project.id
    # Suppress the InsecureRequestWarnings.
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    # Initialize the jira instance.
    jira = JIRA({
        'server': yaml_vars['jira_server'],
        'verify': False
    },
                basic_auth=(yaml_vars['jira_user'],
                            base64.b64decode(
                                yaml_vars['jira_password']).decode("utf-8")))
    confluence = Confluence(
        url=yaml_vars['confluence_server'],
        username=yaml_vars['confluence_user'],
        password=base64.b64decode(
            yaml_vars['confluence_password']).decode("utf-8"))
Ejemplo n.º 22
0
 def __init__(self, project_key, config, plugin_config):
     """
     :param project_key: the project in which the runnable executes
     :param config: the dict of the configuration of the object
     :param plugin_config: contains the plugin settings
     """
     self.project_key = project_key
     self.config = config
     confluence_login = self.config.get("confluence_login", None)
     if confluence_login is None:
         raise Exception("No Confluence login is currently set.")
     self.confluence_username = confluence_login.get(
         "confluence_username", None)
     self.assert_confluence_username()
     self.confluence_password = confluence_login.get(
         "confluence_password", None)
     self.assert_confluence_password()
     self.confluence_url = self.format_confluence_url(
         confluence_login.get("server_type", None),
         confluence_login.get("url", None),
         confluence_login.get("orgname", None))
     self.confluence_space_key = confluence_login.get(
         "confluence_space_key", None)
     self.assert_space_key()
     self.confluence_space_name = confluence_login.get(
         "confluence_space_name", self.confluence_space_key)
     if self.confluence_space_name == "":
         self.confluence_space_name = self.confluence_space_key
     self.check_space_key_format()
     self.client = dataiku.api_client()
     try:
         self.studio_external_url = self.client.get_general_settings(
         ).get_raw()['studioExternalUrl']
         assert (self.studio_external_url not in (None, ''))
     except Exception as err:
         logger.error("studioExternalUrl not set :{}".format(err))
         raise Exception(
             "Please set the DSS location URL in Administration > Settings > Notifications & Integrations > DSS Location > DSS URL"
         )
     self.wiki = DSSWiki(self.client, self.project_key)
     self.wiki_settings = self.wiki.get_settings()
     self.taxonomy = self.wiki_settings.get_taxonomy()
     self.articles = self.wiki.list_articles()
     self.space_homepage_id = None
     self.confluence = Confluence(url=self.confluence_url,
                                  username=self.confluence_username,
                                  password=self.confluence_password)
     self.assert_logged_in()
     self.progress = 0
Ejemplo n.º 23
0
def update_page(cell, content):
    """ update method """
    cipher = Security('ec2cli')
    server, user, passwd, parent_id, page_id, title, response, region, product = config(
        cell)
    confluence = Confluence(url=server,
                            username=user,
                            password=cipher.decrypt(passwd))

    confluence.update_page(parent_id=parent_id,
                           page_id=page_id,
                           title=title,
                           body=content)

    return response
Ejemplo n.º 24
0
    def setUpClass(cls):
        try:
            with open(cls.secret_file) as json_file:
                credentials = json.load(json_file)
                cls.confluence = Confluence(
                    url=credentials["host"],
                    username=credentials["username"],
                    password=credentials["password"],
                )
        except Exception as err:
            raise cls.failureException("[{0}]: {1}".format(
                cls.secret_file, err))

        cls.space = "SAN"
        cls.created_pages = set()
Ejemplo n.º 25
0
    def __init__(self, credentials, path):
        self.remote = Confluence(url=credentials[0],
                                 username=credentials[1],
                                 password=credentials[2])

        def converter_to_local_key(key):
            if isinstance(key, tuple):
                return 'name_' + key[0] + key[1]
            else:
                return 'id_' + key

        self.cached = CachedDataProvider(
            self, path, converter_to_local_key=converter_to_local_key)
        self.simplified = CachedDataProvider(
            ConfluenceSimplifiedTextProvider(self.cached),
            path + '.simplified',
            converter_to_local_key=converter_to_local_key)
Ejemplo n.º 26
0
def main(oauth_token, space, parent_page, url, user_id, overwrite=True):

    global LUA, CONF, SPACE

    SPACE = space
    LUA = __file__.replace("__main__.py","confluence.lua")
    CONF = Confluence(url,user_id,oauth_token)
    parent = CONF.get_page_by_id(parent_page)
    if 'statusCode' in parent: raise Exception ("parent_page %s not found" % (parent_page))

    if overwrite:
        child_pages = CONF.get_child_pages(parent_page)
        for child_page in child_pages:
            CONF.remove_page(child_page['id'],recursive=True)

    source_folder = os.getcwd()
    create_page(source_folder, parent_page)
 def __init__(self,
              aepreq_jira_key,
              fix_version,
              cycle_name,
              map_version,
              map_revision,
              map_region="World",
              platform="Linux64"):
     self.aepreq_key = aepreq_jira_key
     self.fix_version = fix_version
     self.cycle_name = cycle_name
     self.map_version = map_version
     self.map_revision = map_revision
     self.map_region = map_region
     self.platform = platform
     self.zapi = ZephyrAPI()
     self.confluence = Confluence(url='https://confluence.in.here.com',
                                  **user_credentials)
Ejemplo n.º 28
0
    def test_confluence_attach_content(self):
        credentials = None

        try:
            with open(self.secret_file) as json_file:
                credentials = json.load(json_file)
        except Exception as err:
            self.fail('[{0}]: {1}'.format(self.secret_file, err))

        confluence = Confluence(url=credentials['host'],
                                username=credentials['username'],
                                password=credentials['password'])

        # individual configuration
        space = 'SAN'
        title = 'atlassian-python-rest-api-wrapper'

        attachment_name = os.path.basename(tempfile.mktemp())

        # upload a new file
        content = b'Hello World - Version 1'
        result = confluence.attach_content(content,
                                           attachment_name,
                                           'text/plain',
                                           title=title,
                                           space=space,
                                           comment='upload from unittest')

        # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue('results' in result)
        self.assertFalse('statusCode' in result)

        # upload a new version of an existing file
        content = b'Hello Universe - Version 2'
        result = confluence.attach_content(content,
                                           attachment_name,
                                           'text/plain',
                                           title=title,
                                           space=space,
                                           comment='upload from unittest')

        # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue('id' in result)
        self.assertFalse('statusCode' in result)
Ejemplo n.º 29
0
def confluence_get_page():
    #Конфиг
    base_path = os.path.dirname(os.path.abspath(__file__))
    #применяем конфигурацию по умолчанию
    #config_path_default = os.path.join(base_path, "dafault.conf")
    config_path = os.path.join(base_path, "config.conf")
    if os.path.exists(config_path):
        cfg = configparser.ConfigParser()
        #cfg.read(config_path_default)
        cfg.read(config_path)
    else:
        print(
            "Конфиг не обнаружен! см. документацию по настройке https://github.com/AndreyAgafonov/python/tree/master/birthday"
        )
        sys.exit(1)
    # Парсим конфиг файл
    section = "confluence"
    confluence_url = cfg.get(section, "confluence_connection_string")
    confluence_pageid = cfg.get(section, "confluence_pageid")
    confluence_user = cfg.get(section, "confluence_user")
    confluence_passwd = cfg.get(section, "confluence_passwd")

    section = "general"
    general_col_name = cfg.get(section, "column_date_of_birth")
    # Подключение к конфлюенсе
    confluence = Confluence(url=confluence_url,
                            username=confluence_user,
                            password=confluence_passwd)
    content1 = confluence.get_page_by_id(confluence_pageid,
                                         expand="body.storage")
    ##Первая колонка  это порядковый номер -  используем ее как индекс index_col=0
    ##Заголовок в первой строке - ее тоже не учитываем header=0
    ##
    df_list = pd.read_html(content1['body']['storage']['value'],
                           header=0,
                           index_col=0,
                           na_values=['No Acquirer'],
                           converters={general_col_name: str})
    table = df_list[0]
    table.index.name = "Number"
    return table
Ejemplo n.º 30
0
    def __init__(self, confluence_url, jira_url, bibucket_url, username,
                 password, verifyssl):

        self.verifyssl = verifyssl
        self.jira_service = JiraService(url=jira_url,
                                        username=username,
                                        password=password,
                                        verifyssl=self.verifyssl)

        self.bitbucket_service = BitbucketService(url=bibucket_url,
                                                  username=username,
                                                  password=password,
                                                  verifyssl=self.verifyssl)

        self.confluence = Confluence(url=confluence_url,
                                     username=username,
                                     password=password,
                                     verify_ssl=self.verifyssl)

        self.load_product_changelog_template()
        self.load_component_changelog_template()